I was reading this stackoverflow comment which describes how rails authenticity tokens work: Understanding the Rails Authenticity Token

and the highest rated response begins with this:

"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."

This makes sense to me as an abstract concept but I wanted to understand how this works in a concrete sense, I wanted to see exactly how this happens so I am crystal clear about how this works, so I created a new rails app and scaffolded a User with just one field name then I dropped a binding.pry in Users#create right up top inside the action, which should happen directly after the user submits the form. The pry session began right after I added a new user name, hit submit and it moves to create...So I inspected the source of my application in my web browser to find that the csrf-token content value in my rails generated csrf meta tags do not match the hidden authenticity token value within my form and neither one matches the value I get if, during the same pry session, I examine the session.to_hash property and inspect the "_csrf_token" value.

I then tried setting up a pry in the Users#new action and the Users#create action and noted the "_csrf_token" value on the session and compared it to the values of the form fields and meta tags once I quit and my app moved to the pry in the Users#create action but nothing matched. It seems like none of these three values match at all.

Yet protect_from_forgery with: :exception is set in my application controller and from what I read in the top rated response I was expecting to see matching values...somewhere. It seems like nothing matches. So I currently have no concept of what rails is matching what to in order to allow for the form to proceed and the data to be saved.

The author of the top rated response also says that an authenticity_token is stored on the session but all I see is a _csrf_token (are these the same?) and a session_id and, as I said, they don't match anything. I see no match whatsoever.

If rails is matching something to the value in my form field, it doesn't seem to be the value of the '_csrf_token' unless its converting it to something else behind the scenes and then matching that value to the value in the hidden form field or something. I don't feel like I understand what is going on.

  • 1
  • 1
  • If the values don't match, there is probably some encoding going on, the best way to understand deeply these kind of stuff is to follow the code take a look at https://github.com/rails/rails/blob/v4.2.5/actionpack/lib/action_controller/metal/request_forgery_protection.rb#L247, I didn't followed fully, but if you do you should fount what you are looking for. – Leonel Galán Dec 18 '15 at 21:59
  • And maybe this is a good starting point to read: http://guides.rubyonrails.org/security.html – Alexander Trust Dec 19 '15 at 00:42
  • @Alexander I did read that document. Do you know which section answers the question I'm asking? Because I didn't find any section that answered my question. –  Dec 19 '15 at 02:24
  • I linked to it, because it was named as a source for further reading in a Code School course I once took. I remember when they talked about security and sessions. But my remembrance is not as good as to answer your question, sadly. I no longer have a Code School subscription, otherwise I would have searched the videos. But I do remember that the behavior is different if the user has Javascript enabled or not, Rails at least has a fallback for a situation like that. But... I found another one. Maybe this helps: https://www.railstutorial.org/book/_single-page#sec-sessions_and_failed_login – Alexander Trust Dec 19 '15 at 02:54

1 Answers1


Rails is built on top of the HTTP protocol.

enter image description here

HTTP is stateless, which means that each request has to be treated as unique (all the supporting data has to be built each time).

To do this, Rails has the session, which is a series of "cookies" stored on the browser's system:

HTTP is a stateless protocol. Sessions make it stateful.

These sessions are used to keep small snippets of data which are used by Rails to rebuild your user's "environment" with each request. Devise stores user_id in the session, and Rails keeps a ton of other data in there, too.


So as opposed to - for example - a game, where you have a continual flow of data via a stateful protocol such as TCP (once the connection is established, it stays established), Rails basically reconnects with each new request.

Part of this process is form submission.

To prevent problems arising from this (IE bots/spammers sending millions of requests), Rails uses CSRF to "match" the authenticity token in the user's session with the one displayed on the web page.

This basically says that the user submitting the form is the one who originally made the request, not some bot which got the from through a proxy or some shady software.

You end up with the following:

enter image description here

enter image description here

Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • 1
    I have to give you credit in that your explanation was an excellent way of explaining the process of how this works "in general" but (no offense intended) it did not answer my "specific" question. The question I had was: why is the content of the csrf-token in my meta tags, the value of the csrf token in my session and the value in the authenticity token in the hidden part of my form all completely different if rails is somehow 'matching' them to one another? –  Dec 20 '15 at 03:46
  • I looked inside the rails app and I'm currently convinced that rails is encoding the token in at least one way and probably more than one way behind the scenes in this file (/actionpack-4.2.0/lib/action_controller/metal/request_forgery_protection.rb) which might explain why they appear to be different. I could be wrong though. –  Dec 20 '15 at 03:51
  • I answered broadly because your question was huge. If you want specific answers, you really need to write specific questions – Richard Peck Dec 20 '15 at 08:11
  • I appreciate all answers, I didn't mean to offend you. –  Dec 20 '15 at 18:42