
Is Your Site Leaking Password Reset Links? - andrenarchy
https://robots.thoughtbot.com/is-your-site-leaking-password-reset-links
======
andrenarchy
If you use javascript to extract the token from the URL then you can simply
pass it via the hash ("fragment") part of the URL. The hash portion is only
interpreted by the user agent and never sent to a server (see
[https://tools.ietf.org/html/rfc3986#section-3.5](https://tools.ietf.org/html/rfc3986#section-3.5)).
This is how we solved it at paperhive.org.

~~~
oneeyedpigeon
Obviously, the drawback is that you've introduced a javascript dependency to a
core function which definitely doesn't require it. Having said that, I notice
that paperhive.org renders an entirely blank page if javascript is
unavailable, so I guess the password reset is the least of your concerns in
that scenario.

~~~
angry-hacker
And what percentage of the users have javascript disabled? Objectively you
have bigger concerns when you run a site than 10 people who have js disabled.

~~~
oneeyedpigeon
Definitely more than zero. It's not just a case of javascript being disabled,
either - there are many other reasons why it might be "unavailable", which is
why I used that word.

Of course I'm not suggesting you're not allowed to use _any_ javascript on
your site, or even that you should only use it when it's strictly necessary,
but if it's entirely unnecessary and you don't engage in best practise by, for
example, using progressive enhancement, then that's something that could be
improved.

------
tvelichkov
If you steal someones else password-reset link, change the password, then at
the end of the day, won't you end up with a password, but missing
email/username in order to log in? I mean the reset password link shouldn't
reveal any other credentials about the account. (I know at some sites after
reseting a password you may end up automatically logged in, but i think this
is a bad practice).

~~~
derekprior
Depends on how the reset behaves. Some resets log you in immediately after
providing a new password. Some require you to log in after resetting the
password.

I feel like I've seen more of the former than the latter.

------
jpalomaki
One option is to avoid putting the token to the link and at least provide user
a simple way of copy-pasting the token to the password reset form.

Sometimes this is actually something I as user want to have, since it might be
that I'm receiving the email on device A, but want to reset the password on
device B.

And please keep the password reset tokens sane. If you are not encoding some
data into the token, you don't really need that 80 character random string for
security.

~~~
derekprior
I'd certainly consider this for some applications. It depends on the value of
what you are protecting. For some sites and users, the error rate introduced
by this method would be unacceptable.

Tradeoffs...

------
moloch
This is bad but not horrible, especially in the example given leaking the
reset token to Cloudfront. The application is loading JavaScript from the
Cloudfront origin, so that origin by definition could already read the tokens
by modifying the JavaScript (assuming no SRI). The request is sent over
SSL/TLS so the token cannot be viewed by a MITM, and referers aren't sent
across HTTP<->HTTPS transitions.

Again this is far from ideal, but also not readily exploitable by attackers
that couldn't already access the data.

~~~
derekprior
Author here. I completely agree that it's unlikely to be exploited but also
think fixes are mostly simple enough that it should be addressed.

------
darkhorn
What I do is I start a session on password reset page. I send an email with
the reset link. User visits the link. The web page checks wheter the requester
is same with the session identifier. Only then the user has a right to create
a new password. So, in other words, if the user tries to visit the link with a
different browser a warning says "use your browser that you used to reset your
password".

~~~
initram
So if I'm in a private session and clicking the link in my email opens a new
page in a different session, then I can't reset my password? That's lame.

I've even had a situation where I was on my desktop machine and clicked a
reset link on the web site. I realized I didn't have my email set up on that
machine yet, so I went to my phone and did it from there. In your scenario,
this wouldn't work. That seems problematic.

~~~
darkhorn
If you are on a private session you can copy the link to a new private tab and
it will work.

My web site's visitors are only from my universty. Only to those who have METU
email addresses. It is easy to log in to a web mail from the browser. Password
reset is not a something done on daily bases. It is okay for my situation. Not
very user friendly but it is a bit more secure.

In fact the idea come from this; what if a student fills registration form and
sends the validation email to his teavher. And the teachet, without reading
click, in other words validates the registration process mistekenly. Now, I
have a criminal case (I shouldn't allow Professor Naughty Elizabeth to be
registeted for example) against me! I wanted to protect my ass. And I used it
too in password resets.

------
zaroth
Referrer leaks are such a foot-shoot. There should have been a piece of the
URL which never touched the Referrer but also _was_ submitted to the server
from the get-go. But obviously an easy edge case to miss in 1991 - 1996 [1]
[2].

But this is sad: [http://caniuse.com/#feat=referrer-
policy](http://caniuse.com/#feat=referrer-policy)

[1] -
[http://www.w3.org/pub/WWW/Protocols/HTTP/AsImplemented.html](http://www.w3.org/pub/WWW/Protocols/HTTP/AsImplemented.html)
[2] -
[https://tools.ietf.org/html/rfc1945](https://tools.ietf.org/html/rfc1945)

------
kelvin0
I've read the article twice, and I'm not totally clear as to what the problem
is, and how it's being addressed (web n00b here). Also, this bit:

"Browsers will not send the Referer for resources fetched via HTTP from a
document loaded via HTTPS"

So using HTTPS resolves the issue? But breaks analytics?

Any further clarification would be nice, thanks!

~~~
msimpson
The article is simply pointing out that services which record referrers can
inadvertently store live password reset tokens if you're not careful.

For instance, someone places Google Analytics in the head of the default
layout for a given site. Now traffic to and from the password reset page,
which uses that layout, is being recorded. This means an attacker would only
need to gain access to that account, which is probably much less guarded, and
gather referrers containing password reset tokens. From there they could
quickly try the last few--which might still be active--and easily gain access
to one or more accounts within the site.

~~~
kelvin0
>>...would only need to gain access to that account, which is probably much
less guarded..

So "that account" you mention, is it the Google analytics account or the web
application (leaking urls) account? Why is it much less guarded?

~~~
msimpson
I mean the analytics account; and in my experience it is less guarded due to
clients handing out access as it's "just reporting" in their eyes.

------
eterm
For analytics this isn't a big problem.

Once a reset link has been clicked, it should be immediately invalidated.

So unless the server was able to respond to the link and provide the analytics
stuff but not somehow invalidate the token, I can't see how this is a problem.

Another related problem is that some third party mailers move all their links
via URL redirectors. In that case there's a chance the host application fails
and the link is left valid.

~~~
fahrradflucht
>Once a reset link has been clicked, it should be immediately invalidated.

I'm not sure about this... Couldn't this produce some unexpected reset
failures in cases of browsers preloading links in webmail clients?

~~~
mgbmtl
Drupal (and WordPress, if I recall correctly) invalidate immediately.
Considering reset links are sent in plain text by email, it's a good way to
test whether the link has been used by someone else.

Besides, it would be an odd security hazard if browsers/webmails preloaded
links in emails (malicious URLs in spam/scams).

edit: in Drupal, the reset link loads a page with a button that the user must
click on. This avoids issues with potential preload or anti-virus scans.

~~~
zaroth
What if they click the link but don't click the button? Do they need another
password reset token at that point? If not, is there still an attack window
there that needs to be plugged?

~~~
mgbmtl
The links usually expire after 24 hours (attack window), if unused.

Loading the page without clicking does not invalidate the link.

I agree this is not highly secure. It's basically one notch above sending
cleartext passwords by email (which many websites unfortunately still do).

------
Normal_gaussian
So the fragment solution requires JS, invalidate on request can be upset by
email scanning antivirus, manual token entry is inconvenient.

So my first thought is returning a page with no external requests (render on
the srrver) which isn't very dev friendly.

My other thought is returning a redirect to a page with a token derived from
the original token and the clients IP or some other fingerprinting
information.

~~~
kijin
Redirect is the cleanest solution.

Most frameworks provide some form of storage (whether server-side or client-
side) that is tied to a specific session, so you can use that to remember the
fact that the current session recently used a valid password-reset token for
user ID 123.

Or you could use a similar mechanism to put the token in a hidden <input>
after redirection, so that it gets submitted again when the user types in
their new password.

~~~
bahjoite
>Redirect is the cleanest solution.

Exactly. Submitting tokens via GET requests (as is necessary for an emailed
token) should be handled in the same way as POST (POST-redirect-GET): the
resource which validates the token should not be the one that presents the
"password reset" form.

------
r1ch
You can also use the meta referrer tag on modern browsers.

[https://moz.com/blog/meta-referrer-tag](https://moz.com/blog/meta-referrer-
tag)

~~~
derekprior
It's covered in the article, but this is not supported by IE11. That's pretty
modern...

------
zokier
Would putting the token to url fragment id resolve the issue? I suppose the
downside would be that then js is required on the reset page. But that is
fairly minor imho.

------
marichards
British regulators have highlighted this problem too.
[https://iconewsblog.wordpress.com/2015/09/16/does-your-
websi...](https://iconewsblog.wordpress.com/2015/09/16/does-your-website-have-
a-leak/) I wish someone would sue Mozilla, Google, Microsoft and Apple for
data protection violations for supporting referer

------
milankragujevic
Does anyone have a list of common vulnerabilities that you should check your
app against, maybe excluding the obvious ones like SQL Injection, XSS, etc...
? Because I can't keep track of all the vulnerabilities that exist in the
world :(

~~~
Kalium
[https://www.owasp.org/index.php/Top10#OWASP_Top_10_for_2013](https://www.owasp.org/index.php/Top10#OWASP_Top_10_for_2013)

This is a good one in specific. In general, be very careful of what
information you share with who. Information is dangerous, and it leaks all
over.

------
pacifika
The problem is that your password reset token shouldn't be reusable. The token
is only leaked after the person visited the reset page, which should
invalidate the token on load.

Without a valid token, the reset form shouldn't load. Problem solved.

~~~
derekprior
This requires non-idempotent get requests as you must invalidate the token on
get.

I did consider this approach for Clearance and intended to go with it, but was
discouraged from doing so after hearing reports that some enterprise email AV
does things like open some links in emails.

There is also the user experience concern that a click the link in my email,
do something else, then click the link again, having forgotten I already
clicked the link. Now I'd have to re-request again.

Also, this approach is impossible if you use HMAC tokens.

I don't think anyone who opts for this approach is wrong but like most things,
it's a tradeoff.

~~~
nchelluri
Thanks for the additional thought process here; just a minor question, what's
an HMAC token?

~~~
derekprior
[https://en.wikipedia.org/wiki/HMAC-based_One-
time_Password_A...](https://en.wikipedia.org/wiki/HMAC-based_One-
time_Password_Algorithm)

I might not be using the right term here, but the general idea is that you
create an encrypted token out of some data and verify that the data is
unchanged and still valid on the server. In that way you can provide a token
that ensures the user had access to the link you sent them, but you don't have
to store it in the database.

------
garglgarbl
I'm a bit of a web noob as well, so I have a related question: If I have an
https url with a token will the token only be sent through the https
connection or is it contained in any lookups or connection metadata or such?

~~~
briHass
Query parameters are encrypted if you are using HTTPS. The domain/host name
(e.g. news.ycombinator.com) obviously is not protected, since the DNS lookup
is required and the server (resolved IP) may host multiple sites. Seeing the
hostname plaintext in the request is required for the server to disambiguate.

So, for single-use tokens, you're probably OK for passing it in the URL (e.g.
myhost.test/resetpw?token=abcdef), but it is usually considered a bad idea to
use the URL for non-single-use secret info. Once it hits the server, the full
URL may be stored in log files unsecured or if you use SSL termination before
the server, it could be logged in other places as well. Additionally, the
user's browser itself may store your secret URL in the history.

~~~
derekprior
In my experience, password reset tokens are not single use. They are good for
both loading the form and submitting the form. They are not invalidated until
the form is submitted with the new password.

They are good for 1 password reset, not 1 page load. It's possible to make
them good for 1 page load, but most I've encountered are not due to the
tradeoffs that would involve (see other discussions).

------
zeveb
He should have also considered the option of not loading third-party assets
and analytics on password reset pages.

I'm grumpy enough to suggest not loading them period, but I'm a curmudgeon.

------
Lord_Zero
Can anyone check if [https://stormpath.com/](https://stormpath.com/) has this
issue?

------
jccc
Anyone here happen to know how Basecamp works? They seem to be comfortable
with emailed login links, no username or password.

------
agotterer
Why not have a URL that identifies the reset request uniquely but also
requires a code that is entered manually?

~~~
oneeyedpigeon
In my experience, that really confuses users. Some just do not understand or
read the email too quickly; they click the link, then they get stuck. If
everything just works as a result of clicking a link, users are satisfied.

BTW, why would the URL that identifies the reset request need to be unique if
the tokens have enough entropy?

~~~
agotterer
Going to need a whole lot of characters to prevent someone being malicious and
just resetting passwords randomly. Long characters are also more annoying to
type for the end user. If the link is unique you can keep the passcode a
little shorter and it requires two pieces of information to complete a reset.

------
homakov
There's absolutely no situation when it's a risky behavior. It is not a
problem at all.

~~~
HoyaSaxa
Just to illustrate a potential scenario:

You use a piece of open source software that has a javascript file which the
author has hosted on a CDN. In order to mitigate an attack where someone
changes the content of the javascript file you use a hash[1] to ensure the
file does not change. You think you are safe, but a malicious actor gets
access to the CDN and instead of changing the file, instead builds a quick
scripts that looks for password reset referrer patterns and races the human to
reset the password first. A computer is probably going to win that game that
majority of the time.

[1]
[https://github.com/twitter/secureheaders#hash](https://github.com/twitter/secureheaders#hash)

~~~
CGamesPlay
Or the attack just skips the secure hash check on the server... This example
doesn't seem to hold water either.

------
mmanulis
Why not pass the token as an HTTP header? e.g. X-RESET-TOKEN=foo and then read
that via JS

~~~
dom0
How exactly do you encode a HTTP header into an URL?

