The admin tools read every order from GET /admin/orders. They prove who they are with a secret API key in the X-Api-Key header. In the starting code the route checks nothing, so anyone gets every order.
The quick fix, key == API_KEY, has a hidden hole. == compares letter by letter and stops at the first wrong one, so a guess with a right first letter takes a few nanoseconds longer to fail. An attacker who times thousands of guesses learns the key one letter at a time, and then reads every order.
The fix: hmac.compare_digest(a, b) gives the same True or False, but it always goes through every byte, so its time gives nothing away. It compares bytes, so turn each text into bytes with .encode():
import hmac
hmac.compare_digest("pear".encode(), "pezz".encode()) # False, in the same time as for "zzzz"
hmac.compare_digest("pear".encode(), "pear".encode()) # True
Keep the key out of the log. A wrong key deserves a warning, but log who tried, never the key: a wrong key is often the real one with a typo, and everyone who reads the log would see it. A webhook route, for example, could log logging.warning("Bad signature from %s", request.remote_addr).
The check sets the real key to adm-7c41e9a2b8d05f3e, reads the log at /_test/log, and writes a line there if API_KEY is compared with ==:
GET /admin/orders X-Api-Key: adm-7c41e9a2b8d05f3e → 200 [{"id": 1, "status": "paid"}, … 7 orders]
GET /admin/orders (no key) → 401 {"error": "Wrong API key"}
GET /admin/orders X-Api-Key: adm-7c41 → 401 {"error": "Wrong API key"}
GET /admin/orders X-Api-Key: adm-7c41e9a2b8d05f3f → 401 {"error": "Wrong API key"}
GET /_test/log → 200 ["Wrong API key from 127.0.0.1", … 3 times]
Your task: in admin_orders, read the X-Api-Key header into key, with "" when it is missing. Compare it with API_KEY using hmac.compare_digest. When they differ, log a warning with the caller's address and answer 401 with {"error": "Wrong API key"}.
Assignment
Follow the Coding Tutorial to check the API key with hmac.compare_digest and log failed tries without the key.
Hint
Put not in front of hmac.compare_digest(key.encode(), API_KEY.encode()), so the if runs when the keys differ. The warning goes above the return, with %s and request.remote_addr only.
Free plan: 30 tutor messages. PRO is unlimited.
