Closes the post-recovery re-attack window. Previously cookies were authenticated purely cryptographically — once a hijacker received a signed cookie for student_id=X, that cookie remained valid forever (until QUIZ_SECRET_KEY rotated), even after admin clear-student + legit re-claim issued a fresh cookie_id for X. Now /me, /event, and /ws/student all check that the cookie's cookie_id matches participants.cookie_id for the (sid, student_id). Mismatch -> 401 + Set-Cookie clearing for HTTP, ws.close(4001) for WS. The legit re-claim wins because admin clear_student deletes the row and the next join inserts the new student's cookie_id; the hijacker's cookie now fails the DB check on every subsequent request. Test: tests/test_anti_cheat.py::test_post_recovery_old_cookie_is_dead covers the full hijack -> clear -> re-claim -> hijacker-locked-out sequence end to end.
9.2 KiB
9.2 KiB