I'm attempting to get full page caching in Rails but I've hit a big of a snag with regards to CSRF - or perhaps just my understanding of it. I currently have the form_authenticity_token
string stored in a cookie that JS can access and rewrite the header tags with.
There are two places I find tokens in my generated HTML:
1) In the head
<meta name="csrf-token" content="[hash]">
2) Inside a form's hidden input element
<input type="hidden" name="authenticity_token" value="[different hash]">
As indicated, these hashes are different from one another (in development mode where caching isn't enabled). Why are they different? Why is it that I can delete the head meta tags and leave the form input alone and the request is allowed? Yet when I delete the form input tag and leave the headers the request is rejected?
Effectively this means the head tags are useless, no? I can rewrite the form input tag to the value in my cookie just like I did with the header tags, but since they are different from one another I'm cautious as to what the end result might mean especially when it comes to full page caching.
Application Controller contains:
protect_from_forgery with: :exception
before_filter :csrf_cookie
def csrf_cookie
cookies['authenticity-token'.freeze] = {
value: form_authenticity_token,
expires: 1.day.from_now,
secure: (Rails.env.staging? || Rails.env.production?)
}
end
Browsing SO on another issue led me to the answer. In short, Rails helps out jQuery users by inserting the CSRF token into ajax requests automatically. It looks for it in the meta tags.
So having the CSRF token inside the form is useful for when submitting POST requests and having it in the head is useful for saving time/effort/mistakes with ajax requests.
Perhaps it's good to have it in both also because you may want to do an ajax request when there isn't a form present. If there IS a form and javascript is disabled, having it in the header doesn't do anyone any favours as it won't be included in the POST request.
As to why they are different, I can only guess it has something to do with the algorithm at the time of generation...but that's neither here nor there as both tokens work.