Recherche sur le site Web

Tester les clients HTTPS utilisant OpenSSL pour simuler un serveur


Cet article décrit comment tester votre client ou navigateur HTTPS à l'aide d'OpenSSL. Pour tester votre client HTTPS, vous avez besoin d'un serveur HTTPS ou d'un serveur Web, tel que IIS, Apache, Nginx ou OpenSSL. Vous avez également besoin de certains cas de test. Il existe trois modes de défaillance communs dans SSL/TLS:

  1. Le client établit la connexion quand elle ne devrait pas,
  2. La connexion échoue quand elle devrait réussir, et
  3. La connexion est correctement établie, mais les données sont corrompues en transmission.
  4. Il existe un 4e mode de défaillance: les données peuvent ne pas être transmises en toute sécurité. Ce mode de défaillance est hors de portée de cet article.

Pour vous assurer que tout problème découvert dans les tests est dû à des problèmes dans votre client HTTPS, nous voulons utiliser un serveur HTTPS « Good » connu. Nous voulons également un serveur qui est « pédante » ou « impitoyable ». OpenSSL correspond précisément à ces exigences.

Dans cet article, je vais décrire comment utiliser la commande openSSL S_Server pour être un serveur HTTPS. Il existe de nombreux éléments de configuration qui doivent être bien, donc je vais non seulement vous montrer comment faire les choses correctement, mais je vais également partager avec vous ce qui a mal tourné, et comment je les ai diagnostiqués et les réparer .

Saviez-vous?
Un «client» est un ordinateur ou un programme informatique qui initie une connexion à un «serveur». Un «serveur» est un programme informatique qui attend qu'une connexion arrive à partir d'un «client». Pour HTTP et HTTPS, il existe des «navigateurs» et des «clients». Les navigateurs sont conçus pour l'interaction avec les humains et ont généralement des interfaces utilisateur graphiques. Tous les navigateurs sont des clients HTTP/HTTPS.

Cependant, il existe des clients HTTP/HTTPS qui ne sont pas des navigateurs. Ces clients sont conçus pour être utilisés comme systèmes automatisés. Le concepteur de serveurs Wise veillera à ce que leur système puisse être utilisé efficacement avec des clients HTTPS qui sont des navigateurs et des clients HTTPS qui ne sont pas des navigateurs.

Dans ce tutoriel, vous apprendrez:

  • Comment sélectionner un bon client ou navigateur HTTPS
  • Comment utiliser OpenSSL comme serveur HTTPS
  • Comment utiliser un serveur HTTPS pour tester un client HTTPS

Exigences et conventions logicielles utilisées

Comment tester vos instructions étape par étape du client HTTPS

J'utiliserai les adjectifs " juste " pour indiquer qu'un test a fait quelque chose correctement, et " errorneous " pour indiquer qu'un test a fait quelque chose de mal . Si un test échoue quand il le devrait, c'est un échec juste. Si un test passe quand il ne le devrait pas, alors c'est une passe erronée.

Je voulais utiliser un client HTTPS que je pouvais casser et réparer à volonté, et je l'ai trouvé: la commande http (c'est en github en tant que httpie). Si j'utilise l'option -verify=non , le client est rompu: il passera à tort les tests. Je n'ai pas pu créer un échec erroné, et c'est une bonne chose car cela signifie que si le client échoue, alors quelque chose ne va pas.

