I am running into some issues regarding the Authenticity Token in Rails, as I have many times now.

But I really don't want to just solve this problem and go on. I would really like to understand the Authenticity token. Well, my question is, do you have some complete source of information on this subject or would you spend your time to explain in details here?

5 upvote
  flag
Also see: "Why Does Google Prepend while(1) to their JSON response?" //allinonescript.com/questions/2669690/… – Chloe

10 Answers 11

The authenticity token is designed so that you know your form is being submitted from your website. It is generated from the machine on which it runs with a unique identifier that only your machine can know, thus helping prevent cross-site request forgery attacks.

If you are simply having difficulty with rails denying your AJAX script access, you can use

<%= form_authenticity_token %>

to generate the correct token when you are creating your form.

You can read more about it in the documentation.

The Authenticity Token is rails' method to prevent 'cross-site request forgery (CSRF or XSRF) attacks'.

To put it simple, it makes sure that the PUT / POST / DELETE (methods that can modify content) requests to your web app are made from the client's browser and not from a third party (an attacker) that has access to a cookie created on the client side.

up vote 1326 down vote accepted

What happens

When the user views a form to create, update, or destroy a resource, the Rails app creates a random authenticity_token, stores this token in the session, and places it in a hidden field in the form. When the user submits the form, Rails looks for the authenticity_token, compares it to the one stored in the session, and if they match the request is allowed to continue.

Why it happens

Since the authenticity token is stored in the session, the client cannot know its value. This prevents people from submitting forms to a Rails app without viewing the form within that app itself. Imagine that you are using service A, you logged into the service and everything is ok. Now imagine that you went to use service B, and you saw a picture you like, and pressed on the picture to view a larger size of it. Now, if some evil code was there at service B, it might send a request to service A (which you are logged into), and ask to delete your account, by sending a request to http://serviceA.com/close_account. This is what is known as CSRF (Cross Site Request Forgery).

If service A is using authenticity tokens, this attack vector is no longer applicable, since the request from service B would not contain the correct authenticity token, and will not be allowed to continue.

API docs describes details about meta tag:

CSRF protection is turned on with the protect_from_forgery method, which checks the token and resets the session if it doesn't match what was expected. A call to this method is generated for new Rails applications by default. The token parameter is named authenticity_token by default. The name and value of this token must be added to every layout that renders forms by including csrf_meta_tags in the HTML head.

Notes

Keep in mind, Rails only verifies not idempotent methods (POST, PUT/PATCH and DELETE). GET request are not checked for authenticity token. Why? because the HTTP specification states that GET requests is idempotent and should not create, alter, or destroy resources at the server, and the request should be idempotent (if you run the same command multiple times, you should get the same result every time).

Also the real implementation is a bit more complicated as defined in the beginning, ensuring better security. Rails does not issue the same stored token with every form. Neither does it generate and store a different token every time. It generates and stores a cryptographic hash in a session and issues new cryptographic tokens, which can be matched against the stored one, every time a page is rendered. See request_forgery_protection.rb

Lessons

Use authenticity_token to protect your not idempotent methods (POST, PUT/PATCH, and DELETE). Also make sure not to allow any GET requests that could potentially modify resources on the server.


EDIT: Check the comment by @erturne regarding GET requests being idempotent. He explains it in a better way than I have done here.

24 upvote
  flag
@Faisal, is it possible then, for an attacker to simply read/capture the 'hidden' element of the form for Service A and get that unique token generated for the user - given that they have gotten access to the session started by the user for Service A? – marcamillion
1 upvote
  flag
For instance by hijacking the data returned from a debug function? Just a thought. – marcamillion
10 upvote
  flag
@marcamillion: If somebody hijacked your session at service A, then the authenticity token won't protect you. The hijacker will be able to submit a request and it will be allowed to proceed. – Faisal
upvote
  flag
Is it possible to detect when a CSRF attempt occurs? Maybe write to a log file or get a notification email ? – Zabba
12 upvote
  flag
@zabba: Rails raises an ActionController::InvalidAuthenticityToken exception if a form is submitted without the proper token. You can rescue_from the exception and do whatever processing you want. – Faisal
1 upvote
  flag
This behaviour has changed from Rails 3.0.4. weblog.rubyonrails.org/2011/2/8/… – Vijay Dev
5 upvote
  flag
re "Also make sure not to make any GET requests that could potentially modify resources on the server." -- this includes not using match() in routes which could potentially allow GET requests to controller actions intended to receive only POSTs – Steven Soroka
84 upvote
  flag
