requests-http-signature is a Requests authentication plugin (requests.auth.AuthBase
subclass) implementing
the IETF HTTP Message Signatures draft standard.
$ pip install requests-http-signature
import requests
from requests_http_signature import HTTPSignatureAuth, algorithms
preshared_key_id = 'squirrel'
preshared_secret = b'monorail_cat'
url = 'https://example.com/'
auth = HTTPSignatureAuth(key=preshared_secret,
key_id=preshared_key_id,
signature_algorithm=algorithms.HMAC_SHA256)
requests.get(url, auth=auth)
By default, only the Date
header and the @method
, @authority
, and @target-uri
derived component
identifiers are signed for body-less requests such as GET. The Date
header is set if it is absent. In addition,
the Authorization
header is signed if it is present, and for requests with bodies (such as POST), the
Content-Digest
header is set to the SHA256 of the request body using the format described in the
IETF Digest Fields draft and signed.
To add other headers to the signature, pass an array of header names in the covered_component_ids
keyword argument.
See the API documentation for the full list of options and
details.
The class method HTTPSignatureAuth.verify()
can be used to verify responses received back from the server:
class MyKeyResolver:
def resolve_public_key(self, key_id):
assert key_id == 'squirrel'
return 'monorail_cat'
response = requests.get(url, auth=auth)
verify_result = HTTPSignatureAuth.verify(response,
signature_algorithm=algorithms.HMAC_SHA256,
key_resolver=MyKeyResolver())
More generally, you can reconstruct an arbitrary request using the
Requests API and pass it to verify()
:
request = requests.Request(...) # Reconstruct the incoming request using the Requests API
prepared_request = request.prepare() # Generate a PreparedRequest
HTTPSignatureAuth.verify(prepared_request, ...)
To verify incoming requests and sign responses in the context of an HTTP server, see the flask-http-signature and http-message-signatures packages.
See what is signed
It is important to understand and follow the best practice rule of "See what is signed" when verifying HTTP message signatures. The gist of this rule is: if your application neglects to verify that the information it trusts is what was actually signed, the attacker can supply a valid signature but point you to malicious data that wasn't signed by that signature. Failure to follow this rule can lead to vulnerability against signature wrapping and substitution attacks.
In requests-http-signature, you can ensure that the information signed is what you expect to be signed by only trusting
the data returned by the verify()
method:
verify_result = HTTPSignatureAuth.verify(message, ...)
See the API documentation for full details.
To sign or verify messages with an asymmetric key algorithm, set the signature_algorithm
keyword argument to
algorithms.ED25519
, algorithms.ECDSA_P256_SHA256
, algorithms.RSA_V1_5_SHA256
, or
algorithms.RSA_PSS_SHA512
.
For asymmetric key algorithms, you can supply the private key as the key
parameter to the HTTPSignatureAuth()
constructor as bytes in the PEM format, or configure the key resolver as follows:
with open('key.pem', 'rb') as fh:
auth = HTTPSignatureAuth(signature_algorithm=algorithms.RSA_V1_5_SHA256,
key=fh.read(),
key_id=preshared_key_id)
requests.get(url, auth=auth)
class MyKeyResolver:
def resolve_public_key(self, key_id: str):
return public_key_pem_bytes[key_id]
def resolve_private_key(self, key_id: str):
return private_key_pem_bytes[key_id]
auth = HTTPSignatureAuth(signature_algorithm=algorithms.RSA_V1_5_SHA256,
key=fh.read(),
key_resolver=MyKeyResolver())
requests.get(url, auth=auth)
To generate a Content-Digest header using SHA-512 instead of the default SHA-256, subclass HTTPSignatureAuth
as
follows:
class MySigner(HTTPSignatureAuth): signing_content_digest_algorithm = "sha-512"
- Project home page (GitHub)
- Package documentation
- Package distribution (PyPI)
- Change log
- http-message-signatures - a dependency of this library that handles much of the implementation
- IETF HTTP Signatures draft
Please report bugs, issues, feature requests, etc. on GitHub.
Licensed under the terms of the Apache License, Version 2.0.