Ensuring Message Integrity with HTTP Signatures
In the present day, the most common approach for protecting APIs is by using shared secrets. Every time the API is called, this secret must be presented which is not that secure. Then there are other mechanisms such as OAuth which is used to enforce access control on APIs. However none of these mechanisms provide a way to ensure the integrity of the data being transferred. Also, for certain clients, shared secrets is not a possible solution (e.g. SPA). In these scenarios, HTTP signatures plays an important role in uplifting the security of the APIs.
HTTP signatures provide a mechanism to digitally sign an HTTP message using either symmetric or asymmetric keys, such that it helps to both verify the identity of the sender and, verify that the message was not tampered during the transit.
The HTTP Signature specification [1] provides standardized mechanisms on how the client can include the digital signatures in the HTTP-based requests such that, in addition to ensuring end-to-end message integrity, it also authenticates the clients using the same approach instead of using multiple round trips. Also it eliminates the necessity to use shared secrets.
This specification presents two mechanisms to achieve following distinct purposes.
- Authenticity: only the clients that has the private key can send valid HTTP messages to the server
- Message integrity: HTTP requests are protected from a ‘man in the middle attack’ where the messages can get altered.
Purpose 1: For ensuring message integrity
The "Signature" scheme which is intended primarily to allow a sender to assert the contents of the message sent are correct and have not been altered during transmission or storage in a way that alters the meaning expressed in the original message as signed. Any party reading the message (the verifier) may independently confirm the validity of the message signature. This scheme is agnostic to the client/server direction and can be used to verify the contents of either HTTP requests, HTTP responses, or both.
Sample request on how the client can include Signature scheme with the request to enforce message integrity when calling an API.
curl -i -X GET --cert client-cert.pem --key key.pem
-H Date: <req-date>
-H Digest: SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
-H Authorization: Bearer <token>
-H Signature: keyId=<key-id>,algorithm="rsa-sha256",headers="(request-target) date digest",signature=<signature-string>
-H Accept: application/json
https://example.com/api
Purpose 2: For ensuring client authenticity
The "Authorization" scheme which is intended primarily to allow a sender to request access to a resource or resources by proving that they control a secret key. This specification allows for this both with a shared secret (using HMAC) or with public/ private keys. The "Authorization" scheme is typically used in
authentication processes and not directly for message signing. As a consequence `Authorization` header is normally generated (and the message signed) by the HTTP client and the message verified by the HTTP server.
Sample request on how the client can include the digital signature with the Authorization scheme to enforce client authentication to an API.
curl -X POST --cert client-cert.pem --key key.pem
-H Date: <req-date>
-H Digest: SHA-256=2ajR8Q+lBNm0eQW9DWWX8dZDZLB8+h0Rgmu0UCDdFrw=
-H Authorization: Signature keyId=<key-id>,algorithm="rsa-sha256",headers="(request-target) date digest",signature=<signature-string>
-H Content-Type: application/x-www-form-urlencoded
-d grant_type=client_credentials&scope=scope1
https://localhost:9443/oauth2/token
In both of the above mechanisms, the HTTP signature is calculated based on some parameters which are mostly some of other headers in the request. By concatenating these HTTP Headers together to form a string and, obtaining an encrypted hash over that string using either an asymmetric algorithm (rsa-sha256
) with a public/private key pair or a symmetric algorithm (hmac-sha256
) with a shared secret key , the digital Signature for the HTTP request is produced. Finally, the encrypted bytes are Base64 encoded and <signature-string> is obtained.
In both ‘Signature’ header scheme and ‘Authorization’ Header with signature auth scheme, this encrypted signature string, along with key-id, signing algorithm, header names compose the actual value of the header as shown in the examples.
The “Digest” header contains a hash of the message body.
In the server side, we can authenticate/ensure message integrity by verifying the digital signature of the request by using sender’s cryptographic key (public key in the case of asymmetric algorithm).
In order to learn further about how HTTP signature is constructed and each component of signature, please refer https://tools.ietf.org/html/draft-cavage-http-signatures-11#section-2
If you are using WSO2 API Manager to manage APIs, at the time of writing this article, there is no out-of-the-box solution to support HTTP signature validation. However by using available extensions in the product we can achieve this requirement. Please use following extensible capabilities in the product to implement HTTP signature validation.
For API calls to WSO2 API Manager, we can use a custom API handler [2] to validate the HTTP signature of the API request.
For HTTP signature based client authentication, we need to write a custom oauth client authenticator. [3]