Make working with SSL easier. #3007
Replies: 2 comments 6 replies
-
Great, yup! What I'd been planning here was reusing the existing Adapting our existing documentation here's what I've got... SSL certificatesWhen making a request over HTTPS, HTTPX needs to verify the identity of the requested host. To do this, it uses a bundle of SSL certificates (a.k.a. CA bundle) delivered by a trusted certificate authority (CA). Enabling and disabling verificationBy default httpx will verify HTTPS connections, and raise an error for invalid SSL cases... >>> httpx.get("https://expired.badssl.com/")
httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:997) You can configure the verification using >>> ssl_context = httpx.SSLContext()
>>> ssl_context
<httpx.SSLContext [verify=True]>
>>> httpx.get("https://www.example.com", ssl_context=ssl_context)
httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:997) For example, you can use this to disable verification completely and allow insecure requests... >>> no_verify = httpx.SSLContext(verify=False)
>>> no_verify
<httpx.SSLContext [verify=False]>
>>> httpx.get("https://expired.badssl.com/", ssl_context=no_verify)
<Response [200 OK]> Configuring client instancesIf you're using a >>> ssl_context = httpx.SSLContext()
>>> client = httpx.Client(ssl_context=ssl_context) The If you need different SSL settings in different cases you should use more that one client instance, with different settings on each. Each client will then be using an isolated connection pool with a specific fixed SSL configuration on all connections within that pool. Changing the verification defaultsBy default, HTTPX uses the CA bundle provided by Certifi. The following all have the same behaviour... Using the default SSL context. >>> client = httpx.Client()
>>> client.get("https://www.example.com")
<Response [200 OK]> Using the default SSL context, but specified explicitly. >>> default = httpx.SSLContext()
>>> client = httpx.Client(ssl_context=default)
>>> client.get("https://www.example.com")
<Response [200 OK]> Using the default SSL context, with >>> default = httpx.SSLContext(verify=True)
>>> client = httpx.Client(ssl_context=default)
>>> client.get("https://www.example.com")
<Response [200 OK]> Using an SSL context, with >>> default = httpx.SSLContext(verify=certifi.where())
>>> client = httpx.Client(ssl_context=default)
>>> client.get("https://www.example.com")
<Response [200 OK]> For some advanced situations may require you to use a different set of certificates, either by specifying a PEM file: >>> custom_cafile = httpx.SSLContext(verify="path/to/certs.pem")
>>> client = httpx.Client(ssl_context=custom_cafile)
>>> client.get("https://www.example.com")
<Response [200 OK]> Or by providing an certificate directory: >>> custom_capath = httpx.SSLContext(verify="path/to/certs")
>>> client = httpx.Client(ssl_context=custom_capath)
>>> client.get("https://www.example.com")
<Response [200 OK]> These usages are equivelent to using Client side certificatesYou can also specify a local cert to use as a client-side certificate, either a path to an SSL certificate file... >>> cert = "path/to/client.pem"
>>> ssl_context = httpx.SSLContext(cert=cert)
>>> httpx.get("https://example.org", ssl_context=ssl_context)
<Response [200 OK]> Or two-tuple of (certificate file, key file)... >>> cert = ("path/to/client.pem", "path/to/client.key")
>>> ssl_context = httpx.SSLContext(cert=cert)
>>> httpx.get("https://example.org", ssl_context=ssl_context)
<Response [200 OK]> Or a three-tuple of (certificate file, key file, password)... >>> cert = ("path/to/client.pem", "path/to/client.key", "password")
>>> ssl_context = httpx.SSLContext(cert=cert)
>>> httpx.get("https://example.org", ssl_context=ssl_context)
<Response [200 OK]> These configurations are equivalent to using Using alternate SSL contextsYou can also use an alternate For example, using the import ssl
import truststore
import httpx
ssl_context = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
client = httpx.Client(ssl_context=ssl_context) Or working directly with Python's standard library... import ssl
import httpx
ssl_context = ssl.create_default_context()
client = httpx.Client(ssl_context=ssl_context) Working with
|
Beta Was this translation helpful? Give feedback.
-
Just for the record, AnyIO treats |
Beta Was this translation helpful? Give feedback.
-
I believe the current SSL configurations are somewhat complicated, so we can consider simplifying things here.
As Tom mentioned in #947, one of the solutions is:
To be honest, this approach does not appear to be user friendly enough.
Users must create an
SSL context
each time they want to simply disable SSL validation, which is not a fun thing to do, especially for people who don't know what anSSL context
is.Example:
Instead, we can consider keeping the current approach but making some changes, such as removing the
cert
parameter entirely, and users who want to use client side certificates should consider creating their own ssl context and passing it throughhttpx.Client
.Instead of
do
Also, because the user who wants to use other certificates appears to be able to configure the SSL context himself, we can deprecate passing the ca bundle path to
verify
.The current approach is what the requests library does, but we could consider switching to an aiohttp-like API with only one
ssl
argument, which can beNone
,ssl context
, orFalse
.None
indicates that the user wishes to use the default configuration.False
indicates that the user simply wishes to disable SSL verification, whereasSSL Context
indicates that the user provides its own configuredSSL context
and that the library should not perform any implicit configurations on top of this ssl context.We can deprecate the
verify
argument and instead recommend that users use the ssl argument, and we can completely remove the verify argument with the next major version.What are your thoughts? @encode/maintainers
Beta Was this translation helpful? Give feedback.
All reactions