Understanding the Rails Authenticity Token

Understanding the Rails Authenticity Token

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.example/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.


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

Ruby on Rails: Difference of Authenticity Token being in Header or POST

Part one

There is totally no difference if you pass authenticity token via GET params, POST data or request headers (POST/GET params are virtually the same in Rails).

Let's look at the code (not the best code I've ever seen but...)

def verified_request?
!protect_against_forgery? || request.get? || request.head? ||
form_authenticity_token == params[request_forgery_protection_token] ||
form_authenticity_token == request.headers['X-CSRF-Token']
end

Request if valid if (any of following)

  1. protect_against_forgery? is false
  2. request is GET
  3. request is HEAD
  4. token in params equals one stored in session
  5. token in headers equals one stored in session

I should add that token is generated for every request and stored in session for later inspection (if subsequent request is POST/PUT/PATCH/DELETE)

So as you see both ways of passing authenticity token are valid.

Part two

Is passing raw auth token in AJAX dangerous? No, as much as passing it in a form is totally not dangerous. To explain further I will quote an excellent answer in another SO question

Why this happens: Since the authenticity token is stored in the
session, the client can not 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).

original answer: https://stackoverflow.com/a/1571900/2422778

I still consider this question laziness/lack of patience on your side as all I wrote is very well explained both in Rails Guides and on Stack Overflow. Hope next time you will be more persistent in looking for answers before posting here.

Anyway I am glad I could help.

Invalid authenticity token in Rails

I fixed this by clearing my cookie for localhost.

I had used SSH tunneling via a localhost port to access the production site, so a cookie based on a conflicting session and secret_key_base was in the localhost domain.

I prevented this from occurring again by prepending the Rails environment to the name of the cookie:

Rails.application.config.session_store :cookie_store, key: "_incidents_#{Rails.env}_session"

So that despite using localhost to access my development and production environments, they would be writing to different cookies by virtue of being in different environments.

rails 6 token authentication still needed?

No, you don't need to add it manually, Rails does it for you in each form.

<%= form_with do |form| %>
Form contents
<% end %>

generates

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

You'll notice that the HTML contains an input element with type
hidden. This input is important, because non-GET forms cannot be
successfully submitted without it. The hidden input element with the
name authenticity_token is a security feature of Rails called
cross-site request forgery protection, and form helpers generate it
for every non-GET form (provided that this security feature is
enabled). You can read more about this in the Securing Rails
Applications guide.

https://guides.rubyonrails.org/form_helpers.html

Rails Authenticity Token not valid when using a different layout

Since you are using Rails-ujs, you can use it's ajax method that already handles the token for you:

https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee#L15

$(document).on("click",".clickable", function(){
var link = this.dataset.link;
console.log(link);
Rails.ajax({
url: link,
type: "POST",
});
});


Related Topics



Leave a reply



Submit