summaryrefslogtreecommitdiff
path: root/docs/user_guide/security.md
blob: cad4d3f5f47514a0abd6855cf4054d6fcc161d74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
(security)=
# {fa}`lock` Security

## Pickle Vulnerabilities
:::{warning}
The python `pickle` module has [known security vulnerabilities](https://docs.python.org/3/library/pickle.html),
potentially leading to code execution when deserialzing data.
:::

This means it should only be used to deserialize data that you trust hasn't been tampered with.
Since this isn't always possible, requests-cache can optionally use
[itsdangerous](https://itsdangerous.palletsprojects.com) to add a layer of security around these operations.
It works by signing serialized data with a secret key that you control. Then, if the data is tampered
with, the signature check fails and raises an error.

Optional dependencies can be installed with:
```bash
pip install requests-cache[security]
```

## Creating and Storing a Secret Key
To enable this behavior, first create a secret key, which can be any `str` or `bytes` object.

One common pattern for handling this is to store it wherever you store the rest of your credentials
([Linux keyring](https://itsfoss.com/ubuntu-keyring),
[macOS keychain](https://support.apple.com/guide/mac-help/use-keychains-to-store-passwords-mchlf375f392/mac),
[password database](https://keepassxc.org), etc.),
set it in an environment variable, and then read it in your application:
```python
>>> import os
>>> secret_key = os.environ['SECRET_KEY']
```

Alternatively, you can use the [keyring](https://keyring.readthedocs.io) package to read the key
directly:
```python
>>> import keyring
>>> secret_key = keyring.get_password('requests-cache-example', 'secret_key')
```

## Signing Cached Responses
Once you have your key, create a {py:func}`.safe_pickle_serializer` with it:
```python
>>> from requests_cache import CachedSession, safe_pickle_serializer
>>> serializer = safe_pickle_serializer(secret_key=secret_key)
>>> session = CachedSession(serializer=serializer)
>>> session.get('https://httpbin.org/get')
```

:::{note}
You can also make your own {ref}`custom-serializers`, if you would like more control over how
responses are serialized.
:::

You can verify that it's working by modifying the cached item (*without* your key):
```python
>>> serializer_2 = safe_pickle_serializer(secret_key='a different key')
>>> session_2 = CachedSession(serializer=serializer_2)
>>> cache_key = list(session_2.cache.responses.keys())[0]
>>> session_2.cache.responses[cache_key] = 'exploit!'
```

Then, if you try to get that cached response again (*with* your key), you will get an error:
```python
>>> session.get('https://httpbin.org/get')
BadSignature: Signature b'iFNmzdUOSw5vqrR9Cb_wfI1EoZ8' does not match
```

## Removing Sensitive Info
The {ref}`ignored_parameters <filter-params>` option can be used to prevent credentials and other
sensitive info from being saved to the cache. It applies to request parameters, body, and headers.