HTTP Signature
http-signature is a Node.js library providing a reference implementation for Joyent's HTTP Signature scheme. This scheme allows clients and servers to sign and verify HTTP messages to ensure authenticity and integrity. The package's latest version, 1.4.0, was published over two years ago (as of late 2023), indicating a low-cadence or maintenance status, despite high weekly download numbers. While still widely used, it's important to note that this library implements Joyent's specific scheme, which predates and differs from the more recent IETF RFC 9421 "HTTP Message Signatures" standard. Other libraries, such as `http-message-signatures`, implement the newer RFC. This package is primarily designed for CommonJS environments and does not natively support ESM imports.
Common errors
-
Invalid Signature
cause The generated signature does not match the one expected by the server, often due to mismatched keys, incorrect algorithms, altered payloads, or clock skew.fixEnsure correct private/public key pairs are used on client/server, the same signing algorithm is configured, and all signed headers match precisely. Check for payload modifications in transit or significant time differences between client and server. -
TypeError: Cannot read properties of undefined (reading 'keyId')
cause The `httpSignature.parseRequest()` function returned `undefined` or an incomplete object, indicating an issue parsing the incoming HTTP request's Signature header.fixInspect the incoming request's `Authorization` or `Signature` header to ensure it's present, correctly formatted, and adheres to the expected HTTP Signature scheme parameters (keyId, algorithm, headers, signature). -
Error: Missing or malformed signature header
cause The incoming HTTP request lacks a properly formed 'Authorization: Signature ...' or 'Signature: ...' header as required by the HTTP Signature specification.fixEnsure the client is adding the `Authorization: Signature` header with all required parameters (keyId, algorithm, headers, signature) to the HTTP request before sending it.
Warnings
- breaking Versions of `http-signature` prior to 1.0.0 were vulnerable to timing attacks during signature comparison, potentially allowing attackers to guess signatures character by character. Upgrade to version 1.0.0 or higher to mitigate this vulnerability.
- breaking Versions prior to 0.10.0 were susceptible to header forgery because HTTP header names were not included in the signed string, allowing an attacker to modify header names and alter the request's meaning without invalidating the signature. Upgrade to version 0.10.0 or higher.
- gotcha This library implements Joyent's specific 'HTTP Signature Scheme', which is distinct from the newer IETF RFC 9421 'HTTP Message Signatures' standard. If your application needs to comply with the latest IETF RFC, this library may not be suitable, and you should consider alternatives like `http-message-signatures`.
- deprecated The underlying 'HTTP Signature Scheme' (in some contexts, e.g., Cybersource's implementation) is being phased out in favor of alternative authentication methods like JSON Web Tokens (JWT) for enhanced features like message-level encryption. While this specific library is not directly deprecated, the broader adoption of HTTP signature schemes might decline.
Install
-
npm install http-signature -
yarn add http-signature -
pnpm add http-signature
Imports
- httpSignature
const httpSignature = require('http-signature').default;import httpSignature from 'http-signature'
- sign
import { sign } from 'http-signature';const httpSignature = require('http-signature'); httpSignature.sign(req, options); - parseRequest
import { parseRequest } from 'http-signature';const httpSignature = require('http-signature'); httpSignature.parseRequest(req);
Quickstart
const fs = require('fs');
const https = require('https');
const httpSignature = require('http-signature');
// In a real application, manage keys securely, e.g., via environment variables or a KMS.
// For this example, create dummy key.pem and cert.pem files.
// echo '-----BEGIN RSA PRIVATE KEY-----
// ...your-private-key...
// -----END RSA PRIVATE KEY-----' > key.pem
// echo '-----BEGIN CERTIFICATE-----
// ...your-certificate...
// -----END CERTIFICATE-----' > cert.pem
const key = fs.readFileSync('./key.pem', 'ascii');
const options = {
host: 'localhost',
port: 8443,
path: '/',
method: 'GET',
headers: {}
};
const req = https.request(options, function(res) {
console.log('Client received status code:', res.statusCode);
res.on('data', (d) => process.stdout.write(d));
res.on('end', () => console.log('\nClient request complete.'));
});
httpSignature.sign(req, {
key: key,
keyId: 'client-key-id',
keyPassphrase: process.env.KEY_PASSPHRASE ?? '' // Optional, if key is encrypted
});
req.end();
// --- Server-side (for testing the client) ---
const serverOptions = {
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem')
};
https.createServer(serverOptions, function (serverReq, serverRes) {
let responseCode = 200;
try {
const parsed = httpSignature.parseRequest(serverReq);
// For a real server, 'pub' would come from a trusted source based on parsed.keyId
const pub = fs.readFileSync('./cert.pem', 'ascii'); // Using the same cert for simplicity
if (!httpSignature.verifySignature(parsed, pub)) {
responseCode = 401; // Unauthorized
console.error('Server: Signature verification failed!');
} else {
console.log('Server: Signature verified successfully for keyId:', parsed.keyId);
}
} catch (e) {
responseCode = 500;
console.error('Server error processing signature:', e.message);
}
serverRes.writeHead(responseCode, { 'Content-Type': 'text/plain' });
serverRes.end(responseCode === 200 ? 'Hello from signed server!' : 'Authentication failed.');
}).listen(8443, () => console.log('Server listening on port 8443...'));