|
1 | 1 | When 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). |
2 | 2 |
|
3 | | -## Changing the verification defaults |
| 3 | +### Enabling and disabling verification |
4 | 4 |
|
5 | | -By default, HTTPX uses the CA bundle provided by [Certifi](https://pypi.org/project/certifi/). This is what you want in most cases, even though some advanced situations may require you to use a different set of certificates. |
| 5 | +By default httpx will verify HTTPS connections, and raise an error for invalid SSL cases... |
6 | 6 |
|
7 | | -If you'd like to use a custom CA bundle, you can use the `verify` parameter. |
8 | | - |
9 | | -```python |
10 | | -import httpx |
11 | | - |
12 | | -r = httpx.get("https://example.org", verify="path/to/client.pem") |
| 7 | +```pycon |
| 8 | +>>> httpx.get("https://expired.badssl.com/") |
| 9 | +httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:997) |
13 | 10 | ``` |
14 | 11 |
|
15 | | -Alternatively, you can pass a standard library `ssl.SSLContext`. |
| 12 | +You can disable SSL verification completely and allow insecure requests... |
16 | 13 |
|
17 | 14 | ```pycon |
18 | | ->>> import ssl |
19 | | ->>> import httpx |
20 | | ->>> context = ssl.create_default_context() |
21 | | ->>> context.load_verify_locations(cafile="/tmp/client.pem") |
22 | | ->>> httpx.get('https://example.org', verify=context) |
| 15 | +>>> httpx.get("https://expired.badssl.com/", verify=False) |
23 | 16 | <Response [200 OK]> |
24 | 17 | ``` |
25 | 18 |
|
26 | | -We also include a helper function for creating properly configured `SSLContext` instances. |
| 19 | +### Configuring client instances |
27 | 20 |
|
28 | | -```pycon |
29 | | ->>> context = httpx.create_ssl_context() |
30 | | -``` |
31 | | - |
32 | | -The `create_ssl_context` function accepts the same set of SSL configuration arguments |
33 | | -(`trust_env`, `verify`, `cert` and `http2` arguments) |
34 | | -as `httpx.Client` or `httpx.AsyncClient` |
| 21 | +If you're using a `Client()` instance you should pass any `verify=<...>` configuration when instantiating the client. |
35 | 22 |
|
36 | | -```pycon |
37 | | ->>> import httpx |
38 | | ->>> context = httpx.create_ssl_context(verify="/tmp/client.pem") |
39 | | ->>> httpx.get('https://example.org', verify=context) |
40 | | -<Response [200 OK]> |
41 | | -``` |
| 23 | +By default the [certifi CA bundle](https://certifiio.readthedocs.io/en/latest/) is used for SSL verification. |
42 | 24 |
|
43 | | -Or you can also disable the SSL verification entirely, which is _not_ recommended. |
| 25 | +For more complex configurations you can pass an [SSL Context](https://docs.python.org/3/library/ssl.html) instance... |
44 | 26 |
|
45 | 27 | ```python |
| 28 | +import certifi |
46 | 29 | import httpx |
| 30 | +import ssl |
47 | 31 |
|
48 | | -r = httpx.get("https://example.org", verify=False) |
| 32 | +# This SSL context is equivelent to the default `verify=True`. |
| 33 | +ctx = ssl.create_default_context(cafile=certifi.where()) |
| 34 | +client = httpx.Client(verify=ctx) |
49 | 35 | ``` |
50 | 36 |
|
51 | | -## SSL configuration on client instances |
52 | | - |
53 | | -If you're using a `Client()` instance, then you should pass any SSL settings when instantiating the client. |
| 37 | +Using [the `truststore` package](https://truststore.readthedocs.io/) to support system certificate stores... |
54 | 38 |
|
55 | 39 | ```python |
56 | | -client = httpx.Client(verify=False) |
57 | | -``` |
58 | | - |
59 | | -The `client.get(...)` method and other request methods *do not* support changing the SSL settings on a per-request basis. 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. |
| 40 | +import ssl |
| 41 | +import truststore |
| 42 | +import httpx |
60 | 43 |
|
61 | | -## Client Side Certificates |
| 44 | +# Use system certificate stores. |
| 45 | +ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
| 46 | +client = httpx.Client(verify=ctx) |
| 47 | +``` |
62 | 48 |
|
63 | | -You can also specify a local cert to use as a client-side certificate, either a path to an SSL certificate file, or two-tuple of (certificate file, key file), or a three-tuple of (certificate file, key file, password) |
| 49 | +Loding an alternative certificate verification store using [the standard SSL context API](https://docs.python.org/3/library/ssl.html)... |
64 | 50 |
|
65 | 51 | ```python |
66 | | -cert = "path/to/client.pem" |
67 | | -client = httpx.Client(cert=cert) |
68 | | -response = client.get("https://example.org") |
| 52 | +import httpx |
| 53 | +import ssl |
| 54 | + |
| 55 | +# Use an explicitly configured certificate store. |
| 56 | +ctx = ssl.create_default_context(cafile="path/to/certs.pem") # Either cafile or capath. |
| 57 | +client = httpx.Client(verify=ctx) |
69 | 58 | ``` |
70 | 59 |
|
71 | | -Alternatively... |
| 60 | +### Client side certificates |
72 | 61 |
|
73 | | -```python |
74 | | -cert = ("path/to/client.pem", "path/to/client.key") |
75 | | -client = httpx.Client(cert=cert) |
76 | | -response = client.get("https://example.org") |
77 | | -``` |
| 62 | +Client side certificates allow a remote server to verify the client. They tend to be used within private organizations to authenticate requests to remote servers. |
78 | 63 |
|
79 | | -Or... |
| 64 | +You can specify client-side certificates, using the [`.load_cert_chain()`](https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain) API... |
80 | 65 |
|
81 | 66 | ```python |
82 | | -cert = ("path/to/client.pem", "path/to/client.key", "password") |
83 | | -client = httpx.Client(cert=cert) |
84 | | -response = client.get("https://example.org") |
| 67 | +ctx = ssl.create_default_context() |
| 68 | +ctx.load_cert_chain(certfile="path/to/client.pem") # Optionally also keyfile or password. |
| 69 | +client = httpx.Client(verify=ctx) |
85 | 70 | ``` |
86 | 71 |
|
87 | | -## Making HTTPS requests to a local server |
| 72 | +### Working with `SSL_CERT_FILE` and `SSL_CERT_DIR` |
| 73 | + |
| 74 | +`httpx` does respect the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables by default. For details, refer to [the section on the environment variables page](../environment_variables.md#ssl_cert_file). |
| 75 | + |
| 76 | +### Making HTTPS requests to a local server |
88 | 77 |
|
89 | 78 | When making requests to local servers, such as a development server running on `localhost`, you will typically be using unencrypted HTTP connections. |
90 | 79 |
|
91 | | -If you do need to make HTTPS connections to a local server, for example to test an HTTPS-only service, you will need to create and use your own certificates. Here's one way to do it: |
| 80 | +If you do need to make HTTPS connections to a local server, for example to test an HTTPS-only service, you will need to create and use your own certificates. Here's one way to do it... |
92 | 81 |
|
93 | 82 | 1. Use [trustme](https://github.com/python-trio/trustme) to generate a pair of server key/cert files, and a client cert file. |
94 | | -1. Pass the server key/cert files when starting your local server. (This depends on the particular web server you're using. For example, [Uvicorn](https://www.uvicorn.org) provides the `--ssl-keyfile` and `--ssl-certfile` options.) |
95 | | -1. Tell HTTPX to use the certificates stored in `client.pem`: |
| 83 | +2. Pass the server key/cert files when starting your local server. (This depends on the particular web server you're using. For example, [Uvicorn](https://www.uvicorn.org) provides the `--ssl-keyfile` and `--ssl-certfile` options.) |
| 84 | +3. Configure `httpx` to use the certificates stored in `client.pem`. |
96 | 85 |
|
97 | 86 | ```python |
98 | | -client = httpx.Client(verify="/tmp/client.pem") |
99 | | -response = client.get("https://localhost:8000") |
| 87 | +ctx = ssl.create_default_context(cafile="client.pem") |
| 88 | +client = httpx.Client(verify=ctx) |
100 | 89 | ``` |
0 commit comments