homepage

This issue tracker has been migrated to GitHub , and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: SSL certificate verification failed if no dNSName entry in subjectAltName
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.2, Python 3.3, Python 3.4
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: kiilerix, nbareil, pitrou, python-dev, sdaoden
Priority: normal Keywords:

Created on 2011年05月04日 11:35 by nbareil, last changed 2022年04月11日 14:57 by admin. This issue is now closed.

Messages (9)
msg135119 - (view) Author: Nicolas Bareil (nbareil) Date: 2011年05月04日 11:35
When connecting to a SSL server, the certificate verification failed if
it has a subjectAltName extension without any dNSName entry inside: it
should fallback to the Common Name.
Example:
 >>> cert = conn.getpeercert()
 >>> cert
 {'notAfter': 'May 15 14:31:42 2011 GMT',
 'subject': ((('countryName', u'FR'),),
 (('stateOrProvinceName', u'Ile-de-France'),),
 (('localityName', u'Paris'),),
 (('organizationName', 'xxx'),),
 (('organizationalUnitName', 'xxx'),),
 (('commonName', 'foobar.corp'),),
 (('emailAddress', u'test@test.net'),)),
 'subjectAltName': (('email', text@test.net'),)}
This certificate is valid according to RFC 2818:
 If a subjectAltName extension of type dNSName is present, that MUST
 be used as the identity. Otherwise, the (most specific) Common Name
 field in the Subject field of the certificate MUST be used. Although
 the use of the Common Name is existing practice, it is deprecated and
 Certification Authorities are encouraged to use the dNSName instead.
Even if the use of CommonName is deprecated, we should not break
existing systems.
Current revision of Lib/ssl.py :
 108 def match_hostname(cert, hostname):
 ...
 119 san = cert.get('subjectAltName', ())
 120 for key, value in san:
 121 if key == 'DNS':
 122 if _dnsname_to_pat(value).match(hostname):
 123 return
 124 dnsnames.append(value)
 125 if not san:
 126 # The subject is only checked when subjectAltName is empty
 127 for sub in cert.get('subject', ()):
 128 for key, value in sub:
 129 # XXX according to RFC 2818, the most specific Common Name
 130 # must be used.
 131 if key == 'commonName':
 132 if _dnsname_to_pat(value).match(hostname):
 133 return
 134 dnsnames.append(value)
 ...
Proposed patch is:
diff -r 513f6dfd3173 Lib/ssl.py
--- a/Lib/ssl.py Sun May 01 20:24:59 2011 -0500
+++ b/Lib/ssl.py Mon May 02 11:16:46 2011 +0200
@@ -122,8 +122,9 @@
 if _dnsname_to_pat(value).match(hostname):
 return
 dnsnames.append(value)
- if not san:
- # The subject is only checked when subjectAltName is empty
+ if not san and not dnsnames:
+ # The subject is only checked when there is no dNSName entry
+ # in subjectAltName
 for sub in cert.get('subject', ()):
 for key, value in sub:
 # XXX according to RFC 2818, the most specific Common Name
msg135200 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2011年05月05日 13:35
Are you sure about "if not san and not dnsnames"? It is even more restrictive than the currently condition. "if not dnsnames" looks like it would fit the bill better.
Also, better if you can provide a complete patch, including additional test(s) in Lib/test/test_ssl.py.
(see http://docs.python.org/devguide/runtests.html if you want information about running/writing tests)
msg135205 - (view) Author: Steffen Daode Nurpmeso (sdaoden) Date: 2011年05月05日 14:04
P.S.: if you're really right ('have those RFC's, but didn't read
them yet), you could also open an issue for Mercurial at
http://mercurial.selenic.com/bts - i think those guys do the very
same.
Thanks, Steffen!
msg135275 - (view) Author: Nicolas Bareil (nbareil) Date: 2011年05月06日 10:48
Hello Antoine, Steffen,
You are absolutely right about removing the 'not san' part. Here is the
new patch, with tests :
diff -r c22d5b37f6a4 Lib/ssl.py
--- a/Lib/ssl.py Fri May 06 09:31:02 2011 +0300
+++ b/Lib/ssl.py Fri May 06 12:47:14 2011 +0200
@@ -122,8 +122,9 @@
 if _dnsname_to_pat(value).match(hostname):
 return
 dnsnames.append(value)
- if not san:
- # The subject is only checked when subjectAltName is empty
+ if not dnsnames:
+ # The subject is only checked when there is no dNSName entry
+ # in subjectAltName
 for sub in cert.get('subject', ()):
 for key, value in sub:
 # XXX according to RFC 2818, the most specific Common Name
diff -r c22d5b37f6a4 Lib/test/test_ssl.py
--- a/Lib/test/test_ssl.py Fri May 06 09:31:02 2011 +0300
+++ b/Lib/test/test_ssl.py Fri May 06 12:47:14 2011 +0200
@@ -277,6 +277,24 @@
 (('organizationName', 'Google Inc'),))}
 fail(cert, 'mail.google.com')
 
+ # No DNS entry in subjectAltName but a commonName
+ cert = {'notAfter': 'Dec 18 23:59:59 2099 GMT',
+ 'subject': ((('countryName', 'US'),),
+ (('stateOrProvinceName', 'California'),),
+ (('localityName', 'Mountain View'),),
+ (('commonName', 'mail.google.com'),)),
+ 'subjectAltName': (('othername', 'blabla'), )}
+ ok(cert, 'mail.google.com')
+
+ # No DNS entry subjectAltName and no commonName
+ cert = {'notAfter': 'Dec 18 23:59:59 2099 GMT',
+ 'subject': ((('countryName', 'US'),),
+ (('stateOrProvinceName', 'California'),),
+ (('localityName', 'Mountain View'),),
+ (('organizationName', 'Google Inc'),)),
+ 'subjectAltName': (('othername', 'blabla'),)}
+ fail(cert, 'google.com')
+
 # Empty cert / no cert
 self.assertRaises(ValueError, ssl.match_hostname, None, 'example.com')
 self.assertRaises(ValueError, ssl.match_hostname, {}, 'example.com')
Steffen, I will submit a bug report to Mercurial as soon as this patch is expected to be integrate in py3k.
msg135293 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2011年05月06日 13:32
New changeset d4c2a99d1bad by Antoine Pitrou in branch '3.2':
Issue #12000: When a SSL certificate has a subjectAltName without any
http://hg.python.org/cpython/rev/d4c2a99d1bad
New changeset 1b37827984ba by Antoine Pitrou in branch 'default':
Issue #12000: When a SSL certificate has a subjectAltName without any
http://hg.python.org/cpython/rev/1b37827984ba 
msg135294 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2011年05月06日 13:37
Patch committed in 3.2 and 3.x, thank you!
msg135309 - (view) Author: Mads Kiilerich (kiilerix) * Date: 2011年05月06日 15:35
In my opinion the RFCs are a bit unclear about how iPAddress subjectAltNames should be handled. (I also don't know if Python currently do the right thing by accepting and matching IP addresses if specified in commonName.)
Until now Python failed to the safe side by not matching on subjectAltName iPAddress but also not falling back to commonName if they were specified. AFAICS, with this change it is possible to create strange certificates that Python would accept when an IP address matched commonName but other implementations would reject because of iPAddress mismatch.
That is probably not a real problem, but I wanted to point it out as the biggest issue I could find with this fix. Nice catch.
We could perhaps add IP addresses to dnsnames even though we don't match on them.
msg135411 - (view) Author: Nicolas Bareil (nbareil) Date: 2011年05月07日 07:48
Hello Mads
> Until now Python failed to the safe side by not matching on 
> subjectAltName iPAddress but also not falling back to commonName
> if they were specified. AFAICS, with this change it is possible to 
> create strange certificates that Python would accept when an IP 
> address matched commonName but other implementations would reject 
> because of iPAddress mismatch.
Good point! But I think we already have this issue with a certificate 
like this one:
cert = { 'subject': ((('commonName', '192.168.1.1'),),)}
ok(cert, '192.168.1.1')
Do you think this test should fail?
msg138405 - (view) Author: Mads Kiilerich (kiilerix) * Date: 2011年06月16日 00:07
Nicolas Bareil wrote, On 05/07/2011 09:48 AM:
> Do you think this test should fail?
Until now I have considered this behaviour OK but undocumented and 
officially unsupported in Python. One (the best?) reason for considering 
it OK is that if someone (intentionally or not) trusts a certificate 
that happens to have the textual representation of an IP address in 
commonName then there is no doubt what the intention with that is. This 
case is thus within what i considered secure behaviour.
But the more I look at it the more convinced I get that this test should 
fail. RFC 2818 mentions subjectAltName iPAddress as a "must" for IP 
addresses - even though it only uses a lower-case and thus 
perhaps-not-necessarily-authoritative "must". But the best argument 
against IP in commonName is that it isn't mentioned anywhere, and when 
it isn't explicitly permitted we should consider it forbidden.
A consequence of that is that my previous concern is invalid. There is 
no reason the presence of a subjectAltName iPAddress should prevent 
fallback from dNSName to commonName.
History
Date User Action Args
2022年04月11日 14:57:16adminsetgithub: 56209
2011年06月16日 00:07:05kiilerixsetmessages: + msg138405
2011年05月07日 07:48:34nbareilsetmessages: + msg135411
2011年05月06日 15:35:10kiilerixsetmessages: + msg135309
2011年05月06日 14:15:59kiilerixsetnosy: + kiilerix
2011年05月06日 13:37:28pitrousetstatus: open -> closed
resolution: fixed
messages: + msg135294

stage: resolved
2011年05月06日 13:32:51python-devsetnosy: + python-dev
messages: + msg135293
2011年05月06日 10:48:28nbareilsetmessages: + msg135275
versions: + Python 3.4
2011年05月05日 14:04:08sdaodensetnosy: + sdaoden
messages: + msg135205
2011年05月05日 13:35:35pitrousetnosy: + pitrou

messages: + msg135200
versions: - Python 3.4
2011年05月04日 11:35:52nbareilcreate

AltStyle によって変換されたページ (->オリジナル) /