Automatic Login Links
Scary, I know, but hear me out. Implemented correctly, an automatic login link can be just the ticket to appease those pesky, forgetful, real-world users.
Imagine a Rails application that sells fruit. The app has two kinds of users:
- A customer who comes to the site to buy fruit
- A seller who registers at the site to sell and ship fruit to customers
If you’re buying your fruit online, you’re probably pretty tech-savvy. Odds are you have accounts at a lot of sites and you do a decent job of remembering your passwords. And when memory fails, there’s the always-helpful “Forget your password?” link.
But what about the sellers? These guys are a bunch of farmers and not necessarily the most adept computer users. But every time a new order comes in, the seller gets an email prompting them to log in and ship the order.
Spoiler alert: The sellers never remember their passwords.
In this case, an automatic login link can go a long way to keep the sellers and the customers happy. However, with this approach, there are two important bases to cover in order to protect yourself from brute force attacks:
- The link must be secure (random)
- Access to the link must be temporary
In order to make the link temporary, we’ll attach each link to an order rather than to the seller. That way we can expire the link as soon as the order is shipped. Here’s the order model which automatically generates a unique login token when an order is placed and clears the login token when the order ships:
class Order < ActiveRecord::Base belongs\_to :seller belongs\_to :customer before\_create :set\_login\_token scope :authenticatable, where("orders.login\_token IS NOT NULL") def ship\_it! \# TODO Send the fruit off to the customer update\_attribute(:login\_token, nil) end private def set\_login\_token self.login\_token = ActiveSupport::SecureRandom.urlsafe\_base64 end end
Now we need a route for the automatic login link:
ApplesAndOranges::Application.routes.draw do resources :orders, :only => \[:index, :show, :destroy\] do member do get :authenticate post :ship end end end
And in the orders controller, we can automatically log the seller in when she follows the new route:
class OrdersController < ApplicationController respond\_to :html before\_filter :require\_current\_seller, :except => :authenticate before\_filter :load\_order, :except => \[:index, :authenticate\] def index `orders = current_seller.orders respond_with `orders end def show respond\_with @order end def destroy `order.destroy respond_with `order end def authenticate order = Order.authenticatable.find\_by\_login\_token!(params\[:id\]) \# TODO Force-log-in order.seller redirect\_to order end def ship `order.ship_it! respond_with `order end private def load\_order @order = current\_seller.orders.find(params\[:id\]) end end
And that’s it! Now, in the order notification email, we can send the automatic login link to the seller:
Dear <%= @seller.name %>, Congratulations! You have a new order. View your order here: <%= authenticate\_order\_url(@order.login\_token) %> Thank you!
There may be some hesitation with allowing direct authentication like this and in a many cases, it’s justified. This technique certainly doesn’t fit all applications. As always… proceed with extreme caution.
That said, this approach can be extremely useful in certain situations. If you’re already taking advantage of the “Forget your password?” functionality mentioned above, you’re probably sending similar, perishable, password-reset links to your users. Automatic login links are no less secure. Either way, an intruder has to either gain access to your email account or guess the random link.
It may be scary but I hope this helps shed some light on the technique and how it can be done securely. Your farmers will love it!
Update: Jeffrey Paul also wrote a blog post today on this very subject, explaining his own frustration with constant prompts to log in. You can read his post here.
Comments
current_seller would be nil, wouldn’t it?
Nice catch! Fixed. Thank you.
At Kontagent, we have a system where we are able to “morph” into user accounts, thereby receiving all the same permissions and abilities of that user. This helps us debug for support stuff, mostly. But it also includes the ability to send someone else a link to that account and hence the ability to also morph.
It’s a similar system, and I bet more things will start utilizing this paradigm.
This kind of URL is called a “capabilities URL” in security circles. It’s a common pattern, and often considered more secure than username/password pairs, though its security depends on how the user protects it.
You can find more details here: http://en.wikipedia.org/wiki/Capability-based_security
This makes me sad :(
@Phillip, no, it is not more secure. It is equivalent to storing your username/password in an email that will be forward on to someone else.
That means if you forward the email to anyone within the [insert expiry period here] they have full access you your account.
Please don’t be a Sony.
If you really want to do something like this, PLEASE implement some form of out of band 2FA. For example, SMS a PIN to a mobile number once the link has been clicked. This gives the benefit of not needing to remember your password, but requiring more than just a forwarded email to sign in.
You dont even need the column if you use url_store https://github.com/grosser/url_store
@Dan or you could disable the link after being clicked once? Not as efficient for the consumer, but more secure.
@Brad. That helps, but do you click every link you receive via email? I know I don’t.
The other assumption that is bad in terms of comparing it to the forget password link is in frequency.
If someone is intercepting traffic at the MTA, they will have a much lower strike rate than if you were sending auto login links in every email, as opposed to only when the user specifically requests it. Auto-login links = Guaranteed compromise vs Forgot password link = Potential Compromise.
PS: I’m not advocating the forgot password links either, though :)
Multi-factor is always better…and not that hard today. Go, go, gadget SMS API.
Main gotchas with URI tokens are that it must expire and that you should use the same practices as for other login attempts. It shouldn’t be happy hour on brute force attempts simply because there’s not a web form involved.
Yea, I am not sure I like this.
If you need to login with username / password then the bad guy has to have access to your computer to keylog.
With the above method all he needs is access to your email account. And with things like auto-forwarding on gmail…
@Dan actually that’s exactly the point of capabilities URLs, anyone who has the URL has access to the page. You’d email it to anyone who you wanted to give access to the site. Another example of this in the wild is flickr’s secret URLs to a private photo set, or skitch’s secret URLs to a photo.
@Philip, no argument on that.
The topic here, however, is slightly different. We are talking about account sign in and the level of security auto-login links provides.
Unless you are arguing that it is a common use case to have someone want to forward on access to their account (as opposed to access to view stuff they set up through their account), my point still stands.
it is ablicble for bloger and wordpress also?
@Dan I agree with Philip. In real life it is equivalent to giving the keys of your pad to someone else, possibly someone you trust.
Now the someone you trust is free to abuse it. Either by doing something nasty in your house, or worse by copying the key; or maybe even giving it to someone else without your knowledge. Yes. The practice is inherently insecure. But we do it all the time with the people we trust or when the damages of a breach affect both the parties.
That said, if one doesn’t want the user to do the sign in, forget password and confirmation dance for a getting a small action done, I don’t see a comparable easy-to-use solution than using single-use links. In such cases, sharing the link is really a “feature”; and a much better solution than sharing actual account passwords.
Also, application designers rely on certain assumptions - like the user would follow instructions in English (or some other language), the screen-resolution would be a certain minimum and the likes… The assumption that a user is able to secure his/her email account is central in this scheme of things.
At the end of the day, all the application designer has done is to give the user the keys to his own account. If the user can’t secure his email account, or doesn’t know better about whom to trust with the links - I don’t think it is a mistake on the application designer’s part at all.
From a security standpoint, what an application designer should do must really depend on the value at risk. Best practices aren’t for everyone…
@Amol, it’s probably not a good idea to trust the user to secure his/her email account. In addition to the possibility that at least one (and probably most, in this scenario) seller will not take the appropriate steps to secure the account itself, the global email infrastructure is not designed to keep messages secure in transit. At least one hop along the way from sending system to receiving account is likely to allow for the possibility of snooping.
Reading these comments brings to mind another possibility that might better balance the account security concerns with the desire for user convenience. With a bit more developer effort, you could implement a two-tier access system. Automatic login links would give you access to the specific task at hand, and possibly other related tasks. But attempting to access core account functions (bank info, seller contact information and password, full customer list, etc.) would prompt for the account’s password first.
It’s also worth considering that, even if you are willing to accept the reduced security of automatic login links, some of your customers may not. You can address this with a user preference setting (“Allow automatic login from email links”).
@Dan, Adding one point to your post, You have mentioned the case of forwarded mail, My point is if suppose seller1 do not have enough fruits and wants to forward that email to seller2, in that case none of the above solution works. In that case can we ask something like Not Seller1, Log out. But I am biased if the user needs did not get conflicted by doing this? After all security is very important concern for me.
Well currently,I am using Capability URLs.