<div dir="auto">This being a security issue I think it's okay to break 3.6. might even backport to 3.5 if it's easy?</div><div class="gmail_extra"><br><div class="gmail_quote">On Dec 29, 2017 1:59 PM, "Christian Heimes" <<a href="mailto:christian@python.org">christian@python.org</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi,<br>
<br>
tl;dr<br>
This mail is about internationalized domain names and TLS/SSL. It<br>
doesn't concern you if you live in ASCII-land. Me and a couple of other<br>
developers like to change the ssl module in a backwards-incompatible way<br>
to fix IDN support for TLS/SSL.<br>
<br>
<br>
Simply speaking the IDNA standards (internationalized domain names for<br>
applications) describe how to encode non-ASCII domain names. The DNS<br>
system and X.509 certificates cannot handle non-ASCII host names. Any<br>
non-ASCII part of a hostname is punyencoded. For example the host name<br>
'<a href="http://www.xn--bcher-kva.de" rel="noreferrer" target="_blank">www.bücher.de</a>' (books) is translated into '<a href="http://www.xn--bcher-kva.de" rel="noreferrer" target="_blank">www.xn--bcher-kva.de</a>'. In<br>
IDNA terms, '<a href="http://www.xn--bcher-kva.de" rel="noreferrer" target="_blank">www.bücher.de</a>' is called an IDN U-label (unicode) and<br>
'<a href="http://www.xn--bcher-kva.de" rel="noreferrer" target="_blank">www.xn--bcher-kva.de</a>' an IDN A-label (ASCII). Please refer to the TR64<br>
document [1] for more information.<br>
<br>
In a perfect world, it would be very simple. We'd only had one IDNA<br>
standard. However there are multiple standards that are incompatible<br>
with each other. The German TLD .de demands IDNA-2008 with UTS#46<br>
compatibility mapping. The hostname '<a href="http://www.strasse.de" rel="noreferrer" target="_blank">www.straße.de</a>' maps to<br>
'<a href="http://www.xn--strae-oqa.de" rel="noreferrer" target="_blank">www.xn--strae-oqa.de</a>'. However in the older IDNA 2003 standard,<br>
'<a href="http://www.strasse.de" rel="noreferrer" target="_blank">www.straße.de</a>' maps to '<a href="http://www.strasse.de" rel="noreferrer" target="_blank">www.strasse.de</a>', but '<a href="http://strasse.de" rel="noreferrer" target="_blank">strasse.de</a>' is a totally<br>
different domain!<br>
<br>
<br>
CPython has only support for IDNA 2003.<br>
<br>
It's less of an issue for the socket module. It only converts text to<br>
IDNA bytes on the way in. All functions support bytes and text. Since<br>
IDNA encoding does change ASCII and IDNA-encoded data is ASCII, it is<br>
also no problem to pass IDNA2008-encoded text or bytes to all socket<br>
functions.<br>
<br>
Example:<br>
<br>
>>> import socket<br>
>>> import idna  # from PyPI<br>
>>> names = ['<a href="http://strasse.de" rel="noreferrer" target="_blank">straße.de</a>', b'<a href="http://strasse.de" rel="noreferrer" target="_blank">strasse.de</a>', idna.encode('<a href="http://strasse.de" rel="noreferrer" target="_blank">straße.de</a>'),<br>
idna.encode('<a href="http://strasse.de" rel="noreferrer" target="_blank">straße.de</a>').<wbr>encode('ascii')]<br>
>>> for name in names:<br>
...     print(name, socket.getaddrinfo(name, None, socket.AF_INET,<br>
socket.SOCK_STREAM, 0, socket.AI_CANONNAME)[0][3:5])<br>
...<br>
<a href="http://strasse.de" rel="noreferrer" target="_blank">straße.de</a> ('<a href="http://strasse.de" rel="noreferrer" target="_blank">strasse.de</a>', ('89.31.143.1', 0))<br>
b'<a href="http://strasse.de" rel="noreferrer" target="_blank">strasse.de</a>' ('<a href="http://strasse.de" rel="noreferrer" target="_blank">strasse.de</a>', ('89.31.143.1', 0))<br>
b'<a href="http://xn--strae-oqa.de" rel="noreferrer" target="_blank">xn--strae-oqa.de</a>' ('<a href="http://xn--strae-oqa.de" rel="noreferrer" target="_blank">xn--strae-oqa.de</a>', ('81.169.145.78', 0))<br>
<a href="http://xn--strae-oqa.de" rel="noreferrer" target="_blank">xn--strae-oqa.de</a> ('<a href="http://xn--strae-oqa.de" rel="noreferrer" target="_blank">xn--strae-oqa.de</a>', ('81.169.145.78', 0))<br>
<br>
As you can see, '<a href="http://strasse.de" rel="noreferrer" target="_blank">straße.de</a>' is canonicalized as '<a href="http://strasse.de" rel="noreferrer" target="_blank">strasse.de</a>'. The IDNA<br>
2008 encoded hostname maps to a different IP address.<br>
<br>
<br>
On the other hand ssl module is currently completely broken. It converts<br>
hostnames from bytes to text with 'idna' codec in some places, but not<br>
in all. The SSLSocket.server_hostname attribute and callback function<br>
SSLContext.set_servername_<wbr>callback() are decoded as U-label.<br>
Certificate's common name and subject alternative name fields are not<br>
decoded and therefore A-labels. The *must* stay A-labels because<br>
hostname verification is only defined in terms of A-labels. We even had<br>
a security issue once, because partial wildcard like 'xn*.<a href="http://example.org" rel="noreferrer" target="_blank">example.org</a>'<br>
must not match IDN hosts like '<a href="http://xn--bcher-kva.example.org" rel="noreferrer" target="_blank">xn--bcher-kva.example.org</a>'.<br>
<br>
In issue [2] and PR [3], we all agreed that the only sensible fix is to<br>
make 'SSLContext.server_hostname' an ASCII text A-label. But this is an<br>
backwards incompatible fix. On the other hand, IDNA is totally broken<br>
without the fix. Also in my opinion, PR [3] is not going far enough.<br>
Since we have to break backwards compatibility anyway, I'd like to<br>
modify SSLContext.set_servername_<wbr>callback() at the same time.<br>
<br>
Questions:<br>
- Is everybody OK with breaking backwards compatibility? The risk is<br>
small. ASCII-only domains are not affected and IDNA users are broken anyway.<br>
- Should I only fix 3.7 or should we consider a backport to 3.6, too?<br>
<br>
Regards,<br>
Christian<br>
<br>
[1] <a href="https://www.unicode.org/reports/tr46/" rel="noreferrer" target="_blank">https://www.unicode.org/<wbr>reports/tr46/</a><br>
[2] <a href="https://bugs.python.org/issue28414" rel="noreferrer" target="_blank">https://bugs.python.org/<wbr>issue28414</a><br>
[3] <a href="https://github.com/python/cpython/pull/3010" rel="noreferrer" target="_blank">https://github.com/python/<wbr>cpython/pull/3010</a><br>
<br>
<br>
<br>
<br>
______________________________<wbr>_________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org">Python-Dev@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/guido%40python.org" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/options/python-dev/<wbr>guido%40python.org</a><br>
</blockquote></div></div>