Consommer une API sur HTTP c'est bien, être sûr que l'on discute avec le
bon serveur c'est mieux.
Il n'en reste pas moins que ce modèle de confiance
est défaillant. Cela
ne met pas en cause le chiffrement lui-même, il reste possible de remplacer la
liste des « autorités de confiance » par une liste choisie avec soin, voire un
unique certificat.
Pour certains besoins comme les API consommées par les applis mobiles, celà se
pratique fréquemment, ça s'appelle le certificate-pining.
On peut se poser la question pour un site web d'utiliser un certificat
commercial, signé par une Autorité « de confiance » à des fins de simplicité
pour l'utilisateur. Pour des APIs consommées par des développeurs, utiliser une
liste de CA de confiance est une perte de sécurité.
En outre, utiliser un certificat auto-signé ou géré par sa propre CA vous donne
la maîtrise et la gratuité.
Étonnamment, on trouve très peu d'exemples et de docs sur le pining en python.
Exemples
En python avec urllib2
Par défaut, Python ne vérifie pas du tout le certificat
envoyé par le serveur. Il faut donc changer ça pour lui spécifier un fichier
contenant le certificat pinné au format
X.509/base64 (dit parfois PEM), c'est
l'argument ca_certs
, et lui dire de refuser une connexion d'un certificat
qui n'est pas dans cette liste : cert_reqs
(cf
doc du module ssl).
import httplib, socket, ssl
class HTTPSPinnedClient(httplib.HTTPSConnection):
def __init__(self, host, port, ca_certs, **kwargs):
httplib.HTTPSConnection.__init__(self, host, port, **kwargs)
self.ca_certs = ca_certs
def connect(self):
if self._tunnel_host:
self.sock = sock
self._tunnel()
sock = socket.create_connection((self.host, self.port))
self.sock = ssl.wrap_socket(sock, ca_certs=self.ca_certs, cert_reqs=ssl.CERT_REQUIRED)
cli = HTTPSPinnedClient('db.ffdn.org', 443, ca_certs='/tmp/ffdn.org')
cli.request('GET', '/api/v1/isp/36/')
On ne peut pas dire que ça soit aisé, mais ça se fait…
En python, avec la bibliothèque restkit
Restkit est une lib python écrite il y a quelques années dédiée à la
consommation d'API REST, basée sur la notion de ressource.
Exemple d'utilisation
from restkit.resource import Resource
resource = restkit.Resource('https://db.ffdn.org/api/v1/isp/36/')
data = resource.get()
On peut utiliser du HTTPS sans problème, mais par défaut, python ne valide pas
du tout le certificat reçu.
Il faut alors sauvegarder le certificat reçu dans un fichier PEM pour lui
indiquer ensuite. On peut le faire par un moyen out of band ou
Limites
Révocation ?
commentaires