What Can Go Wrong
Write a Finding
“The
This lesson gives a finding its shape, works one example in full, and then has you complete two more. In the next lesson they go into SECURITY.md, the file in your project that
Four parts
A finding answers four questions.
Exposed. What does an attacker get to read or change? Name the asset.
Attack. How do they reach it, and what must they already have? Name the attacker.
Fix. What change would close it?
Proof. What would show that it is closed? The strongest proof is a test that performs the attack and fails today.
The first two parts come straight from the threat model of the last lesson: an asset, and an attacker with a certain reach. The last two make the finding useful. A fix says what to do. A proof says how you will know.
A worked finding
Here are the tokens your server accepts, as sample_state() builds them:
"tokens": {
"tok-ana-writes-7Qx2": {"user": "ana", "can_write": True},
"tok-ben-reads-K9pz": {"user": "ben", "can_write": False},
},
Take it through the four parts.
Exposed: the power to add, change, and delete books as ana.
Attack: the writing token is a fixed
in the source. Anyone who reads the file, or a backup of the state, has it. It never stops working. Fix: a token the server makes itself, that only the server could have made, and that expires.
Proof: a test that sends the old string
tok-ana-writes-7Qx2with a write and expects 401.
Each part is one or two sentences, and each names something specific. This is finding F6 in the course’s numbering, and Chapter 5 closes it. You do not need to fix it now. Writing it down is the work of this chapter.
Which statement is a finding you could fix and test?
Your turn, with half filled in
Now the session store. These are the two places in your server that deal with how long a session lasts:
def start_session(state, user):
"""Store a new session for user under a random id and return the id."""
session_id = secrets.token_urlsafe(16)
state["sessions"][session_id] = {"user": user}
return session_id
and, in session_cookie(), the line cookie["session"]["max-age"] = max_age, where max_age is 3600 unless the caller says otherwise.
So the
It prints True. Nothing in the server reads a clock, so the entry stays until someone logs out with that id. If nobody does, it stays for as long as the server runs. Max-Age is a request to the client. A client that kept a copy of the id, or an attacker who copied it once, can go on sending it, and the server still accepts it.
Two parts of the finding are given:
Exposed: a logged-in session, for as long as the server runs.
Attack: a session id that was ever copied keeps working.
Max-Ageasks the client to forget, and the server never does.
The server never ends a session by time. Which pair completes the finding?
Your turn
The last one is the cookie itself. This is the whole
def session_cookie(session_id, *, max_age=SESSION_AGE):
"""Return the Set-Cookie value that hands session_id to the client."""
cookie = http.cookies.SimpleCookie()
cookie["session"] = session_id
cookie["session"]["httponly"] = True
cookie["session"]["path"] = "/"
cookie["session"]["max-age"] = max_age
return cookie["session"].OutputString()
For the id abc it returns session=abc; HttpOnly; Max-Age=3600; Path=/.
A cookie can say two more things, and this one says neither. Secure means: send me over HTTPS only. SameSite means: do not send me with requests that start on another website. HTTP with Python named both, and you only need to recognize them today. Chapter 4 teaches both, and Chapter 8 shows what goes wrong without the second.
Here are four sentences from a finding about this cookie. Which one is its Attack?
The two drafts
Here are both findings as rows, in the layout SECURITY.md uses. The file calls the proof Test, because in this course a proof is always a test. Neither finding has one yet.
| # | Exposed | Attack | Fix | Test |
|---|---|---|---|---|
| F3 | A logged-in session, for as long as the server runs | A session id that was ever copied keeps working: Max-Age asks the client to forget, and the server never does | Record when a session started and was last used, and refuse and remove old ones | none yet |
| F4 | The session cookie, wherever a browser is willing to send it | The cookie carries HttpOnly, Max-Age, and Path only, so nothing tells a browser to keep it to HTTPS or to requests from this site | Set Secure and SameSite on purpose | none yet |
In the next lesson you copy these rows, or your own wording of them, into SECURITY.md.
Both rows end with “none yet”, and that is the weak spot of a finding on paper: it is a claim. The fourth part is what turns a claim into something a computer can check. The next lesson writes the first one, for a finding you have not seen yet.