3

This question already has an answer here:

Vision

I envision a login/signup-system, where the user enters his/her email, and receives an email with a link to sign in. The user clicks the link, and is automatically signed in (confirming the email address in the process).

This would be a form of passwordless authentication.

Problem

The problem I see with this approach, is that a link will send a GET request - but this GET request will change the state of the user session (activating it).

GET requests should (according to the HTTP-standard) never have side-effects. This is something browsers assume as well, which means they might prefetch GET requests to optimize performance/user experience.

Let's say you are using Gmail in the browser. Could it then happen that the browser will prefetch the link in the email?

It would be a huge security problem if simply opening the email was enough to be signed in to the site.

What I have considered

Using JavaScript on the landing page to send a POST request

My gut feeling tells me that, even though HTML/CSS/JavaScript from a link could be prefetched, none of this code (especially the JavaScript) will be run before the browser explicitly opens the link.

This should mean that if the user session is somehow activated using JavaScript (by sending a POST request to the backend), it should be safe to use a GET request to get to this page (as a link in the email).

Am I right by assuming this?

Is it bad practice to do it this way?

Using a HTML form in the email body

Another option would be to place a form in the email, which would allow for a POST request from the email directly.

It seems, however - that several email clients will block form submissions from within emails. Email clients that that allow form submissions tend to warn the user that the email is likely malicious. It seems this is not really a good solution.

marked as duplicate by Daisetsu, forest, Matthew, Jeroen, ThoriumBR Nov 27 '18 at 14:59

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 1
    The killer argument with the side effects aside, you must always allow for the case of a prankster entering a "victim"'s email address and that victim first wanting to take a look at what this strange and unexpected mail is about and - kazam! - they have signed a binding contract – Hagen von Eitzen Nov 23 '18 at 6:20
  • "several email clients will block form submissions from within emails" Heck, several clients (or their users) block HTML in email by default. – TripeHound Nov 23 '18 at 9:45
  • Not just the user's/victim's browser or email client may send GET requests. An email server with anti-spam, -virus, or -phishing systems might visit links. – Future Security Nov 23 '18 at 20:12
  • Don’t forget that email is not private, and you’re essentially allowing anyone who can see the email while in transit to impersonate that user. – Steve Nov 24 '18 at 16:29
5

So, first of all, links from email change state all the time. While non-idempotent GETs are a violation of the de jure standard, the de facto standard is that any browser or email system which doesn't allow users to make GET requests for things like email verification, password reset, and the various other services that use email-based passwordless login (such as Slack) is a browser or mail client that everybody will consider broken. Another common example is the "logout" link (which on many sites is just a GET to https://example.com/logout, without even any CSRF protection); while this is potentially a security risk, in practice it is extremely widely used and difficult to exploit as anything other than a prank.

WITH THAT SAID the standard way to handle this is safely is for the link in the email to contain a (strong random unique) token that takes the user to a landing page, and the landing page says, basically, "hey, you appear to be [username], click HERE to log in" along with a "Not [username]? Click HERE instead" link in smaller text underneath. The emailed link expires after either link on the landing page is clicked, or after a certain period of time has elapsed (usually no more than an hour), whichever comes sooner. Loading the landing page does not immediately expire the link, though, and the "links" on the landing page can safely be buttons on a HTML form that POSTs to your service, or JavaScript triggers. The "links" on the landing page should ideally have CSRF protection.

This has a few advantages:

  • No problem if the landing page is visited "ahead of time".
  • No way for an attacker to trick somebody into signing on by accident.
  • No state-changing GET requests needed (though the JS event could just trigger a navigation to an unpredictable "complete this login" page, if you don't care about that part of the spec).
  • Allows the user to cancel the login if they clicked the link by accident, or have multiple accounts and used the wrong one by accident.

It also has some disadvantages:

  • The user has to click an extra time. You can mitigate this by have a timeout that automatically logs the user in after a few seconds, if they don't click anything or close the page.
  • The token sent in the URL can potentially remain active for a period after being visited. This is a risk because URLs are sometimes logged in places where untrusted users might see, or are otherwise exposable. I don't think there's any mitigation for this (aside from having the lifetime be very short) other than making the link itself single-use, which both breaks "GETs should be idempotent" (not actually a big deal, as explained above) and "pre-fetching this URL should not break stuff" (see above).

Incidentally, webmail services and web-based chat systems and so on pretty much always rewrite links in received messages (try copying the actual link out of a Gmail message, for example; the status bar text is a lie). When you click the link, you get bounced through a redirect page in another tab, rather than it opening the destination in that other tab directly. This prevents things like the linked-to page using the window.opener property to mess with the webmail/chat page.

Not the answer you're looking for? Browse other questions tagged or ask your own question.