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). This is how we solved it at paperhive.org.
This has been suggested numerous time since I published. I had not previously considered this solution and I think it's a fine solution for people to make if they know the tradeoffs.
It's worth noting there are a number of reasons this JavaScript could possibly not execute beyond people who have JS turned off. I've seen a number of sites fail to execute JavaScript when an Ad Blocker is run, for instance.
In this case, there are a number of server side fixes available that wouldn't require any JavaScript. They're not terribly complicated and will always work. For that reason, I'm still comfortable with the server side fix, but think the JS fix is a decent alternative.
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.
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.
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.
Using an addon like NoScript it's possible to selectively enable javascript per domain. When a website doesn't work without js I am forced to decide whether it's worth enabling js for this site. Very often I decide it's not worth it and I never use that site again.
That would be meaningful if we were talking about some optional UX feature, but this is security. Does having a non-representative browser config mean we don't deserve security? I think not. Security has to work without JS.
And I hate it when a site is loading JavaScript from a large domain, e.g. cloudflare.com; generally I just close my browser window and view something else.
No way am I going to allow JavaScript from every single site using CloudFlare to run all at once.
When your browser runs JavaScript, it downloading and automatically executing untrusted, unsigned, ephemeral code. Even if the site is over SSL, only the _party_ is validated---the resources themselves are not signed.
If your browser instead presented the JavaScript as a program itself, and listed the programs it executed, and from what sources, users would have a wholly different perspective. JavaScript has the illusion of remote execution; most users don't think of it as executing programs on their computer.
Addons like NoScript are essential security precautions that mitigate a host of attacks. Unfortunately, even security-essential software like the Tor Browser Bundle leaves JS enabled by default because it'd "break" the web.
There's other reasons---as a free software user and activist, I won't run non-free JavaScript programs.
Why should we trust any website and execute their JS code on our machine? What about privacy, if they decide they can track us and sell the information to whoever they want? And even if they're "legit", what about the 3rd parties they might trust wrongly?
I come to a website to read it, not for it (and who knows what else) to execute code on my machine, no matter how deep into the sandbox it is. If I want to watch the video and allow it to use my data, I will explicitly allow it.
For users without JavaScript it's reasonable to include a plain form and send token in plain text as well, so user can copy-paste it into a form and reset his password.
You are assuming that 3rd party javascript is not taking the whole URL including the hash part and sending it for analytics. I have seen at least one analytics company do that.
Right. The scenario I'm considering is not a hostile attacker but simple an overzealous analytics script which also tracked hash strings. Heck, since some sites used to do AJAX navigation that way it wouldn't surprise me if some analytics services were configured to track hash strings.
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).
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.
Before generating the PW reset link, someone might try to login first. So they'd enter bob@example.com into the login form and then when that failed, it's not uncommon to redirect with error-msg-in-session to /login?email=bob@example.com . So you'd leak the email first.
Given how many (more than 90% imo) reset links are something like /reset?email=test@test.com&token=0123456789, that's not quite an issue as it may seem...
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.
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.
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.
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".
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.
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.
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].
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.
The basic observation is that the reset url is sent as the referrer to any 3rd party content that gets loaded on the page. So if you have a facebook link, ads, analytics, cdn, or other random content on the page, they get to see what the reset url is.
This is a valid concern, though it's worth noting that you shouldn't be including 3rd party content you don't trust on any potentially sensitive page in the first place. This includes not only password pages, but potentially ANY logged in page on the site.
Otherwise, you should consider the 3rd party content trusted and move on.
The thing about https only applies when an https page is loading assets from a non-http address. But if your site uses https and so does your CDN, then the referer headers are still sent.
If you're site uses a password reset token email as the article describes, and your Reset Password page loads 3rd party scripts or css, those 3rd parties (and any servers en route to them) may be able to see the password reset token as part of the HTTP Referer header.
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.
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.
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?
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.
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.
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.
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.
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 :(
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.
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.
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.
That should definitely happen anyway but, as the article points out, that leaves a window of time between the user clicking the link in their email and them completing the form. It might be a very brief window, but it's still exploitable (and it won't always be brief - consider a user clicking the link in their email, leaving their desk for 5 minutes or going to make a cup of tea...)
Not to mention the users that will request a reset but then remember, or forget to follow up with the reset (which I myself have been guilty of in the past).
How to implement this if the token is not stored in database at all (eg. JWT)? As far as I know, enforcing one-time-use only requires storing a bit in backend.
Hence one of the proposed solutions at the end of the article is to generate a new token when the link is used and put the new token into the form.
But some sort of token needs to be used even after clicking the email link because the "enter a new password" form needs it posted as well (to prevent people from using that form willy-nilly on any account).
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?
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.
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).
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?
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.
Yet plenty of successful companies do the equivalent with SMS codes during registration or account verification. It's not unreasonable that during a password reset you have to put in a few characters of extra work. If you're too lazy to type a few extra characters the account clearly isn't all that valuable to you.
I think people are looking to closely at the first degree attack -- a trusted partner is pwned. If this is the case, there's far more interesting things an attacker could do. It's not too hard to envision a scenario where an attacker does not have access to embeded assets, but does have access to logs.
1. You include a script such a TypeKit. The typekit deliverable itself is not owned, but bad actors have access to typekit.com logs.
2. You use a smaller third party add on service that itself uses a logging service such as PaperTrail. PaperTrail is hacked, providing attackers access to logs.
3. You reference no external assets, but your site contains external links in the footer. Users click the navigation links rather than completing the form. You have leaked the token to whatever site that is. You are at the mercy of their log storage. YES, this does actually happen. User's click crazy things.
As I mention in the article and in other comments here: this is not likely to be exploited. Fixes, however, are not too difficult. Even adding the not-quiet-fully-supported `meta` tag to your head is a good start.
If I read this article today, I'd think, "That's interesting. let me make a note to check that out." It's not a hair-on-fire security situation, but it's not "not a problem at all" either.
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.