-
Notifications
You must be signed in to change notification settings - Fork 40
Authenticated HTTP Requests
This document specifies an experimental and simplified HTTP digital signature algorithm for requests and responses designed to protect against common attacks and provide the fundamental layer for authorized API access and account authentication/authorization.
This document describes a pre-exchanged public/private digital signature algorithm used to authenticate HTTP requests from known third parties, and for those third parties to authenticate the HTTP response returning from the server. Special attention has been paid to MITM attacks: mitigated through the use of end-to-end SSL/TLS channel encryption with verified certificates in addition to dedicated client-server signing key pairs.
Existing technologies exist which purport to increase security while individually handling authentication (OpenID), authorization (OAuth), or a mixture of the two (OAuth 2). The problem with these technologies is that of design-by-committee. The major influencers of these standards has been enterprise, which generally means they’re severely over-engineered, hideously difficult to configure securely, and exhibit bugs which result in multiple incompatible independent implementations, most notably with OAuth 2.
The problems with these “entrenched” standards include the fact that one of the core authors of OAuth 2 left the project and had his name stripped from all documents on the way out. He has a good presentation on some of the failures of the protocol.
This proposal covers the primary use cases (starting with authenticated API/RPC calls) of these more complicated protocols, while excluding features that have been better implemented in other standards, such as OAuth 1’s use of custom data encryption (in addition to the typical use of SSL/TLS) and without the complexity that results in OAuth 2’s 70+ page security analysis whitepaper and multiple (incompatible) hot fix implementations.
An application is any third-party software which wishes to issue RPC/API calls against a server utilizing this protocol. A sample implementation is available in the Python protocol bindings package. Applications must:
- Generate and securely store a 256-bit ECDSA key and store information as indicated by the authentication/authorization process.
- Use the NIST curves.
- Use the SHA-256 hashing algorithm.
- Share the public key with the server out-of-band, generally through a web interface.
- On each request:
- Perform request canonicalization by combining (using UNIX newlines) the following:
- The request’s
Dateheader. The request will be rejected outright by the server if theDateheader is out-of-bounds by more than 30 seconds using an atomic-synchronized (NTP) clock. - The request URI, e.g.:
https://example.com/api/endpoint - The HTTP request body.
- The request’s
- Submit the HTTP request with two additional HTTP headers:
-
X-Service— the unique identifier assigned to your application when exchanging keys. -
X-Signature— the result of digitally signing the aforementioned canonicalized data. May, depending on client and server capabilities, be sent as a chunked transfer HTTP header trailer. The signature should be hex encoded.
-
- Verify the response
X-Signatureheader using the pre-exchanged application-specific public key. - Discard responses which do not validate.
- Perform request canonicalization by combining (using UNIX newlines) the following:
A server is any software service which wishes to respond to RPC/API calls from applications utilizing this protocol. A sample implementation is available in the Python protocol bindings package. Servers must:
- Maintain a database of registered applications and their public keys.
- Generate and securely store a 256-bit ECDSA public/private key pair unique to each registered application.
- Use the NIST curves.
- Use the SHA-256 hashing algorithm.
- Share the public key of this pair with the client out-of-band, generally through a web interface.
- Before processing the request:
- Ensure the
X-ServiceandX-Signaturekeys are present. - Validate the
X-Signatureagainst the canonical data as defined in the application section.
- Ensure the
- After processing the request:
- Ensure the returned data is either empty or valid JSON data.
- Canonicalize the response by combining (using UNIX newlines) the following:
- The requesting service ID, from the
X-Serviceheader. - The response’s
Dateheader. - The request’s endpoint URI.
- The response body.
- The requesting service ID, from the
- Add an
X-Signatureheader that is the result of signing the canonical data using the application-specific server key.
- Discard requests which fail validation by returning a
400 Bad Requeststatus code with explanation string in the body.
The official implementation of the above protocol in Python is available from the API package and is used across most Python projects.
Additional implementations may be available.