Christopher Long
Christopher Long

Reputation: 904

Is there an easy way to check if a website has an SSL certificate

Using urllib2/SSL?

I have a large list of domains, and it would be useful to know if any had ssl certs that I wasn't aware of.

Upvotes: 2

Views: 4315

Answers (1)

Ricardo Altamirano
Ricardo Altamirano

Reputation: 15198

I'm not sure about using urllib2. Unfortunately, the closest information I can find on this is this link and the associated code:

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))

Although the comments in the file are fairly extensive, the wiki in the first link also lists these instructions:

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.

You will need cacerts.txt file that contains root certificates placed alongside the script - 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.

You could look into the pyopenssl module as well, since according to the first link I posted, it can be used to validate SSL certificates in this way:

import socket
from OpenSSL import SSL

HOST = "www.google.com"
PORT = 443

# replace HOST name with IP, this should fail connection attempt,
# but it doesn't by default
HOST = socket.getaddrinfo(HOST, PORT)[0][4][0]
print(HOST)

# uses HOST
def verify_cb(conn, x509, errno, errdepth, retcode):
  """
  callback for certificate validation
  should return true if verification passes and false otherwise
  """
  if errno == 0:
    if errdepth != 0:
      # don't validate names of root certificates
      return True
    else:
      if x509.get_subject().commonName != HOST:
        return False
  else:
    return False

context = SSL.Context(SSL.SSLv23_METHOD)
context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb)
context.load_verify_locations("cacerts.txt")

# create socket and connect to server
sock = socket.socket()
sock = SSL.Connection(context, sock)
sock.connect((HOST, PORT))
sock.do_handshake()

According to the documentation in the first link, you will need the latest certificate versions from here for these examples.

Upvotes: 4

Related Questions