2017-07-06 1 views
7

J'ai écrit un proxy d'interception dans Python 3 qui utilise une technique d'attaque man-in-the-middle pour inspecter et modifier les pages qui y transitent à la volée. Une partie du processus d '"installation" ou de configuration du proxy implique la génération d'un certificat "racine" qui doit être installé dans le navigateur et chaque fois qu'un nouveau domaine est touché via HTTPS via le proxy, le proxy génère un nouveau certificat de site à la volée (et met en cache tous les certificats générés sur le disque afin de ne pas avoir à générer de nouveaux certificats pour les domaines pour lesquels des certificats ont déjà été générés) signé par le certificat racine et utilise le certificat de site pour communiquer avec le navigateur. (Et, bien sûr, le proxy forge sa propre connexion HTTPS au serveur distant.Le proxy vérifie également la validité du certificat de serveur si vous êtes curieux.)Les certificats d'interception de proxy générés à la volée provoquent des erreurs de navigateur

Eh bien, cela fonctionne très bien avec le navigateur surf. (Et, cela pourrait être pertinent - à partir de quelques versions, au moins, le surf n'a pas vérifié/faire respecter la validité du certificat.) Je ne peux pas certifier si c'est le cas pour des versions plus récentes.) Mais, Firefox donne un Erreur SEC_ERROR_REUSED_ISSUER_AND_SERIAL sur la deuxième (et toutes les demandes HTTPS) faites à travers le proxy et Chromium (je n'ai pas testé avec Chrome proprement dit) donne NET :: ERR_CERT_COMMON_NAME_INVALID sur chaque demande HTTPS. Ceux-ci présentent évidemment un problème majeur lorsque j'essaie de parcourir mon proxy d'interception. La bibliothèque SSL que j'utilise est pyOpenSSL 0.14 si cela fait une différence.

En ce qui concerne l'erreur SEC_ERROR_REUSED_ISSUER_AND_SERIAL de Firefox, je suis sûr que je suis pas réutilisant des numéros de série. (Si quelqu'un veut vérifier mon travail, ce serait plutôt rad: cert.py - notez le "crt.set_serial_number (getrandbits (20 * 8))" sur la ligne 168.) L'émetteur du certificat racine ne change évidemment pas, mais ne devrait pas changer, non? Je ne suis pas sûr de ce que signifie exactement "émetteur" dans le message d'erreur si ce n'est pas l'émetteur du certificat racine. En outre, la boîte de dialogue "Afficher le certificat" de Firefox affiche des numéros de série complètement différents pour les différents certificats générés par le proxy. (Par exemple, j'en ai un généré pour www.google.com avec un numéro de série de 00: BF: 7D: 34: 35: 15: 83: 3A: 6E: 9B: 59: 49: A8: CC: 88: 01: BA: BE: 23: A7: AD et un autre généré pour www.reddit.com avec un numéro de série de 78: 51: 04: 48: 4B: BC: E3: 96: 47: AC: DA: D4 : 50: EF: 2B: 21: 88: 99: AC: 8C.) Donc, je ne suis pas vraiment sûr de ce que Firefox se plaint exactement.

Mon proxy réutilise la clé privée (et donc la clé publique/le module) pour tous les certificats qu'elle crée à la volée. J'en suis venu à penser que c'était ce que Firefox essayait de faire et j'ai essayé de changer le code pour générer une nouvelle paire de clés pour chaque certificat créé par le proxy à la volée. Cela n'a pas résolu le problème dans Firefox. Je reçois toujours le même message d'erreur. Je n'ai pas encore testé si cela résout le problème de Chromium.

En ce qui concerne l'erreur NET :: ERR_CERT_COMMON_NAME_INVALID de Chromium, le nom commun du certificat de site est supposé être le domaine, n'est-ce pas? Je ne devrais pas inclure un numéro de port ou quoi que ce soit, n'est-ce pas? (Encore une fois, si quelqu'un veut vérifier mon travail, voir cert.py.) Si cela aide, mon proxy intercepter n'utilise pas de caractères génériques dans les noms communs de certificat ou quoi que ce soit. Chaque certificat généré est pour un fqdn spécifique.

