Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p><code>token_authenticatable</code> is vulnerable to timing attacks, which are very well explained in <a href="http://codahale.com/a-lesson-in-timing-attacks/" rel="noreferrer">this blog post</a>. These attacks were the reason <code>token_authenticatable</code> was removed from Devise 3.1. See the <a href="http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/" rel="noreferrer">plataformatec blog post</a> for more info.</p> <p>To have the most secure token authentication mechanism, the token:</p> <ol> <li><p>Must be sent via HTTPS.</p></li> <li><p>Must be random, of cryptographic strength.</p></li> <li><p>Must be securely compared.</p></li> <li><p>Must not be stored directly in the database. Only a hash of the token can be stored there. (Remember, token = password. We don't store passwords in plain text in the db, right?)</p></li> <li><p>Should expire according to some logic.</p></li> </ol> <p>If you forego some of these points in favour of usability you'll end up with a mechanism that is not as secure as it could be. It's as simple as that. You should be safe enough if you satisfy the first three requirements and restrict access to your database though.</p> <p>Expanding and explaining my answer:</p> <ol> <li><p><strong>Use HTTPS</strong>. This is definitely the most important point because it deals with sniffers.</p> <p>If you don't use HTTPS, then a lot can go wrong. For example:</p> <ul> <li><p>To securely transmit the user's credentials (username/email/password), you would have to use digest authentication but <a href="http://devblog.avdi.org/2013/02/04/the-trouble-with-http-digest-authentication/" rel="noreferrer">that just doesn't cut it these days since salted hashes can be brute forced</a>.</p></li> <li><p>In Rails 3, cookies are only shrouded by Base64 encoding, so they can be fairly easily revealed. See <a href="http://www.andylindeman.com/2013/02/18/decoding-rails-session-cookies.html" rel="noreferrer">Decoding Rails Session Cookies</a> for more info. </p> <p>Since Rails 4 though, the cookie store is encrypted so data is both digitally verified and unreadable to an attacker. Cookies should be secure as long as your <code>secret_key_base</code> is not leaked.</p></li> </ul></li> <li><p>Generate your token with:</p> <ul> <li><a href="http://www.ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html" rel="noreferrer"><code>SecureRandom.hex</code></a> only if you are on Ruby 2.5+.</li> <li>The gem <a href="https://github.com/cryptosphere/sysrandom" rel="noreferrer"><code>sysrandom</code></a> if you are on an older Ruby.</li> </ul> <p>For an explanation on why this is necessary, I suggest reading the <a href="https://github.com/cryptosphere/sysrandom" rel="noreferrer"><code>sysrandom</code></a>'s README and the blog post <a href="https://paragonie.com/blog/2016/05/how-generate-secure-random-numbers-in-various-programming-languages#ruby-csprng" rel="noreferrer">How to Generate Secure Random Numbers in Various Programming Languages</a>.</p></li> <li><p>Find the user record using the user's ID, email or some other attribute. Then, compare that user's token with the request's token with <a href="https://github.com/plataformatec/devise/blob/8e4a700f81c8bfc3d46951e34f5ff3c18138c89e/lib/devise.rb#L481-L488" rel="noreferrer"><code>Devise.secure_compare(user.auth_token, params[:auth_token]</code></a>. If you are on Rails 4.2.1+ you can also use <a href="http://api.rubyonrails.org/classes/ActiveSupport/SecurityUtils.html#method-c-secure_compare" rel="noreferrer"><code>ActiveSupport::SecurityUtils.secure_compare</code></a>.</p> <p>Do <strong>not</strong> find the user record with a Rails finder like <code>User.find_by(auth_token: params[:auth_token])</code>. This is vulnerable to timing attacks!</p></li> <li><p>If you are going to have several applications/sessions at the same time per user, then you have two options:</p> <ul> <li><p>Store the unencrypted token in the database so it can be shared among devices. This is a bad practice, but I guess you can do it in the name of UX (and if you trust your employees with DB access).</p></li> <li><p>Store as many encrypted tokens per user as you want to allow current sessions. So if you want to allow 2 sessions on 2 different devices, keep 2 distinct token hashes in the database. This option is a little less straightforward to implement but it's definitely safer. It also has the upside of allowing you to provide your users the option to end current active sessions in specific devices by revoking their tokens (just like <a href="https://github.com/blog/1658-view-active-browser-sessions" rel="noreferrer">GitHub</a> and Facebook do).</p></li> </ul></li> <li><p>There should be some kind of mechanism that causes the token to expire. When implementing this mechanism take into account the trade-off between UX and security.</p> <p><a href="https://developers.google.com/identity/protocols/OAuth2#expiration" rel="noreferrer">Google expires a token if it has not been used for six months</a>.</p> <p><a href="https://developers.facebook.com/docs/facebook-login/access-tokens/expiration-and-extension" rel="noreferrer">Facebook expires a token if it has not been used for two months</a>:</p> <blockquote> <p>Native mobile apps using Facebook's SDKs will get long-lived access tokens, good for about 60 days. These tokens will be refreshed once per day when the person using your app makes a request to Facebook's servers. If no requests are made, the token will expire after about 60 days and the person will have to go through the login flow again to get a new token.</p> </blockquote></li> <li><p>Upgrade to Rails 4 to use its encrypted cookie store. If you can't, then encrypt the cookie store yourself, like suggested <a href="https://stackoverflow.com/questions/4147387/how-to-secure-a-rails-app-against-firesheep">here</a>. There would absolutely be no problem in storing an authentication token in an encrypted cookie store.</p></li> </ol> <p>You should also have a contingency plan, for example, a rake task to reset a subset of tokens or every single token in the database.</p> <p>To get you started, you could check out <a href="https://gist.github.com/josevalim/fb706b1e933ef01e4fb6" rel="noreferrer">this gist</a> (by one of the authors of Devise) on how to implement token authentication with Devise. Finally, <a href="http://railscasts.com/episodes/352-securing-an-api" rel="noreferrer">the Railscast on securing an API</a> should be helpful.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload