Start learning free

LESSON 6 OF 20 · SECURITY: TOKENS AND SESSIONS

Compare Secrets Safely in Python

Practice

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.


STEP 1 OF 4
    Ctrl + Enter to check

    Secx Timing Compare in Python

    Keep going with Python1,600+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free