"...and the request should be idempotent (if you run the same command multiple times, you should get the same result every time)." Just a subtle clarification here. Safe means no side-effects. Idempotent means the same side effect no matter how many time a service is called. All safe services are inherently idempotent because there are no side effects. Calling GET on a current-time resource multiple times would return a different result each time, but it's safe (and thus idempotent). – erturne
upvote
  flag
only remeber if you are working in app that will make a remote post from another url (like Facebook iframe apps) you need to deactivate for this especific form. – Weverton Timoteo
1 upvote
  flag
GET requests shouldn't need to be idempotent. What if you are viewing a frequently updated feed, or a list of random things? – fabspro
upvote
  flag
Whats if request is coming from mobile platform instead of browser ? then how I gona handle this situation @Faisal – mfq
upvote
  flag
@mfq: If by mobile you mean something like API access, then you should be using API keys and secret API keys to authenticate and validate requests. The built-in CSRF token in rails does not work with API like requests – Faisal
upvote
  flag
@Faisal like facebook connect. I guess you are referring to outh token implementation ? – mfq
upvote
  flag
@mfq, that's one way to handle it. See this railscast for others: railscasts.com/episodes/352-securing-an-api?view=asciicast – chuck w
upvote
  flag
Note that by default, the statement, "Since the authenticity token is stored in the session, the client can not know its value." is not true. See this answer. – dukedave
2 upvote
  flag
@dukedave in the context of a CSRF attack, the statement holds, because the client (in this case, javascript running in the malicious site origin) can not view the cookie from a different origin. Also, for anyone reading this way down here, dukedave says "by default", because the default session store is the cookie. Switching to any other session store will nullify this risk. – Faisal
upvote
  flag
Thx for the explanation - can you explain also why the authenticity_token is placed both in the meta tag, and also in each form, and what happens if an ajax response replaces a form on the page. Aren't there forms now with different authenticity_tokens? Are they one time use? And if so how do the other forms continue to work. – Brian Armstrong
1 upvote
  flag
@BrianArmstrong The meta tags do the exact same thing as the hidden form fields, however they exist as a way for javascript and ajax to have access to the token to send with AJAX requests. As for your other question, the token stays the same during a single session, so loading multiple forms won't generate multiple tokens, and they would all function as expected – Faisal
upvote
  flag
Gotcha. What if the page first loads with one authenticity token in the meta tag. Then you have two ajax forms on the page. The first runs and uses the authenticity token. The second then runs and tries to use the same token, but it has already been used. Is this possible or what am I missing? Thanks! – Brian Armstrong
upvote
  flag
Ahh, didn't realize the authentity_token stays the same for an entire session. Makes more sense now! – Brian Armstrong
upvote
  flag
Hey so if the cookie store holds the session couldn't a csrf attack just grab the token from cookie and then submit? – Austio
2 upvote
  flag
@austio, same-origin policy would prevent the javascript loaded by other origins (websites) from reading a cookie from a different domain. As an added precaution, you should make your session cookie "httpOnly", so that javascript can't read it at all. – Faisal
upvote
  flag
@Faisal PUT and DELETE are idempotent methods. So rails CSRF premise is not based on idempotence (state of system after a request has been executed). Rather it is proactive damage control that potentially "evil" code can cause to your system. – saihgala
upvote
  flag
@saihgala please check the comment by erturne. He explained it in a better way than I have. – Faisal
1 upvote
  flag
Can't a malicious page use JavaScript to issue a GET to the target page, extract the authenticity token from the returned HTML, and thus have the token for its own purposes? – Chuck Batson
upvote
  flag
Why the html include two token? head token and form token – Abel
upvote
  flag
@Faisal So turning csrf off for javascript requests is not safer..right?? – Abhilash
upvote
  flag
"GET request are not checked for authenticity token." ---> Would like to add that, apparently (if i read wikipedia correctly), this was the root cause of an ING security breach (ING is a bank). – BKSpurgeon
upvote
  flag
@ChuckBatson - I 100% agree, CSRF tokens are kind of pointless in this regard, an attacker can just grab it before making POST call. It does make it slightly harder and can't do it with just links, but require extra javascript steps, besides that its barely more secure it seems... – bjm88
upvote
  flag
@bjm88 The same-origin policy prevents the attacker from doing so. – Demi

Beware the Authenticity Token mechanism can result in race conditions if you have multiple, concurrent requests from the same client. In this situation your server can generate multiple authenticity tokens when there should only be one, and the client receiving the earlier token in a form will fail on it's next request because the session cookie token has been overwritten. There is a write up on this problem and a not entirely trivial solution here: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/

