How does urllib3 determine which TLS extensions to use? - python

I'd like to modify the Extensions that I send in the client Hello packet with python.
I've had a read of most of the source code found on GitHub for urllib3 but I still don't know how it determines which TLS extensions to use.
I am aware that it will be quite low level and the creators of urllib3 may just import another package to do this for them. If this is the case, which package do they use?
If not, how is this determined?
Thanks in advance for any assistance.

The HTTPS support in urllib3 uses the ssl package which uses the openssl C-library. ssl does not provide any way to directly fiddle with the TLS extension except for setting the hostname in the TLS handshake (i.e. server_name extension aka SNI).

Related

Get available SSL/TLS protocols in Python 2.7

Avoiding trial-and-error, I would like to list which TLS/SSL protocols are available for use on the system (in a cross-platform way).
Python ssl module offers constants such as:
ssl.PROTOCOL_SSLv2
But these do not say anything about what the system actually supports when connecting to a server.
Additionally, is there a way to get from an SSLContext the supported protocols that will be used?
For example SSLv2 and SSLv3 are disabled when using ssl.create_default_contex(), would it be possible to somehow parse the SSLContext.options to list which protocols are supported?
EDIT:
For example on the latest Debian TLSv1 is disabled and connecting to TLSv1-only hosts will fail, yet ssl.PROTOCOL_TLSv1 is still available and also creating ssl.SSLContext(ssl.PROTOCOL_TLSv1) works.

