I am building a web app using spring security and thymeleaf, I got it work with login and logout, but I have some issues when I try to register as an end user. I am getting an invalid csrf token error. I am new to this, and I could use some help. My question is how can I attach a token to that request?(Post /registration) By the way I haven't used any XML, I use annotations.
This is the request
$scope.registerUser = function() {
$http.post(BASE_URL + "/registration", $scope.registrationRequest).
success(function (data, status, headers, config) {
$log.info("Success: " + headers('Location'));
$scope.hasRegistered = true;
}).error(function(data, status, headers, config) {
$scope.hasRegisterErrors = true;
$log.info("Error: status =" + status + ", body =" + JSON.stringify(data));
});
}
And the error
Error: status =403, body ={"timestamp":1430645356572,"status":403,"error":"Forbidden","message":"Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.","path":"/registration"}
The response message is telling you that the server expects your POST request to include either a "_csfr" parameter or an "X-CSRF-TOKEN" header. Presumably, your server has CSFR protection enabled. This is default behaviour with Spring Security though you can disable it. (I wouldn't recommend doing that.)
The Cross Site Request Forgery (CSRF) chapter of the Spring Security documentation explains what this is all about. This section explains how to implement CSRF tokens, including examples that how to embed the token in an HTML <form> or include it in an AJAX request.
you should add csrf parameter as a parameter in your request. for example, use following code to define an input in your form to get csrf token and add as request parameter for sending post request by form:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
or use following code to send as ajax call:
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>
Then construct the header:
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
so, you need get srcf token to send as a parameter in your request. by spring security you can define csrf parameter name by following configuration:
<http>
<!-- ... -->
<csrf token-repository-ref="tokenRepository"/>
</http>
<bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false">
<property name="sessionAttributeName" value="_csrf"/>
<property name="headerName" value="_csrf_header"/>
</bean>
for more, see following links:
https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html
https://spring.io/blog/2013/08/21/spring-security-3-2-0-rc1-highlights-csrf-protection/
http://www.baeldung.com/spring-security-csrf
Related
I was struggling to send and recieve CSRF token, and I found, in the end, that Django was not able to get the token value because its name was different from the recommended one in its documentation. Why?
(I am doing AJAX on a HTTPS address and requests are cross-site.)
Django documentation recommends that I add token to AJAX header in following way:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Here, the name is X-CSRFToken, which somehow becomes HTTP_X_CSRFTOKEN.
On the other hand, Django is looking up the cookie under CSRF_COOKIE.
Line 278 in csrf.py of CsrfViewMiddleware:
csrf_token = request.META.get('CSRF_COOKIE')
if csrf_token is None:
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
# and in this way we can avoid all CSRF attacks, including login
# CSRF.
return self._reject(request, REASON_NO_CSRF_COOKIE)
I cannot change the variable name because I get this error:
Request header field CSRF_COOKIE is not allowed by Access-Control-Allow-Headers in preflight response.
So, I ended up changing the variable name in the source code from CSRF_COOKIE to HTTP_X_CSRFTOKEN. Are there any way to make this work?
(I do not do #csrf_exempt, so please do not recommend.)
The problem is not from Django, if you read closely here: https://docs.djangoproject.com/en/2.0/ref/csrf/#how-it-works you will understand how it works and what kind of logic they follow.
The problem is that you are not allowing the headers:
Request header field CSRF_COOKIE is not allowed by Access-Control-Allow-Headers in preflight response.
If you search for this ACAH you will find that you must edit your server config file to allow this kind of posts.
The other case is that you may not be sending properly the header and that's why it's looking for the cookie. In that case you can try adding this to your header:
xhr.setRequestHeader('X-CSRFToken': $('meta[name="token"]').attr('content') });
I have Spring boot app where I integrate JWT authentication.
In order to authenticate, user need to send a POST request to /login with username and password, then he gets a JSON response with {"token": "BEARER SOME_TOKEN" }.
In my swagger UI, when I click "Try it out" and execute a request, the request is being sent without any token.
Question - Is there any way to query a login request and add authorization token to the Swagger UI requests?
In this case we can intercept the tokens and add them to all the requests by using these two interceptors in the index.html when defining SwaggerUIBundle:
const ui = SwaggerUIBundle({
...
responseInterceptor:
function (response) {
if (response.obj.access_token) {
console.log(response.obj.access_token)
const token = response.obj.access_token;
localStorage.setItem("token", token)
}
return response;
},
requestInterceptor:
function (request) {
request.headers.Authorization = "Bearer " + localStorage.getItem("token");
return request;
}
}
The responseInterceptor captures the response and if it contains the field "token" it saves it in local storage.
The requestInterceptor adds the Authorization header on each call you make from swagger-ui using the value from local storage.
This fix is for using v3 of swagger-ui:
<script src="https://unpkg.com/swagger-ui-dist#3.12.1/swagger-ui-standalone-preset.js"></script>
<script src="https://unpkg.com/swagger-ui-dist#3.12.1/swagger-ui-bundle.js"></script>
Actually, I use spring boot, thymeleaf and spring security.
Security is splitted in two in the application.
One for mvc and other one for rest call.
I search a way if login is ok to create a cookie with login/password to be able to do add header to every ajax call.
headers: {
"Authorization": "Basic " + $.cookie('cookie-authorization')
},
So i search to create a cookie if login success
Edit is it possible do to it on the server side?
You can use this code from the official doc to include the tokens in all your Ajax calls.
$(function() {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
})
Hope this helps.
I am attempting to perform an AJAX submission to Laravel. According to Laravel's documentation here, I need to add a CSRF token as both a request header and POST parameter. I'm retrieving the CSRF Token from the XSRF-TOKEN cookie as also described in the documentation:
// Grab XSRF cookie.
var csrf_token;
var cookies = document.cookie.split(';');
for(cookie_offset in cookies) {
var cookie_parts = cookies[cookie_offset].split('=');
if(cookie_parts[0].trim() === 'XSRF-TOKEN') {
csrf_token = cookie_parts[1].trim();
}
}
This retrieves a token similar to the following:
eyJpdiI6Ik96QTdtcFIzam85TGJNQ3pzNUF1blE9PSIsInZhbHVlIjoiQm8zelBSaFpaM0JZcjlxcURFVEZNenZWMzNxUHFBMm1VVzM3YXpBbjVvaTBReEY5cFA1RGV3UVBHQWhjVGhZYmtDZ2lacGxFejJwQkxHaGplV1wvVEtRPT0iLCJtYWMiOiI3NTlkZmI5ODU2YTdlN2RiYTA1YTAyM2NiZmZlOWUwZTQyY2I0NTUzOWEyNzI5YjE2ODIyMmU1YzZiNDE1MmQ0In0
Once I have the token, I am adding it as a X-CSRF-Token header and _token POST parameter.
When I perform the request, I'm receiving this error:
TokenMismatchException in VerifyCsrfToken.php line 46
I've also tried passing the token as a X-XSRF-TOKEN header instead, but am getting the same error
When I set the X-XSRF-TOKEN and omit the _token POST parameter, I encounter a different error:
DecryptException in Encrypter.php line 142:
Invalid data.
What is it that I am missing here?
UPDATE:
After some more debugging and comparing this request with requests being generated by an AngularJS implementation which ran side-by-side this implementation, I discovered the problem was that I needed to URL Decode the cookie's value.
I simply needed to do the following, after extracting the csrf token from the cookies:
csrf_token = decodeURIComponent(csrf_token );
You add the token to the client's request either for AJAX or as a hidden field in a form. You have to add a way for the client to know what the token is. One method is to use the token meta tag from the base view:
<meta name="xsrf-token" content="{{Session::token()}}" />
Then in your javascript side for any ajax request you can do:
$(document).ready(function(){
$.ajaxSetup({
headers: {
'X-CSRF-Token':$('meta[name="xsrf-token"]).attr('content')
}
});
}
Otherwise when you post you can use the CSRF token in a hidden field
as so:
<input type="hidden" name="_token" value="{{Session::token()}}">
I am working on a web application built using Spring framework. I am getting Invalid CSRF Token error. I see this behavior only in Chrome browser. Following are the steps followed:
Login to the application by providing userName and password
Click on Logout button to logout. The user will be re-directed to the login page
Then, in the login page again try to login. I am getting the below error
Invalid CSRF Token 'd82dfa89-81b1-449e-9ef5-cdd32957e7f3' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
Spring security configuration:
http.
addFilter(headerAdminFilter).
authorizeRequests().
regexMatchers("/login.*").permitAll().
regexMatchers("/api.*").fullyAuthenticated().
regexMatchers("/jolokia.*").hasRole(ADMINISTRATOR).
regexMatchers("/appadmin.*").hasRole(ADMINISTRATOR).
regexMatchers(".*").fullyAuthenticated().
and().
formLogin().loginPage("/login").successHandler(new RedirectingAuthenticationSuccessHandler()).
and().
exceptionHandling().authenticationEntryPoint(new RestAwareAuthenticationEntryPoint("/login"));
HTML code for Logout button:
<a id="logout-button" ng-click="ac.logout()" href="/login">Log Out</a>
AngularJS code for logout function:
this.logout = function () {
$http.post("/logout");
}
The following javascript snippet fixes stale CSRF token. The idea is to fetch a fresh token when the user tries to submit the login form and update the CSRF value in the form before the form is actually submitted.
$(document).ready(function() {
$("form[method=post]").submit(function(event) {
var form = this;
$.get("csrf-token", function(content) {
$(form).find("input[name=_csrf]").val(content);
form.submit();
});
event.preventDefault();
});
});
You need a /csrf-token mapping on the server side that returns the current CSRF token.