In this example, the server code looks like this:
Incoming value is "X|Y" -> split between "X" and "Y"
Compute id = Un-Base64(X)
Compute h = SHA-1(secret_key + id)
If the hexadecimal value of h is not equal to Y, then deny access
id is "user|role" with "role" being either 0 or 1; so, if id ends with "|0",
grant administrator access.
secret_key
, of course, is unknown to the attacker. But the attacker can try to guess the length of that key, if only by trying different lengths; in that example, secret_key
has length 25 bytes.
SHA-1 suffers from the length extension attack (like all Merkle–Damgård hash functions). See this answer for detailed explanations; bottom-line is that by knowing the length of secret_key
, the attacker can compute a special "id" string like this:
id2 = aaaa|1somebytes|0
where somebytes
include the SHA-1 standard padding, and then the attacker can compute the appropriate hash value for that "id", starting with the known SHA-1 value (the one for aaaa|1
) and without knowing secret_key
(that's the point of the "length extension attack": computing h(m') where m' starts with an unknown m, but with h(m) known).
The server will believe the provided "id" string to be genuine, and grant administrator rights to a user named "aaaa|1somebytes".