Boto [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed while connecting to S3

I am trying to connect to S3 using boto, but it seems to fail. I've tried some workarounds, but they don't seem to work. Can anyone please help me with this. Below is the code.
import boto
if not boto.config.has_section('Credentials'):
boto.config.add_section('Credentials')
boto.config.set('Credentials', 'aws_access_key_id', AWS_KEY)
boto.config.set('Credentials', 'aws_secret_access_key', AWS_SECRET_KEY)
if not boto.config.has_section('Boto'):
boto.config.add_section('Boto')
boto.config.set('Boto', 'https_validate_certificates', 'False')
boto.config.add_section('aws info')
boto.config.set('aws info','aws_validate_certs','False')
s3 = boto.connect_s3(validate_certs=False)
bucket = s3.get_bucket(Bucket_NAME)
Probably your bucket name contains a dot, that's why ssl certificate verification fails. This is quite a frequent problem, see this github issue for example.
Don't use an insecure connection (is_secure=False), instead use OrdinaryCallingFormat:
import boto
conn = boto.s3.connect_to_region('eu-west-1', calling_format=boto.s3.connection.OrdinaryCallingFormat())
bucket = conn.get_bucket(your_bucket)
You probably need to update your AWS Region, e.g. us-east-1
In boto3, if you are using the s3 client, use verify=False when creating the s3 client.
For eg:
s3 = boto3.client('s3', verify=False)
As mentioned on boto3 documentation, this only turns off validation of SSL certificates. SSL will still be used (unless use_ssl is False), but SSL certificates will not be verified.
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html
I found a way,
used is_secure=False in connect_s3().
I encounter this problem, too. My environment is Ubuntu 15.04, Python 2.7.9 and Boto 2.38.0.
Setting the argument validate_certs=False doesn't make it work with the HTTPS connection without valid certificate. After reading the code of boto, I found that it's a behavior of Python's ssl modules. Then I found a solution here: "SSL: CERTIFICATE_VERIFY_FAILED" Error. And the solution does work!!!.
add verify=False
boto3.resource(
"s3",
endpoint_url=<URL>,
aws_access_key_id=<ID>,
aws_secret_access_key=<Key>,
verify=False
)
macOS users: If you are using the Python 3.6 from the python.org
binary installer linked on this page, please carefully read the
Important Information displayed during installation; this information
is also available after installation by clicking on
/Applications/Python 3.6/ReadMe.rtf. There is important information
there about changes in the 3.6.0 installer-supplied Python,
particularly with regard to SSL certificate validation.
https://www.python.org/downloads/release/python-360/
From ReadMe.rtf at the time of this writing:
Certificate verification and OpenSSL
NEW This variant of Python 3.6 now includes its own private copy of OpenSSL 1.0.2. Unlike previous releases, the deprecated
Apple-supplied OpenSSL libraries are no longer used. This also means
that the trust certificates in system and user keychains managed by
the Keychain Access application and the security command line utility
are no longer used as defaults by the Python ssl module. For 3.6.0, a
sample command script is included in /Applications/Python 3.6 to
install a curated bundle of default root certificates from the
third-party certifi package (https://pypi.python.org/pypi/certifi).
If you choose to use certifi, you should consider subscribing to the
project's email update service to be notified when the certificate
bundle is updated.
The bundled pip included with the Python 3.6 installer has its own
default certificate store for verifying download connections.
Office laptops usually have network monitors installed. Figured out that it was the network monitoring software interfering with python, not letting it verify ssl certs of aws. We had to import its's cert(got from office) onto python's cacert.pem file, then it started working fine.

Using TLS1.2 with ftplib in python 2.7.*

I need to connect to a ftp server which requires TLS 1.2
the ftplib has an object called FTP_TLS.ssl_version but I can't choose ssl.PROTOCOL_TLSv1_2 because its available only in Python 3.4 and will be available at python 2.7.9 which is not released as of this post.
There is no way I can change my program to use Python 3.4 so what are my options?
One could assume that the default should already be to connect with the best TLS version possible. An explicit setting to TLS1.2 just means, that the client will not accept anything below TLS1.2 back from the server.
Unfortunately ftplib decided to hard code the version to TLSv1 and thus reduce the connection to TLS 1.0 even if the OpenSSL would support better versions. Since there is no way with older python versions to explicitly request TLS 1.1 or TLS 1.2 you need to request SSLv23 which automatically requests the best version possible:
import ssl
from ftplib import FTP_TLS
ftps = FTP_TLS('127.0.0.1')
## set protocol to SSLv23 to request best version
ftps.ssl_version = ssl.PROTOCOL_SSLv23;
ftps.login()
ftps.prot_p()
ftps.retrlines('LIST')
ftps.quit()
The only change to normal use of ftplib is to set ssl_version to ssl.PROTOCOL_SSLv23 and thus it will request the best version possible. If this will be TLS 1.2 depends on the server and on the supported versions in the client. With Ubuntu TLS 1.2 is disabled on the client side up to version 13.10, so it will use at most TLS 1.1. With Ubuntu 14.04 it will use TLS 1.2 if the server supports it.
A side effect of this change is that it will not send an AUTH TLS command to the FTP server, but instead the older AUTH SSL command, but most servers will probably not care. Another side effect is that it will also allow TLS 1.0 or SSL 3.0 if the server does not support anything better. If you don't want this you have to fiddle with the SSL context options, but it looks like this is only available with python3.

How to disable SNI in Python Requests?

I'm attempting to use requests to access a remote server over SSL. Unfortunately it's misconfigured such that it responds with the error TLSV1_UNRECOGNIZED_NAME during the SNI handshake, which is ignored by browsers but raises an exception in requests.
This appears to be the same issue as this question, but in Python rather than Java: SSL handshake alert: unrecognized_name error since upgrade to Java 1.7.0`
The connection works perfectly in Python 2, which doesn't support SNI. How can I disable SNI in Python 3 so that the connection works?
I couldn't find a way to disable SNI on the requests level, but I found a hack that will trick it into thinking SNI isn't supported. Add this code before importing requests (not after, or it will be too late):
import ssl
ssl.HAS_SNI = False

patching python eventlet to workaround OpenSSL TLS 1.1 bug in Ubuntu 12.04

I've just ran into a bug in OpenSSL in Ubuntu 12.04 with TLS connections and I need to workaround it.
Bug brief - on Ubuntu 12.04 bug in OpenSSL implementation makes various calls to HTTPS with TLS 1.1 fail randomly.
The usual python workaround is also provided on that link above, and it basically enforces TLS 1.0 to be used instead of TLS 1.1. Yet that workaround doesn't work for me out of the box because I'm using eventlet lib that implements non-blocking HTTP requests.
As I understand - eventlet library redefines some classes related to the matter and particularly - the httplib.HTTPSConnection class that I need to patch to enforce TLS 1.0.
So the question is - what exactly I need to patch in eventlet or what to redefine to enforce TLS 1.0 connection for non-blocking HTTP calls via eventlet?
First, you should upgrade eventlet. As of 2013-09, the latest release is 0.14 and we have large number of bugs fixed since 0.9.16.
Second, the solution provided there is a bit too complicated and only fixes httplib. If they provided solution for ssl, it would also fix HTTPS and work with eventlet.
Here's a simpler version for Python 2.6+ that fixes all SSL sockets:
import functools
import ssl
old_init = ssl.SSLSocket.__init__
#functools.wraps(old_init)
def ubuntu_openssl_bug_965371(self, *args, **kwargs):
kwargs['ssl_version'] = ssl.PROTOCOL_TLSv1
old_init(self, *args, **kwargs)
ssl.SSLSocket.__init__ = ubuntu_openssl_bug_965371
I don't have access to broken system right now, so I can't really test it. Does not break good version of openssl.
A simple fix that worked for me was to override SSL's default protocol:
import ssl
ssl.PROTOCOL_SSLv23 = ssl.PROTOCOL_TLSv1

Categories

Resources