The password reset
worked like everyone's.
Click forgot,
get an email,
follow a link
with a token on the end.
Eight hex characters.
Someone had written it years ago
in one line.
random.getrandbits of thirty two,
formatted as hex.
It looked random.
Every token was different.
Nobody could guess one
by looking at the others.
Nobody human.
Python's random module
is a Mersenne Twister.
It is a very good generator
for simulations,
for shuffling,
for tests.
It was never meant
to keep a secret.
Its entire future
is decided by its internal state,
six hundred and twenty four numbers
of thirty two bits.
And every output it gives you
is one of those numbers,
lightly scrambled
in a way that can be undone.
So if you see
six hundred and twenty four outputs in a row,
you can rebuild the state.
Then you are the generator.
I tried it.
A server seeded from the operating system,
six hundred and twenty four tokens read off it,
each one unscrambled,
loaded into a fresh copy.
The next three tokens
from both of them
matched,
character for character.
Now picture the attacker.
He makes an account.
He asks for a reset
six hundred and twenty four times,
which your system allowed,
because it was his own account.
Then he asks for a reset
on someone else's.
The email goes to them.
The token
he already knows.
The Python documentation
says it plainly,
near the top of the page.
Do not use this module
for security purposes.
Use secrets.
secrets.token_urlsafe
reads from the operating system's
cryptographic source,
and seeing a million of its outputs
tells you nothing
about the next one.
So search your code
for random,
and read every place it is used
for a token,
a password,
an invite code,
a session.
Make tokens long,
single use,
and short lived.
Store only their hash.
And rate limit the reset form,
because even a good token
should not be
something you can ask for
six hundred times an hour.
Random enough to look at
is not
random enough to bet on.
– Serguey Asael Shinder
Top comments (0)