What is CSRF?

The Authenticity Token is a countermeasure to Cross-Site Request Forgery (CSRF). What is CSRF, you ask?

It's a way that an attacker can potentially hijack sessions without even knowing session tokens.

Scenario:

  • Visit your bank's site, log in.
  • Then visit the attacker's site (e.g. sponsored ad from an untrusted organization).
  • Attacker's page includes form with same fields as the bank's "Transfer Funds" form.
  • Attacker knows your account info, and has pre-filled form fields to transfer money from your account to attacker's account.
  • Attacker's page includes Javascript that submits form to your bank.
  • When form gets submitted, browser includes your cookies for the bank site, including the session token.
  • Bank transfers money to attacker's account.
  • The form can be in an iframe that is invisible, so you never know the attack occurred.
  • This is called Cross-Site Request Forgery (CSRF).

CSRF solution:

  • Server can mark forms that came from the server itself
  • Every form must contain an additional authentication token as a hidden field.
  • Token must be unpredictable (attacker can't guess it).
  • Server provides valid token in forms in its pages.
  • Server checks token when form posted, rejects forms without proper token.
  • Example token: session identifier encrypted with server secret key.
  • Rails automatically generates such tokens: see the authenticity_token input field in every form.
1 upvote
  flag
Here is a version of this same explanation that is less precise but also less abstract: //allinonescript.com/a/33829607/2810305 – Lutz Prechelt
upvote
  flag
I'm not sure but, do modern browsers allow sending not idempotent requests(POST/PUT/DELETE) to another domain? I guess, there must be protection against such in things in browser itself – divideByZero

since Authenticity Token is so important, and in Rails 3.0+ you can use

 <%= token_tag nil %>

to create

<input name="authenticity_token" type="hidden" value="token_value">

anywhere

upvote
  flag
This was helpful to me. I was actually trying to do XSS on the login page, not for nefarious purposes, but to create a new session with pre-filled user name. Now I know I can just use value="token_value". – Michael

Methods Where authenticity_token is required

authenticity_token is required in case of idempotent methods like post, put and delete, Because Idempotent methods are affecting to data.

Why It is Required

It is required to prevent from evil actions. authenticity_token is stored in session, whenever a form is created on web pages for creating or updating to resources then a authenticity token is stored in hidden field and it sent with form on server. Before executing action user sent authenticity_token is cross checked with authenticity_token stored in session. If authenticity_token is same then process is continue otherwise it does not perform actions.

3 upvote
  flag
Actually, isn't it the opposite ? GET is idempotent since its call shouldn't alter the state of the system, where PUT POST and DELETE verbs are NOT idempotent verbs since they alter the system state. I.E : authenticity_token is required in case of NOT idempotent methods. – Jean-Daube
2 upvote
  flag
@Jean-Daube, uma: idempotent means that if done twice, action only happens once. GET, PUT and DELETE are idempotent: w3.org/Protocols/rfc2616/rfc2616-sec9.html The key property here is not idempotency, but if the method changes or not the data, which is called "Safe method" or not. – Ciro Santilli 华涌低端人口 六四事件 法轮功

Minimal attack example that would be prevented

On my website evil.com I convince you to submit the following form:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="to"      value="ciro"></p>
  <p><input type="hidden" name="ammount" value="100"></p>
  <p><button type="submit">CLICK TO GET PRIZE!!!</button></p>
</form>

If you are logged into your bank through session cookies, then the cookies would be sent and the transfer would be made without you even knowing it.

That is were the CSRF token comes into play:

  • with the GET response that that returned the form, Rails sends a very long random hidden parameter
  • when the browser makes the POST request, it will send the parameter along, and the server will only accept it if it matches

So the form on an authentic browser would look like:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="authenticity_token" value="j/DcoJ2VZvr7vdf8CHKsvjdlDbmiizaOb5B8DMALg6s=" ></p>
  <p><input type="hidden" name="to"                 value="ciro"></p>
  <p><input type="hidden" name="ammount"            value="100"></p>
  <p><button type="submit">Send 100$ to Ciro.</button></p>
</form>

Thus, my attack would fail, since it was not sending the authenticity_token parameter, and there is no way I could have guessed it since it is a huge random number.

This prevention technique is called Synchronizer Token Pattern.

The synchronizer token pattern works because of the Same Origin Policy: if I could make an XHR GET request to your bank from evil.com, and read the result, I would be able to just read a token and then make the request later. I have explained this further at: https://security.stackexchange.com/a/72569/53321

I highly recommend you to read the OWASP guide, on this and any other security matter.

How Rails sends the tokens

Covered at: Rails: How Does csrf_meta_tag Work?

Basically:

  • HTML helpers like form_tag add a hidden field to the form for you if it's not a GET form

  • AJAX is dealt with automatically by jquery-ujs, which reads the token from the meta elements added to your header by csrf_meta_tags (present in the default template), and adds it to any request made.

    uJS also tries to update the token in forms in outdated cached fragments.

Other prevention approaches

upvote
  flag
Thank you, but your point about relying on same origin policy to not be able to just read the CSRF token first seems flawed. So first you saying you can POST to a different origin but can't read from it, seems weird but I guess that is correct, but you could inject an image or script tag with a get to the page and link a handler to parse response and get it yes? – bjm88
upvote
  flag
@bjm88 inject the script where? On your site, or on the attacked site? If attacked site, allowing script injection is a well known security flaw, and effectively pawns the website. Every website must fight it through input sanitation. For images, I don't see how they can be used for an attack. On attacking site: you could modify your browser to allow the read, and thus auto pawn yourself at will :-) but decent browsers prevent it by default, give it a try. – Ciro Santilli 华涌低端人口 六四事件 法轮功

What is an authentication_token ?

This is a random string used by rails application to make sure that the user is requesting or performing an action from the app page, not from another app or site.

Why is an authentication_token is necessary ?

To protect your app or site from cross-site request forgery.

How to add an authentication_token to a form ?

If you are generating a form using form_for tag an authentication_token is automatically added else you can use <%= csrf_meta_tag %>.

The authenticity token is used to prevent Cross-Site Request Forgery attacks (CSRF). To understand the authenticity token, you must first understand CSRF attacks.

CSRF

Suppose that you are the author of bank.com. You have a form on your site that is used to transfer money to a different account with a GET request:

enter image description here

A hacker could just send an HTTP request to the server saying GET /transfer?amount=$1000000&account-to=999999, right?

enter image description here

Wrong. The hackers attack won't work. The server will basically think?

Huh? Who is this guy trying to initiate a transfer. It's not the owner of the account, that's for sure.

How does the server know this? Because there's no session_id cookie authenticating the requester.

When you sign in with your username and password, the server sets a session_id cookie on your browser. That way, you don't have to authenticate each request with your username and password. When your browser sends the session_id cookie, the server knows:

Oh, that's John Doe. He signed in successfully 2.5 minutes ago. He's good to go.

A hacker might think:

Hmm. A normal HTTP request won't work, but if I could get my hand on that session_id cookie, I'd be golden.

The users browser has a bunch of cookies set for the bank.com domain. Every time the user makes a request to the bank.com domain, all of the cookies get sent along. Including the session_id cookie.

So if a hacker could get you to make the GET request that transfers money into his account, he'd be successful. How could he trick you into doing so? With Cross Site Request Forgery.

It's pretty simply, actually. The hacker could just get you to visit his website. On his website, he could have the following image tag:

<img src="http://bank.com/transfer?amount=$1000000&account-to=999999">

When the users browser comes across that image tag, it'll be making a GET request to that url. And since the request comes from his browser, it'll send with it all of the cookies associated with bank.com. If the user had recently signed in to bank.com... the session_id cookie will be set, and the server will think that the user meant to transfer $1,000,000 to account 999999!

enter image description here

Well, just don't visit dangerous sites and you'll be fine.

That isn't enough. What if someone posts that image to Facebook and it appears on your wall? What if it's injected into a site your visiting with a XSS attack?

It's not so bad. Only GET requests are vulnerable.

Not true. A form that sends a POST request can be dynamically generated. Here's the example from the Rails Guide on Security:

<a href="http://www.harmless.com/" onclick="
  var f = document.createElement('form');
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = 'http://www.example.com/account/destroy';
  f.submit();
  return false;">To the harmless survey</a>

Authenticity Token

When your ApplicationController has this:

protect_from_forgery with: :exception

This:

<%= form_tag do %>
  Form contents
<% end %>

Is compiled into this:

<form accept-charset="UTF-8" action="/" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
  Form contents
</form>

In particular, the following is generated:

<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />

To protect against CSRF attacks, if Rails doesn't see the authenticity token sent along with a request, it won't consider the request safe.

How is an attacker supposed to know what this token is? A different value is generated randomly each time the form is generated:

enter image description here

A Cross Site Scripting (XSS) attack - that's how. But that's a different vulnerability for a different day.

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