admin管理员组文章数量:1434978
Ok. I officially lost my mind with this problem.
Let's take a default Rails application (5
, but I tried also with a 4
default app).
I'm trying to use a simple javascript code to send an ajax POST request to one controller action.
In my ApplicationController
I have this code:
class ApplicationController < ActionController::Base
after_action :set_csrf_cookie
protected
def set_csrf_cookie
cookies["X-CSRF-Token"] = form_authenticity_token
end
end
which sets a cookie "X-CSRF-Token"
with the value of form_authenticity_token
.
After that I can read this cookie in my SPA (Single Page Application) using this code:
<script>
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(";");
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === " ") c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
// var token = document.getElementsByName('csrf-token')[0].content; // this works!
const token = readCookie("X-CSRF-Token"); // this doesn't work!
fetch('/api/v1', {
method: 'POST',
body: {""},
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': token
},
credentials: 'include'
}).then(function(response) {
return response.json();
});
</script>
When I use this line:
var token = document.getElementsByName('csrf-token')[0].content;
it works because it reads what Rails insert in html page with:
<%= csrf_meta_tags %>
<meta name="csrf-param" content="authenticity_token">
<meta name="csrf-token" content="VXaKlO+/Gr/8pGhr5y0bThQ5L/0IDiznMR/9SpaoI6vOoF9KtmB5/9ka+Hz+zjyssNRi/Em/Ye27C+E5pl3odg==">
So the content of "csrf-token" works and my Rails application can validate CSRF.
This is the code from Rails source: .2.0/actionpack/lib/action_controller/metal/request_forgery_protection.rb
When instead I use this line:
const token = readCookie("X-CSRF-Token");
it doesn't work and I get this error:
Started POST "/api/v1" for 172.18.0.1 at 2018-05-01 18:52:56 +0000
Processing by MyController#action as */*
Parameters: {"body"=>{}}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms)
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
Also if I use another page with another server (npm http-server or Microsoft IIS or others) with the same script the problem is the same.
If I copy the content of "csrf-token" from Rails html page and use this line in my Javascript script:
const token = "VXaKlO+/Gr/8pGhr5y0bThQ5L/0IDiznMR/9SpaoI6vOoF9KtmB5/9ka+Hz+zjyssNRi/Em/Ye27C+E5pl3odg==";
it WORKS!
So my question is: WHY?
What I have read (nut nothing!):
- .md
- Invalid Auth Token with Rails, Graphql, Apollo Client
- Rails CSRF Protection + Angular.js: protect_from_forgery makes me to log out on POST
- /
- Work with authenticity token? Or disable it?
- Rails API design without disabling CSRF protection
Ok. I officially lost my mind with this problem.
Let's take a default Rails application (5
, but I tried also with a 4
default app).
I'm trying to use a simple javascript code to send an ajax POST request to one controller action.
In my ApplicationController
I have this code:
class ApplicationController < ActionController::Base
after_action :set_csrf_cookie
protected
def set_csrf_cookie
cookies["X-CSRF-Token"] = form_authenticity_token
end
end
which sets a cookie "X-CSRF-Token"
with the value of form_authenticity_token
.
After that I can read this cookie in my SPA (Single Page Application) using this code:
<script>
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(";");
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === " ") c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
// var token = document.getElementsByName('csrf-token')[0].content; // this works!
const token = readCookie("X-CSRF-Token"); // this doesn't work!
fetch('/api/v1', {
method: 'POST',
body: {""},
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': token
},
credentials: 'include'
}).then(function(response) {
return response.json();
});
</script>
When I use this line:
var token = document.getElementsByName('csrf-token')[0].content;
it works because it reads what Rails insert in html page with:
<%= csrf_meta_tags %>
<meta name="csrf-param" content="authenticity_token">
<meta name="csrf-token" content="VXaKlO+/Gr/8pGhr5y0bThQ5L/0IDiznMR/9SpaoI6vOoF9KtmB5/9ka+Hz+zjyssNRi/Em/Ye27C+E5pl3odg==">
So the content of "csrf-token" works and my Rails application can validate CSRF.
This is the code from Rails source: https://github./rails/rails/blob/v5.2.0/actionpack/lib/action_controller/metal/request_forgery_protection.rb
When instead I use this line:
const token = readCookie("X-CSRF-Token");
it doesn't work and I get this error:
Started POST "/api/v1" for 172.18.0.1 at 2018-05-01 18:52:56 +0000
Processing by MyController#action as */*
Parameters: {"body"=>{}}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms)
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
Also if I use another page with another server (npm http-server or Microsoft IIS or others) with the same script the problem is the same.
If I copy the content of "csrf-token" from Rails html page and use this line in my Javascript script:
const token = "VXaKlO+/Gr/8pGhr5y0bThQ5L/0IDiznMR/9SpaoI6vOoF9KtmB5/9ka+Hz+zjyssNRi/Em/Ye27C+E5pl3odg==";
it WORKS!
So my question is: WHY?
What I have read (nut nothing!):
- https://github./equivalent/scrapbook2/blob/master/archive/blogs/2017-10-12-csrf-protection-on-single-page-app-api.md
- Invalid Auth Token with Rails, Graphql, Apollo Client
- https://www.bhalash./archives/13544808782
- Rails CSRF Protection + Angular.js: protect_from_forgery makes me to log out on POST
- https://technpol.wordpress./2014/04/17/rails4-angularjs-csrf-and-devise/
- Work with authenticity token? Or disable it?
- Rails API design without disabling CSRF protection
-
You could simplify your readCookie function with this:
document.cookie.match("X-CSRF-Token=([^;]+)")[1]
– gabeodess Commented Jan 15, 2019 at 15:41
3 Answers
Reset to default 6Tnx, I got your code working. I only needed to add decodeURIComponent()
const token = decodeURIComponent(readCookie("X-CSRF-Token"));
I used this approach for a Progressive Web App with cached html. The default rails meta tag (<%= csrf_meta_tags %>
) doesn't work with cached html.
This blog post also gives some other alternatives: https://www.fastly./blog/caching-uncacheable-csrf-security
The name of the header Rails expects is X_CSRF_TOKEN
(note the underscores). I don't see a problem with the rest of the code you've shared - except maybe that the token from the cookie must be URI decoded (decodeURIComponent
), so check this as well if you still get the warning.
are you checkedreadCookie("X-CSRF-Token")
value?
the value probably is different when storing to cookie, probably the value escaped
本文标签:
版权声明:本文标题:javascript - Rails CSRF protection with Single Page Application (react, angular, ember) - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1743648997a2515993.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论