Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python-Requests: direct .pem pinning with self-signed cert

Tags:

python

ssl

Using python-requests, how can I pin a self-signed .pem certificate for a specific server directly, without using CA root bundles?

Is this currently possible? If yes, can you please provide an example?

I read https://2.python-requests.org/en/v2.8.1/user/advanced/#ssl-cert-verification but am not sure if this applies to what I'm trying to do:

You can also specify a local cert to use as client side certificate, as a single file (containing the private key and the certificate) or as a tuple of both file’s path: requests.get('https://kennethreitz.com', cert=('/path/server.crt', '/path/key')) Response [200]

like image 508
user4164456 Avatar asked Oct 21 '14 04:10

user4164456


People also ask

Does request use certifi?

Requests uses certificates from the package certifi. This allows for users to update their trusted certificates without changing the version of Requests. Before version 2.16, Requests bundled a set of root CAs that it trusted, sourced from the Mozilla trust store.

Is SSL pinning deprecated?

HTTP pinningAlmost all browsers no longer support it as attacks against HPKP surfaced. HPKP is being replaced by the reactive Certificate Transparency framework coupled with the Expect-CT header.


2 Answers

Because the certificate file is self-signed, this works just as you would do it normally with requests. Below is a step-by-step procedure:

  1. Obtain the self-signed certificate, ideally in some secure, out-of-band manner. For example, I run a webserver that offers HTTPS access via a self-signed certificate, so I downloaded the certificate using scp:

    scp <username>@<server>:/path/to/certfile.crt .
    
  2. Because I use nginx this is already in PEM format, but if it's not you'll need to convert it. That's outside the scope of this answer.

  3. Use the certificate file from inside requests:

    r = requests.get('https://yoursite.com/', verify='certfile.crt')
    

That's all you need to do.

If you can't obtain the certificate in an out-of-band manner you trust, you can obtain the certificate using your browser. All browsers will let you export the certificate via their UIs. This is less-secure: if someone is going to MITM you then they may well have already started, and can offer you their MITM root CA instead of your self-signed cert.

like image 63
Lukasa Avatar answered Sep 29 '22 05:09

Lukasa


You can also verify certificates against their fingerprints. For this you need a custom transport adapter for requests. An example for a simple one can be found here:

https://github.com/untitaker/vdirsyncer/blob/9d3a9611b2db2e92f933df30dd98c341a50c6211/vdirsyncer/utils/init.py#L198

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

class _FingerprintAdapter(requests.adapters.HTTPAdapter):
    def __init__(self, fingerprint=None, **kwargs):
        self.fingerprint = str(fingerprint)
        super(_FingerprintAdapter, self).__init__(**kwargs)

    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = PoolManager(num_pools=connections,
                                       maxsize=maxsize,
                                       block=block,
                                       assert_fingerprint=self.fingerprint)
like image 25
t-8ch Avatar answered Sep 29 '22 07:09

t-8ch