"ssl" module doesn't validate that domain of certificate is correct

John Nagle nagle at animats.com
Mon Apr 19 19:49:19 CEST 2010

exarkun at twistedmatrix.com wrote:
> On 04:51 pm, nagle at animats.com wrote:
>>    I'm converting some code from M2Crypto to the new "ssl" module, and
>> I've found what looks like a security hole.  The "ssl" module will
>> validate the certificate chain, but it doesn't check that the certificate
>> is valid for the domain.
> It's a bit debatable.  There probably should be a way to make this 
> happen, but it's far from clear that it's the only correct behavior. 
> And, as it turns out, there is a way to make it happen - call 
> getpeercert() and perform the check yourself. ;)

    "Checking it yourself" is non-trivial. The checking code has to
understand DNS wildcards and additional domains in cert extensions.
The SSL module doesn't seem to let you read all the cert extensions,
(in particular, you don't get "certificatePolicies", so you can't
tell if a cert is an "extended validation" cert) but it looks like
you do get the subjectAltName fields present in the extensions, like this:

   subjectAltName = (('DNS', 'www.chapinfurniture.com'),
	('DNS', 'chapinfurniture.com')))

    So it's at least possible to check.  Almost.

    (DNS wildcards look like this: "*.example.com".  It's also possible
to have "*.*.example.com".  However, no DNS wildcard cert should cover
more than one second-level domain (huge security hole if you allow that)
and no extended validation cert should have a wildcard.)

    There may also be issues with internationalized domain names.

    It's very bad for the "ssl" module to both ignore this check and
not have that mentioned prominently in the documentation.  This is
a security-critical function.  Somewhere, there's a Python program that
can be exploited due to this bug.

    Here's a comparison of what M2Crypto and the SSL module return, for
"verisign.com", which uses most cert features.

Trying domain "www.verisign.com"
Host: www.verisign.com Port: 443

Info from "M2Crypto: module:

Cipher = DHE-RSA-AES256-SHA
    Subject info: [('CN', 'verisign.com'),
	('OU', 'production Security Services  '),
	('O', 'VeriSign, Inc.'),
	('streetAddress', '487 East Middlefield Road'),
	('L', 'Mountain View'),
	('ST', 'California'),
	('postalCode', '94043'),
	('C', 'US'),
	('serialNumber', '2497886'),
	('', 'V1.0, Clause 5.(b)'),
	('jurisdictionOfIncorporationStateOrProvinceName', 'Delaware'),
	('jurisdictionOfIncorporationCountryName', 'US')]

   Certificate has 10 extensions.
Extension #0: subjectAltName = DNS:verisign.com, DNS:www.verisign.com, 
DNS:verisign.mobi, DNS:www.verisign.mobi, DNS:verisign.eu, DN
Extension #1: basicConstraints = CA:FALSE
Extension #2: subjectKeyIdentifier = 
Extension #3: keyUsage = Digital Signature, Key Encipherment
Extension #4: crlDistributionPoints = 

Extension #5: certificatePolicies = Policy: 2.16.840.1.113733.
   CPS: https://www.verisign.com/rpa

Extension #6: extendedKeyUsage = TLS Web Server Authentication, TLS Web Client 
Authentication, Netscape Server Gated Crypto
Extension #7: authorityKeyIdentifier = 

Extension #8: authorityInfoAccess = OCSP - URI:http://EVIntl-ocsp.verisign.com
CA Issuers - URI:http://EVIntl-aia.verisign.com/EVIntl2006.cer

Extension #9: UNDEF = None

Info from "ssl" module:

SSL cert for "www.verisign.com":
     notAfter = Apr  2 23:59:59 2012 GMT
     subject = ((('', u'US'),),
		(('', u'Delaware'),),
		(('', u'V1.0, Clause 5.(b)'),),
		(('serialNumber', u'2497886'),),
		(('countryName', u'US'),),
		(('postalCode', u'94043'),),
		(('stateOrProvinceName', u'California'),),
		(('localityName', u'Mountain View'),),
		(('streetAddress', u'487 East Middlefield Road'),),
		(('organizationName', u'VeriSign, Inc.'),),
		(('organizationalUnitName', u'production Security Services  '),),
		(('commonName', u'verisign.com'),))

				John Nagle

More information about the Python-list mailing list