by Serguey Shinder
I found the flaw by accident, the way I find most things, by being lazy. I had forgotten my password on an internal tool and clicked the reset link. The email arrived, I set a new password, and something about how smoothly it went made me curious. So I tried resetting the password for a colleague's account, just to see what would stop me.
Nothing stopped me. The reset flow asked for an email address, sent a link, and if you could read that inbox you were in. That much is normal. What was not normal was that the link never expired and could be used more than once. A reset link sent months ago, sitting quietly in someone's archived mail, was still a live key to their account.
I sat with that for a moment before reporting it, because I wanted to understand how it had happened. Nobody had decided that reset links should live forever. Someone had built the happy path, watched it work, and moved on. Expiry is one of those things you only miss when you deliberately go looking for the unhappy story, and unhappy stories rarely make it into the acceptance criteria.
That is the lesson I keep relearning about security. The dangerous gaps are almost never dramatic. They are not clever exploits or exotic attacks. They are ordinary features that were tested only in the way they were meant to be used. The link works, so the ticket closes, and the question of how long the link should keep working never gets asked out loud.
I have started treating every credential-shaped thing in a system as a small promise about time. A session, a token, a reset link, an API key. Each one is saying "trust the holder of this until some moment." If I cannot say what that moment is, the honest answer is that the promise is forever, and forever is almost always wrong.
The fix in this case was three lines and an afternoon. Expire the link after an hour, invalidate it once used. The hard part was not the code. The hard part was the habit of asking, for every convenience I build, what happens when it is used at the worst possible time by the worst possible person.
I no longer feel clever for finding that bug. Mostly I feel humbled by how easy it was, and by how many others like it I have surely shipped without noticing. Security, for me, has become less about defending against attackers and more about refusing to leave promises open-ended.
– Serguey Asael Shinder
Leave a comment