mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-24 03:14:03 +03:00
Improved HTTPSignature library
ref https://linear.app/tryghost/issue/MOM-73 We've made it easier to use by adding defaults for required header, as well as adding support for signing POST requests.
This commit is contained in:
parent
e98f505ae3
commit
f31330a228
@ -1,4 +1,5 @@
|
||||
import assert from 'assert';
|
||||
import crypto from 'crypto';
|
||||
import {HTTPSignature} from './http-signature.service';
|
||||
|
||||
describe('HTTPSignature', function () {
|
||||
@ -50,4 +51,50 @@ describe('HTTPSignature', function () {
|
||||
assert.equal(actual, expected, 'The signature should have been validated');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#sign', function () {
|
||||
it('Can sign a request that does not have explicit Date or Host headers', async function () {
|
||||
const keypair = crypto.generateKeyPairSync('rsa', {
|
||||
modulusLength: 512
|
||||
});
|
||||
const request = new Request('https://example.com:2368/blah');
|
||||
const signed = await HTTPSignature.sign(request, new URL('https://keyid.com'), keypair.privateKey);
|
||||
|
||||
assert.ok(signed);
|
||||
});
|
||||
|
||||
it('Can sign a post request which is valid', async function () {
|
||||
const keypair = crypto.generateKeyPairSync('rsa', {
|
||||
modulusLength: 512
|
||||
});
|
||||
class MockHTTPSignature extends HTTPSignature {
|
||||
protected static async getPublicKey() {
|
||||
return keypair.publicKey;
|
||||
}
|
||||
}
|
||||
|
||||
const request = new Request('https://example.com:2368/blah', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/ld+json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: 'https://mastodon.social/users/testingshtuff',
|
||||
type: 'Accept',
|
||||
actor: 'https://mastodon.social/users/testingshtuff',
|
||||
object: 'https://mastodon.social/79f89120-fd13-43e8-aa6d-3bd03652cfad'
|
||||
})
|
||||
});
|
||||
|
||||
const signed = await MockHTTPSignature.sign(request, new URL('https://keyid.com'), keypair.privateKey);
|
||||
|
||||
assert.ok(signed);
|
||||
|
||||
const url = new URL(signed.url);
|
||||
const body = Buffer.from(await signed.text());
|
||||
const result = await MockHTTPSignature.validate(signed.method, url.pathname, signed.headers, body);
|
||||
|
||||
assert.equal(result, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -106,7 +106,9 @@ export class HTTPSignature {
|
||||
.update(requestBody)
|
||||
.digest('base64');
|
||||
|
||||
const remoteDigest = requestHeaders.get('digest')?.split('SHA-256=')[1];
|
||||
const parts = requestHeaders.get('digest')?.split('=');
|
||||
parts?.shift();
|
||||
const remoteDigest = parts?.join('=');
|
||||
|
||||
return digest === remoteDigest;
|
||||
}
|
||||
@ -168,9 +170,24 @@ export class HTTPSignature {
|
||||
algorithm: 'rsa-sha256'
|
||||
};
|
||||
const url = new URL(request.url);
|
||||
const requestHeaders = new Headers(request.headers);
|
||||
if (!requestHeaders.has('host')) {
|
||||
requestHeaders.set('host', url.host);
|
||||
}
|
||||
if (!requestHeaders.has('date')) {
|
||||
requestHeaders.set('date', (new Date()).toUTCString());
|
||||
}
|
||||
if (request.method.toLowerCase() === 'post') {
|
||||
const digest = crypto
|
||||
.createHash(signatureData.algorithm)
|
||||
.update(Buffer.from(await request.clone().text(), 'utf8'))
|
||||
.digest('base64');
|
||||
|
||||
requestHeaders.set('digest', `${signatureData.algorithm}=${digest}`);
|
||||
}
|
||||
const signatureString = this.generateSignatureString(
|
||||
signatureData,
|
||||
request.headers,
|
||||
requestHeaders,
|
||||
request.method,
|
||||
url.pathname
|
||||
);
|
||||
@ -180,14 +197,13 @@ export class HTTPSignature {
|
||||
.sign(privateKey)
|
||||
.toString('base64');
|
||||
|
||||
const newHeaders = new Headers(request.headers);
|
||||
newHeaders.set(
|
||||
requestHeaders.set(
|
||||
'Signature',
|
||||
`keyId="${keyId}",headers="${headers.join(' ')}",signature="${signature}",algorithm="${signatureData.algorithm}"`
|
||||
);
|
||||
|
||||
return new Request(request, {
|
||||
headers: newHeaders
|
||||
headers: requestHeaders
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user