Je suis tout à fait certain de faire ce travail sans que Firefox ou Chrome (ou Chromium ou IE etc) soit possible. Une entreprise que je travaillais pour acheter et mettre en place un man-in-them-middling proxy à travers lequel tout le trafic à partir du réseau d'entreprise à l'Internet devait passer.Les administrateurs PC de cette société ont installé un certificat auto-signé comme autorité de certification dans chaque navigateur sur chaque ordinateur appartenant à l'entreprise utilisé par les employés et le résultat n'a jamais produit d'erreurs comme celles que Firefox et Chromium m'ont données pour les certificats. propre logiciel d'interception proxy produit. Il est possible que les administrateurs du PC en aient modifié quelques-uns sur: les paramètres de configuration dans Firefox pour que tout cela fonctionne ou quelque chose, mais j'en doute un peu. Pour être juste, le proxy utilisé dans cette entreprise était soit une couche de réseau ou de transport, pas une couche d'application comme la mienne. Mais je m'attendrais à ce que la même chose puisse être accomplie dans un proxy HTTP (s) de couche application. Editer: J'ai essayé de définir le subjectAltName comme suggéré par brain99. Voici la ligne que j'ajouté à l'emplacement brain99 suggéré:.

r.add_extensions([crypto.X509Extension(b"subjectAltName", False, b"DNS:" + cn.encode("UTF-8"))])

Je suis toujours obtenir SEC_ERROR_REUSED_ISSUER_AND_SERIAL de Firefox (sur la deuxième et les demandes ultérieures HTTPS et je reçois ERR_SSL_SERVER_CERT_BAD_FORMAT de chrome

Voici quelques certificats générés par le proxy:

google.com: https://pastebin.com/YNr4zfZu

stackoverflow.com : https://pastebin.com/veT8sXZ4

+0

Le texte de prime Danged n'a pas conservé le formatage. Désolé pour le wall-o-text. – AntiMS

+0

Vous devez utiliser subjectAltName au lieu de commonName. –

+0

Partager deux certs que firefox prétend être réutilisés pourrait aider. –

Répondre

1

J'ai remarqué que vous définissez uniquement le CN dans votre X509Req. Les deux Chrome et Firefox nécessitent l'extension subjectAltName à être présent; Voir par exemple this Chrome help page ou this Mozilla wiki page pour discuter des pratiques CA requises ou recommandées. Pour citer le wiki de Mozilla:

Certains croient à tort que les CA d'un nom DNS primaire devrait entrer dans le sujet Nom commun et tous les autres dans le SAN.

Selon le CA/Browser Forum Exigences de base:

  • BR # 9.2.1 (section 7.1.4.2.1 dans la version 1.3 BR), Objet Alternative Nom Extension
    • requis/Facultatif: Obligatoire
    • Contenu: Cette extension DOIT contenir au moins une entrée. Chaque entrée DOIT être un nom DNN contenant le nom de domaine entièrement qualifié ou une adresse IP contenant l'adresse IP d'un serveur.

Vous devriez être en mesure de le faire facilement avec pyOpenSSL:

if not os.path.exists(path): 
    r = crypto.X509Req() 
    r.get_subject().CN = cn 
    r.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:" + cn]) 
    r.set_pubkey(key) 
    r.sign(key, "sha1") 

Si cela ne résout pas le problème, ou si elle ne résout que partiellement, s'il vous plaît poster un ou deux exemples de certificats qui présentent le problème. En dehors de cela, j'ai également remarqué que vous signez en utilisant SHA1. Notez que les certificats signés avec SHA1 ont été dépréciés dans plusieurs navigateurs principaux, donc je suggère de passer à SHA-256.

r.sign(key, "sha256") 
+0

Merci pour votre réponse, mais pas de dés pour l'instant. Je mettrai à jour mon message avec plus de détails. – AntiMS

+0

Le certificat pour stackoverflow.com que vous avez posté ne contient * pas * l'extension subjectAltName (n'a pas vérifié l'autre). Êtes-vous sûr de mettre en place des certificats générés par la dernière version de votre code? – brain99

+0

Hrm. Eh bien, après un double contrôle, je suis certain que c'est à partir de la version avec la nouvelle ligne "add_extensions". Mais, en vérifiant les détails du certificat avec OpenSSL, il semble bien qu'il produise des certificats sans l'extension. Pas sûr de ce qui se passe avec ça. Je ferai quelques recherches sur Google et je verrai si je peux comprendre ce qui se passe là-bas. – AntiMS