I just happened across your code whilst looking for a definition of x-hub-signature header and how it should be parsed, and I noticed that you've a timing attack in your code.
You tried to address this through #7 but accidentally didn't correctly address it.
verify (expectedSignature, requestBody) {
const expected = this.#encoder.encode(expectedSignature)
const actual = this.#encoder.encode(this.sign(requestBody))
if (expected.length !== actual.length) {
return false
}
return crypto.timingSafeEqual(expected, actual)
}
By bailing out early if expected.length !== actual.length you create a timing attack. The correct implementation would be:
verify (expectedSignature, requestBody) {
const expected = this.#encoder.encode(expectedSignature)
const actual = this.#encoder.encode(this.sign(requestBody))
return crypto.timingSafeEqual(expected, actual)
}
This ensures that a timing safe comparison is always used, meaning that mismatching string lengths for signatures and mismatching signatures would happen in the exact same time expense.
I'm not a user of your module, so can't manage a PR for you.
I just happened across your code whilst looking for a definition of
x-hub-signatureheader and how it should be parsed, and I noticed that you've a timing attack in your code.You tried to address this through #7 but accidentally didn't correctly address it.
By bailing out early if
expected.length !== actual.lengthyou create a timing attack. The correct implementation would be:This ensures that a timing safe comparison is always used, meaning that mismatching string lengths for signatures and mismatching signatures would happen in the exact same time expense.
I'm not a user of your module, so can't manage a PR for you.