Function std.digest.secureEqual
Securely compares two digest representations while protecting against timing
attacks. Do not use ==
to compare digest representations.
bool secureEqual(R1, R2)
(
R1 r1,
R2 r2
)
if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 && (isIntegral!(ElementEncodingType!R1) || isSomeChar!(ElementEncodingType!R1)) && !is(CommonType!(ElementEncodingType!R1, ElementEncodingType!R2) == void));
The attack happens as follows:
- An attacker wants to send harmful data to your server, which requires a integrity HMAC SHA1 token signed with a secret.
- The length of the token is known to be 40 characters long due to its format,
so the attacker first sends
"0000000000000000000000000000000000000000"
, then"1000000000000000000000000000000000000000"
, and so on. - The given HMAC token is compared with the expected token using the
==
string comparison, which returnsfalse
as soon as the first wrong element is found. If a wrong element is found, then a rejection is sent back to the sender. - Eventually, the attacker is able to determine the first character in the correct token because the sever takes slightly longer to return a rejection. This is due to the comparison moving on to second item in the two arrays, seeing they are different, and then sending the rejection.
- It may seem like too small of a difference in time for the attacker to notice, but security researchers have shown that differences as small as 20µs can be reliably distinguished even with network inconsistencies.
- Repeat the process for each character until the attacker has the whole correct token and the server accepts the harmful data. This can be done in a week with the attacker pacing the attack to 10 requests per second with only one client.
This function defends against this attack by always comparing every single
item in the array if the two arrays are the same length. Therefore, this
function is always Ο(n
) for ranges of the same length.
This attack can also be mitigated via rate limiting and banning IPs which have too many rejected requests. However, this does not completely solve the problem, as the attacker could be in control of a bot net. To fully defend against the timing attack, rate limiting, banning IPs, and using this function should be used together.
Parameters
Name | Description |
---|---|
r1 | A digest representation |
r2 | A digest representation |
Returns
true
if both representations are equal, false
otherwise
See Also
Example
import std .digest .hmac : hmac;
import std .digest .sha : SHA1;
import std .string : representation;
// a typical HMAC data integrity verification
auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV" .representation;
auto data = "data" .representation;
auto hex1 = data .hmac!SHA1(secret) .toHexString;
auto hex2 = data .hmac!SHA1(secret) .toHexString;
auto hex3 = "data1" .representation .hmac!SHA1(secret) .toHexString;
assert( secureEqual(hex1[], hex2[]));
assert(!secureEqual(hex1[], hex3[]));
Authors
Johannes Pfau