7

Usecase: I want to find out how many ciphers are supported by the hostname with python request module.

I am not able to find a way to provide the cipher name to request module hook. Can anyone suggest the way to provide the way to specify cipher.

import ssl

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager


class Ssl3HttpAdapter(HTTPAdapter):
    """"Transport adapter" that allows us to use SSLv3."""

    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = PoolManager(
            num_pools=connections, maxsize=maxsize,
            block=block, ssl_version=ssl.PROTOCOL_SSLv3)
user87005
  • 964
  • 3
  • 10
  • 27

1 Answers1

11

If you are using requests version 2.12.0+, there is a blog post on Configuring TLS With Requests, which describes new functionality to allow you to configure the SSLContext (note that this blog post was written after the OP asked the question):

The feature added in Requests v2.12.0 is that urllib3 now accepts an SSLContext object in the constructors for ConnectionPool objects. This SSLContext will be used as the factory for the underlying TLS connection, and so all settings applied to it will also be applied to those low-level connections.

The best way to do this is to use the SSLContext factory function requests.packages.urllib3.util.ssl_.create_urllib3_context. This is analogous to Python’s ssl.create_default_context function but applies the more-strict default TLS configuration that Requests and urllib3 both use. This function will return an SSLContext object that can then have further configuration applied. On top of that, the function also takes a few arguments to allow overriding default configuration.

To provide the new SSLContext object, you will need to write a TransportAdapter that is appropriate for the given host.

The following sample code is given as an example of how to re-enable 3DES in Requests using this method.

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.ssl_ import create_urllib3_context

# This is the 2.11 Requests cipher string, containing 3DES.
CIPHERS = (
    'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:'
    'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:'
    '!eNULL:!MD5'
)


class DESAdapter(HTTPAdapter):
    """
    A TransportAdapter that re-enables 3DES support in Requests.
    """
    def init_poolmanager(self, *args, **kwargs):
        context = create_urllib3_context(ciphers=CIPHERS)
        kwargs['ssl_context'] = context
        return super(DESAdapter, self).init_poolmanager(*args, **kwargs)

    def proxy_manager_for(self, *args, **kwargs):
        context = create_urllib3_context(ciphers=CIPHERS)
        kwargs['ssl_context'] = context
        return super(DESAdapter, self).proxy_manager_for(*args, **kwargs)

s = requests.Session()
s.mount('https://some-3des-only-host.com', DESAdapter())
r = s.get('https://some-3des-only-host.com/some-path')

There is also a hack possible, which you can read on the github pages for the requests module, or at https://stackoverflow.com/a/32651967/2364215 but it modifies the underlying library code, I don't recommend it (neither do the authors of the requests module, as you will find on that page). On the other hand, if you are on an older requests package and you can't upgrade, it may be your best option. It amounts to overriding the urllib3 module's DEFAULT_CIPHERS:

requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':RC4-SHA' 

If you have other code that will use the requests module after doing the modification, but doesn't need the modification, you may want to restore DEFAULT_CIPHERS to its previous value.

hlongmore
  • 1,603
  • 24
  • 28
  • This doesn't work anymore now, because [urllib3 2.0 removed DEFAULT_CIPHERS](https://github.com/urllib3/urllib3/issues/2168). – Some Guy May 23 '23 at 07:45
  • @SomeGuy Is it the hack that relies on `DEFAULT_CIPHERS` that doesn't work anymore, or is it the SSLContext method that doesn't work anymore? If it's just the hack that no longer works, it would probably be more correct to say "this doesn't work in Python 3.x and above because..." (where x is probably 9 or 10 or 11 in this case) – hlongmore May 24 '23 at 11:04
  • Well, neither of your solutions works in any python version that is currently supported by urllib, because the DEFAULT_CIPHERS object does not exist in urllib3 2.0. Neither appending to it nor assigning it to the ssl context works, because it's not there anymore. Try running your code and you'll see. – Some Guy May 25 '23 at 14:13
  • 1
    The "clean" solution still works with urllib3 > 2.0, the hacky version does not, since DEFAULT_CIPHERS is gone (since the system default of OpenSSL 1.1.1+ is now used) – TobiX Jul 09 '23 at 08:03