Au cœur du protocole SSL/TLS (ils ont changé le nom et peu d'autre) se trouvent deux fichiers, un «certificat» (ou «certificat» pour faire court) et une «clé» secrète. Partout dans le protocole, une extrémité de la connexion demandera à l'autre extrémité un certificat. Le premier extrémité utilisera certaines des informations du CERT pour créer un puzzle mathématique que seul quelque chose qui a la clé secrète peut répondre. La clé secrète ne quitte jamais sa machine: résoudre le problème signifie que l'extrémité proche sait que l'extrémité a la clé, mais pas la clé.

La commande openSSL est essentiellement une interface de ligne de commande à libssl . Il contient un serveur brut invoqué avec la sous-commande S_Server . OpenSSL aura besoin d'une paire publique CERT/Private Key. Dans mon cas, je les avais déjà pour mon serveur Web de production. Je les ai obtenus de Let's Encrypt, gratuitement.

En tant que preuve de concept que le serveur fonctionne correctement, j'ai copié le certificat et la clé de ma machine de développement et j'ai commencé le serveur HTTPS OpenSSL.

Du côté du serveur:

$ openssl s_server -status_verbose -HTTP -cert fullchain.pem -key privkey.pem
Using default temp DH parameters
ACCEPT

Ma première tentative a échoué!

$ http --verify=yes jeffs-desktop:4433/index.html

http: error: ConnectionError: (Connection aborted., RemoteDisconnected(Remote end closed connection without response)) while doing GET request to URL: http://jeffs-desktop:4433/index.html

Première hypothèse: la clé et le certificat ne correspondent pas. J'ai vérifié cela:

$ openssl x509 -noout -modulus -in fullchain.pemls  | openssl md5
(stdin)= b9dbd040d9a0c3b5d3d50af46bc87784openssl rsa -noout -modulus -in privkey.pem | openssl md5
(stdin)= b9dbd040d9a0c3b5d3d50af46bc87784

Ils correspondent. Alors pourquoi cela échoue-t-il? Parce que mon certificat est destiné à LinuxConfig.Dns.net mais j'utilise Jeffs-Desktop comme nom d'hôte.

jeffs@jeffs-desktop:~/documents$ openssl x509 -text -noout -in fullchain.pem | fgrep CN
        Issuer: C = US, O = Let’s Encrypt, CN = R3
        Subject: CN = linuxconfig.ddns.net

Il s'agit d'un échec juste: le serveur a été mal configuré et mon client l'a détecté. Si j'avais utilisé le
-verify=non Option, alors j'aurais un client cassé et il n'aurait pas détecté le problème. Notez que toutes les données transmises seraient toujours sécurisées contre une écoute. Je peux résoudre ce problème en modifiant mon fichier /etc/hosts avec mes propres adresses IPv4 et IPv6.

192.168.1.149   linuxconfig.ddns.net
2601:602:8500:b65:155a:7b81:65c:21fa    linuxconfig.ddns.net

(Soit dit en passant, la facilité par laquelle vous pouvez simuler une adresse IP est l'une des motivations de SSL/TLS en premier lieu).
Essayer à nouveau. Du côté du serveur:

$ openssl s_server -status_verbose -HTTP -cert fullchain.pem -key privkey.pem
Using default temp DH parameters
ACCEPT

Côté client:

http --verify=yes https://linuxconfig.ddns.net:4433/index.html
On the server side, I get the error message:

140101997737280:error:14094418:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:../ssl/record/rec_layer_s3.c:1543:SSL alert number 48
On the client side, I get the error message:
http: error: SSLError: HTTPSConnectionPool(host='linuxconfig.ddns.net', port=4433): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131)'))) while doing GET request to URL: https://linuxconfig.ddns.net:4433/

Ce message d'erreur, certificate_verify_failed , est un indice important: cela signifie que l'autorité du certificat du certificat (CA) n'a pas pu être vérifiée. Puisque le client n'a pas pu vérifier le certificat, s'il n'a pas réussi à établir la connexion. Il s'agit d'une autre échec juste.

Le certificat lui-même pourrait être falsifié - et le client n'a aucun moyen de savoir. Cependant, le certificat fait référence à une autorité de certificat (CA), et le CA sait que le certificat est valide, sinon il rejette la vérification. Comment savons-nous que l'AC est digne de confiance?

Le CA lui-même a un certificat, un certificat intermédiaire, et ce certificat fait référence à un autre CA. Finalement, cette chaîne de certificats atteint un certificat racine. Un certificat racine se signe et est donc, par définition, digne de confiance. Dans ce cas, quelque chose a mal tourné avec cette chaîne de certificats, cette chaîne de confiance.

$ openssl s_client -showcerts -connect linuxconfig.ddns.net:4433
CONNECTED(00000003)
depth=0 CN = linuxconfigan.ddns.net
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = linuxconfigan.ddns.net
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:CN = linuxconfigan.ddns.net
   i:C = US, O = Let's Encrypt, CN = R3
-----BEGIN CERTIFICATE-----

Je sais que mon serveur de production fonctionne correctement. C'est à quoi la chaîne est censée ressembler (notez le numéro de port 443, pas 4433):

$ openssl s_client -showcerts -connect linuxconfig.ddns.net:443
CONNECTED(00000003)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = linuxconfig.ddns.net
verify return:1
---
Certificate chain
 0 s:CN = linuxconfig.ddns.net
   i:C = US, O = Let's Encrypt, CN = R3
-----BEGIN CERTIFICATE-----
MIIFYjCCBEqgAwIBAgISA0MTOSmISSsIyRls8O/2XpAaMA0GCSqGSIb3DQEBCwUA
...
-----END CERTIFICATE-----
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
-----BEGIN CERTIFICATE----
...
-----END CERTIFICATE-----
 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
-----BEGIN CERTIFICATE-----
…

Il existe deux façons de procéder à partir d'ici: je peux désactiver la vérification du certificat ou je peux ajouter le certificat de Let’s Encrypt à la liste des CAS connus. L'éteindre la vérification est rapide et sûr. L'ajout de CA à la liste des CAS connus est plus arcanique. Faisons les deux. Du côté du serveur, je n'ai rien touché. Côté client, j'éteins la vérification et j'obtiens:

$ http –verify=no  https://linuxconfig.ddns.net:4433/index.html
http: error: ConnectionError: ('Connection aborted.', BadStatusLine('\n')) while doing GET request to URL: https://linuxconfig.ddns.net:4433/index.htmlecho $?
1

Ce message d'erreur me dit qu'il y a eu une violation du protocole HTTP (pas HTTPS). Le serveur a servi la première ligne du fichier, index.html, lorsqu'il aurait dû retourner un bloc d'en-tête de retour HTTP. Il s'agit d'un défaut côté serveur, et il briserait tous les clients HTTP. Un aperçu attentif de la documentation me dit d'utiliser l'option -www (pas -www) avec OpenSSL, au lieu de l'option -http. Je fais ça:

OpenSSL S_SERVER -STATUS_VERBOSE -WWW -CERT Fullchain.pem -Key privkey.pem Et cela fonctionne correctement, avec la mise en garde que je n'ai pas encore obtenu la validation du certificat pour fonctionner.

$ http -verify=no https://linuxconfig.ddns.net:4433/helloworld.c
HTTP/1.0 200 ok
Content-type: text/plain

#include 
int main (int argc, char *argv[]) {
        printf("Hello, world\n\n");
}

Étant donné que j'ai utilisé -verify=non , il s'agit en fait d'un laissez-passer erroné.

Pour vérifier que ma chaîne de certificat est valide, je peux utiliser la commande openSSL Verify :

$ openssl verify -purpose sslserver fullchain.pem
CN = linuxconfig.ddns.net
error 20 at 0 depth lookup: unable to get local issuer certificate
error cert.pem: verification failed

La solution rapide consistait à essayer la commande openSSL S_Server sur mon serveur Web de production, en utilisant des fichiers de configuration de production. Ceci est (raisonnablement) sûr à faire car le serveur OpenSSL s'exécutera sur le port 4433 pendant que mon serveur de production s'exécute sur le port 443.

# openssl s_server -status_verbose -WWW \
-cert /etc/letsencrypt/live/linuxconfig.ddns.net/fullchain.pem  \
-key /etc/letsencrypt/live/linuxconfig.ddns.net/privkey.pem -accept 4433

Hum. Nginx fonctionne comme un champion. OpenSSL ne l'est pas. C'est pourquoi OpenSSL fait un meilleur lit de test que Nginx: si la configuration de Nginx est erronée, il essaiera de se mêler. Si la configuration d'OpenSSL est erronée, elle vous appellera. La configuration d'OpenSSL est stockée dans /etc/ssl/opendsl.cnf .

Il indique que les certificats CA sont dans /etc/ssl/certs . Le certificat racine du groupe de recherche sur les services Internet (ISRG) est là. Mais le certificat intermédiaire de Encrypt ne l'est pas. Cela a du sens d'une certaine manière: Let's Encrypt a un merveilleux certificat qui savait tout sur Nginx quand je l'ai exécuté, mais je n'ai pas exécuté de certificat avec OpenSSL, donc le certificat de Let's Encrypt n'était pas dans /etc/ssl/certs/. J'ai obtenu le certificat de Let's Encrypt avec le:

$ wget https://letsencrypt.org/certs/lets-encrypt-r3.pem

La commande ci-dessus, copié le fichier lets_encrypt_r3.pem dans /etc/ssl/certs/, exécuté le programme c_rehash, et voila:

# openssl verify -CApath /etc/ssl/certs/ \ 	/etc/letsencrypt/live/linuxconfig.ddns.net/fullchain.pem
/etc/letsencrypt/live/linuxconfig.ddns.net/fullchain.pem: OK

C’est bien, mais le test est, puis-je voir Helloworld.c?

$ http --verify=yes https://linuxconfig.ddns.net:4433/helloworld.c
HTTP/1.0 200 ok
Content-type: text/plain

#include 
int main (int argc, char *argv[]) {
	printf("Hello, world\n\n");
}

Oui. J'ai maintenant vérifié que mon client HTTPS qui fonctionne passera à juste titre et échouera avec justice, du moins pour les cas de test avec lesquels j'ai travaillé. Il y a d'autres choses qui tournent mal avec SSL/TLS telles que les listes de révocation des certificats (CRL), mais j'espère que vous aurez une bonne idée.

Ensuite, je souhaite vérifier que les fichiers envoyés entre le serveur HTTPS OpenSSL et mon client HTTPS ne seront pas corrompus, pas même un bit. Je ne peux pas vérifier que chaque fichier sera transmis sans erreur, mais ce que je peux faire, c'est transmettre un grand fichier binaire, vérifier qu'il a été transmis correctement, puis déduire que les fichiers gros ne seront pas corrompus.

J'ai utilisé la commande ls -Lors pour trouver un grand fichier, calculé sa somme SHA256, transmis en utilisant OpenSSL comme serveur, enregistré le fichier reçu et calculé la somme SHA256 sur ce fichier. Les sommes SHA 256 devraient correspondre.

Du côté du serveur:

$ ls -lorS | tail -1
-rw-rw-r-- 1 jeffs 121329853 May 23  2020 CybersecurityEssentials.pdfsha256sum CybersecurityEssentials.pdf
49a49c8e525a3d6830fce1c1ee0bfce2d3dd4b000eeff5925b074802e62024e0 CybersecurityEssentials.pdf

Côté client:

$ http --verify=no https://linuxconfig.ddns.net:4433/CybersecurityEssentials.pdf -o /tmp/CybersecurityEssentials.pdf sha256sum /tmp/CybersecurityEssentials.pdf 
49a49c8e525a3d6830fce1c1ee0bfce2d3dd4b000eeff5925b074802e62024e0  /tmp/CybersecurityEssentials.pdf

Ce fichier PDF est de 121 Mo, assez grand pour mes besoins. Le SHA256 correspond à la correspondance, donc le fichier a été transmis correctement.

Conclusion

Dans cet article, j'ai décrit les modes de défaillance communs le protocole HTTPS. J'ai utilisé certains critères pour sélectionner un serveur HTTPS à utiliser pour tester un client HTTPS et sélectionné OpenSSL. J'ai sélectionné un client HTTPS facile à utiliser. J'ai montré quelques modes de défaillance communs et j'ai observé que le client détectait ces échecs.

La partie difficile consistait à configurer correctement OpenSSL, j'ai donc montré ce qui peut aller mal et comment le réparer. Enfin, j'ai démontré que, en utilisant OpenSSL comme serveur et mon client HTTPS, je pouvais transmettre un fichier sans corruption de données.

Articles connexes: