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 namedauthenticity_token
by default. The name
and value of this token must be added to every layout that renders
forms by includingcsrf_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)
protect_against_forgery?
is false- request is GET
- request is HEAD
- token in params equals one stored in session
- 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
How to Calculate Number of Chars Common to Two Strings
Can't Install Ruby Under Lion With Rvm - Gcc Issues
Where and How Is the _ (Underscore) Variable Specified
How to Set Tls Context Options in Ruby (Like Openssl::Ssl::Ssl_Op_No_Sslv2)
Ruby Multiline Block Without Do End
Calling a Method from a String With the Method'S Name in Ruby
When to Use 'Self.Foo' Instead of 'Foo' in Ruby Methods
What Are the Brackets [5.1] After Activerecord Migration and How Does It Work
Where Is Ruby'S String Literal Juxtaposition Feature Officially Documented
Accessing Elements of Nested Hashes in Ruby
Case Statement With Multiple Values in Each 'When' Block
How to Track System-Specific Config Files in a Repo/Project
How to Count Duplicate Elements in a Ruby Array