1227
Comment: explain how to eavesdrop Python 2.x ssl connections
|
4408
there could be problem with validation of certificate expiration time
|
Deletions are marked like this. | Additions are marked like this. |
Line 5: | Line 5: |
SSL support is available from http://pypi.python.org/pypi/ssl Unfortunately, there are no binaries for Windows, and that's a major showstopper when using Python 2.5. | SSL support is available from external '''ssl''' module http://pypi.python.org/pypi/ssl Unfortunately, there are no binaries for Windows, and that's a major showstopper when using Python 2.5. |
Line 8: | Line 8: |
SSL module from http://pypi.python.org/pypi/ssl is bundled with installer. It has a serious security issue that allows successful MITM attack using valid certificate from an other site - http://bugs.python.org/issue1589 Basically, the module validates that certificate is correct and correctly signed by root certificate, but it does not check that it actually belong to the connected site, i.e. that site name match the one specified in certificate. | '''ssl''' module from http://pypi.python.org/pypi/ssl is a part of standard library (version is unknown, though). It has a serious security issue that allows successful MITM attack using valid certificate from other site - http://bugs.python.org/issue1589 Basically, the module validates that certificate is correct and correctly signed by root certificate, but it does not check that certificate actually belongs to the connected site, i.e. that site name matches the name specified in certificate. |
Line 10: | Line 10: |
=== Validating server identity === Client need to connect to server over SSL, fetch its certificate and check that the certificate indeed belongs to this server (server name). |
=== Validating server identity with ssl module === Client need to connect to server over SSL, fetch its certificate, check that the certificate is valid (signed properly) and belongs to this server (server name). Let's illustrate '''ssl''' vulnerability in Python 2.x versions. The following snippet should fail - it replaces HOST "www.google.com" to connect to with its IP address. If you try to use this IP in Chrome like https://74.125.232.50 - it will show an error, but '''ssl''' library will not. |
Line 14: | Line 16: |
import socket import ssl |
|
Line 15: | Line 19: |
HOST = "www.google.com" PORT = 443 # replace HOST name with IP, this should fail connection attempt, # but it doesn't in Python 2.x HOST = socket.getaddrinfo(HOST, PORT)[0][4][0] print(HOST) # create socket and connect to server # server address is specified later in connect() method sock = socket.socket() sock.connect((HOST, PORT)) # wrap socket to add SSL support sock = ssl.wrap_socket(sock, # flag that certificate from the other side of connection is required # and should be validated when wrapping cert_reqs=ssl.CERT_REQUIRED, # file with root certificates ca_certs="cacerts.txt" ) |
|
Line 17: | Line 42: |
=== Get updated list of root certificates == | You will need "cacerts.txt" file that contains root certificates placed alongside the script - feel free to use the one attached to this page or see below how to get an updated list. To check that certificate validation works - use https://www.debian-administration.org/ in HOST name. This site's certificate is not signed by any root certificates from "cacerts.txt", so you will get an error. To validate that a certificate matches requested site, you need to check ''commonName'' field in the ''subject'' of the certificate. This information can be accessed with ''getpeercert()'' method of wrapped socket. {{{ import socket import ssl HOST = "www.google.com" PORT = 443 # replace HOST name with IP, this should fail connection attempt HOST = socket.getaddrinfo(HOST, PORT)[0][4][0] print(HOST) # create socket and connect to server # server address is specified later in connect() method sock = socket.socket() sock.connect((HOST, PORT)) # wrap socket to add SSL support sock = ssl.wrap_socket(sock, # flag that certificate from the other side of connection is required # and should be validated when wrapping cert_reqs=ssl.CERT_REQUIRED, # file with root certificates ca_certs="cacerts.txt" ) # security hole here - there should be an error about mismatched host name # manual check of hostname cert = sock.getpeercert() for field in cert['subject']: if field[0][0] == 'commonName': certhost = field[0][1] if certhost != HOST: raise ssl.SSLError("Host name '%s' doesn't match certificate host '%s'" % (HOST, certhost)) }}} That's it. === Validate certificate expiration === Needs to be researched if Python SSL libraries validate certificate expiration times correctly. Entrypoint: certificate fields ''notBefore'' and ''notAfter''. === Get updated list of root certificates === You will need the latest version of certificate data from http://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt and convert it to PEM format by any of available tools. Or just grab the latest version from http://curl.haxx.se/ca/cacert.pem |
SSL stands for Secure Sockets Layer and is designed to create secure connection between client and server. Secure means that connection is encrypted and therefore protected from eavesdropping. It also allows to validate server identity.
SSL libraries availability and limitations
Python 2.5 (the version AppEngine is running)
SSL support is available from external ssl module http://pypi.python.org/pypi/ssl Unfortunately, there are no binaries for Windows, and that's a major showstopper when using Python 2.5.
- Python 2.6
ssl module from http://pypi.python.org/pypi/ssl is a part of standard library (version is unknown, though). It has a serious security issue that allows successful MITM attack using valid certificate from other site - http://bugs.python.org/issue1589 Basically, the module validates that certificate is correct and correctly signed by root certificate, but it does not check that certificate actually belongs to the connected site, i.e. that site name matches the name specified in certificate.
Validating server identity with ssl module
Client need to connect to server over SSL, fetch its certificate, check that the certificate is valid (signed properly) and belongs to this server (server name).
Let's illustrate ssl vulnerability in Python 2.x versions. The following snippet should fail - it replaces HOST "www.google.com" to connect to with its IP address. If you try to use this IP in Chrome like https://74.125.232.50 - it will show an error, but ssl library will not.
import socket import ssl HOST = "www.google.com" PORT = 443 # replace HOST name with IP, this should fail connection attempt, # but it doesn't in Python 2.x HOST = socket.getaddrinfo(HOST, PORT)[0][4][0] print(HOST) # create socket and connect to server # server address is specified later in connect() method sock = socket.socket() sock.connect((HOST, PORT)) # wrap socket to add SSL support sock = ssl.wrap_socket(sock, # flag that certificate from the other side of connection is required # and should be validated when wrapping cert_reqs=ssl.CERT_REQUIRED, # file with root certificates ca_certs="cacerts.txt" )
You will need "cacerts.txt" file that contains root certificates placed alongside the script - feel free to use the one attached to this page or see below how to get an updated list. To check that certificate validation works - use https://www.debian-administration.org/ in HOST name. This site's certificate is not signed by any root certificates from "cacerts.txt", so you will get an error.
To validate that a certificate matches requested site, you need to check commonName field in the subject of the certificate. This information can be accessed with getpeercert() method of wrapped socket.
import socket import ssl HOST = "www.google.com" PORT = 443 # replace HOST name with IP, this should fail connection attempt HOST = socket.getaddrinfo(HOST, PORT)[0][4][0] print(HOST) # create socket and connect to server # server address is specified later in connect() method sock = socket.socket() sock.connect((HOST, PORT)) # wrap socket to add SSL support sock = ssl.wrap_socket(sock, # flag that certificate from the other side of connection is required # and should be validated when wrapping cert_reqs=ssl.CERT_REQUIRED, # file with root certificates ca_certs="cacerts.txt" ) # security hole here - there should be an error about mismatched host name # manual check of hostname cert = sock.getpeercert() for field in cert['subject']: if field[0][0] == 'commonName': certhost = field[0][1] if certhost != HOST: raise ssl.SSLError("Host name '%s' doesn't match certificate host '%s'" % (HOST, certhost))
That's it.
Validate certificate expiration
Needs to be researched if Python SSL libraries validate certificate expiration times correctly. Entrypoint: certificate fields notBefore and notAfter.
Get updated list of root certificates
You will need the latest version of certificate data from http://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt and convert it to PEM format by any of available tools.
Or just grab the latest version from http://curl.haxx.se/ca/cacert.pem