csrf token without jsp (spring mvc) - spring

I enabled csrf token in config of Spring security. But how mobile device recieve csrf token? When I had jsp, it looked like:
<input type='hidden' name='${_csrf.parameterName}' value='${_csrf.token}'/>
But now I have no jsp... so any way to send csrf manually?

A popular practice is to code a filter to attach the token as a cookie. Your client then sends a GET request first to fetch that cookie. For the subsequent requests, that cookie is then sent back as a header.
You can look at the official Spring Angular guide, and refer to Spring Lemon's source code for a detailed implementation.

You could implement a stateless CSRF protection. One solution nicely explained by Robbert van Waveren is to have the clients generate and send the same unique secret value in both a Cookie and a custom HTTP header:
Considering a website is only allowed to read/write a Cookie for its
own domain, only the real site can send the same value in both
headers. Using this approach all your server has to do is check if
both values are equal, on a stateless per request basis!

You may obtain it by looking inside of the _csrf attribute.
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/csrf_token")
public class CsrfTokenController {
#GetMapping
public String getToken(HttpServletRequest request) {
CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
return token.getToken();
}
}

If CSRF is enabled, you have to include a _csrf.token in the page you want to login or logout. Otherwise, both login and logout function will be failed.
Refer this for more help.
EDIT: You can get csrf token from request and send it according to your need. I have shared two references for csrf token please go through them. It will help you.
First Ref
Second Ref

Related

How to use key instead of `Set-Cookie` in Response Headers with Spring Security?

I am having difficulty to store cookies in a React Native apps.
My goal is to send the JSESSIONID and XSRF-TOKEN as Response Header's keys instead of Set-Cookie, and the client will handle to store it as cookie manually.
I will store the JSESSIONID as cookie with HttpOnly set to true.
I will store the XSRF-TOKEN as cookie with HttpOnly set to false.
Basic Security Concepts:
1. Session Cookie (JSESSIONID for example).
Should always be HttpOnly, have a domain set (or use the default as the server that provided it).
Never be stored anywhere other than the browser handling it. Your JS/HTML should know nothing about the JSESSION Cookie and only just move the user to a login screen if they get a 401 (UnAuthorised) from an endpoint.
2. CSRF Tokens.
Back in the day (5 years a go probably ha!), most sites were rendered on the server. The server created the HTML and then just sent it back via the URI. Like you went to /profile then the server knew who you were and then created the profile page on its server and just fed back the HTML document.
When wanting to get some user input, this HTML rendered by the server would contain a <form/> which would collect user's data (password/bank details etc.) and then with the onSubmit passes it back to the server in a application/x-www-form-urlencoded format
eg.
https://thewebsite.com/sendmoney_to?account=512&amount=1milliondollars
By simply sending that link to someone who has an active session with the site thewebsite.com the browser would visit it and carry out the request.
The victim is logged in as far as the site is concerned and will happily run that request sent by the attacker. These links were at one point even follower by simply loading an <img> like by posting on their wall or in an email etc.
So how did they fix this?
By adding some fields to the form called hidden fields. These hidden fields are created by the server when the page is rendered. They contain a value that is the CSRF TOKEN and also something in a CSRF COOKIE. So when the application/x-www-form-urlencoded form is sent, it must have these values produced on the server when rendering the form. The server can then verify the form was the one they created and not some malicious link an attacker created.
An attacker can not know/guess these when making their naughty link.
3. Nower Days
With having only JSON requests as many sites, like React Apps, are rendered client side and use Axios/Fetch...CSRF is somewhat redundant. You don't post forms/application/x-www-form-urlencoded...only make POST requests with a JSON body.
Sessions are still important as an XSS attack > CSRF attack. Store the session properly (JWT token or not...don't jump on the JWT band wagon and start throwing that JWT Auth Token around Local Storage which seems to have become some kinda strange default for newer devs).
If you are sure you only have application/json capable endpoints, then the only way an attacker is going to get you to POST their content instead of what you are meant to is via an XSS attack. But once they have an XSS attack it is game over anyway. They are simply then you as far as the server is concerned as they use their naughty injected <script> to manipulate the request before it sent and the server would have no way of knowing it had been manipulated on the fly (usually).
4. Getting your CSRF Token via a Header as it wont be made in the form
The XSRF-TOKEN you may need to expose the XSRF token by using a filter that will extract the CSRF Token (via the session) and adds it to a header on the response entity.
I wrote my own but it is basically the same in a library some use to do this same thing.
#Log4j2
public class CsrfBindingFilter extends OncePerRequestFilter {
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";
protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";
protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrfToken = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
log.debug("CRSF Token from session : {}", csrfToken != null ? csrfToken.getToken() : "CsrfToken is NULL");
if (csrfToken != null) {
response.setHeader(RESPONSE_HEADER_NAME, csrfToken.getHeaderName());
response.setHeader(RESPONSE_PARAM_NAME, csrfToken.getParameterName());
response.setHeader(RESPONSE_TOKEN_NAME, csrfToken.getToken());
}
filterChain.doFilter(request, response);
}
}
As far as the session, the browser should handle that and you should not be messing with it client side unless it is for a very specific reason (and I can't fathom one). The Cookies for Sessions are set as HttpOnly for the specific reason to disallow any JS running in the client to edit/read/add it.
One little cheeky advert/xss with some naughty code like get the cookie called JSESSION ID if the host is myvictim.com and post it over here... could mean you are compromised.
Read this for more detail:
Add HttpOnly Cookie via JS
The browser should handle the Set-Cookie headers as intended and is best practice (out of security reasons and also just for plain simple ease of use).
WebSocket STOMP Authentication
Once a user has been authenticated via your /login, Spring should send a Set-Cookie header with the JSESSIONID Cookie. This cookie will be stored by the browser and should be inaccessible to your front end javascript app.
If you then use the STOMP Spring WebSocket implementation, you can get the principal user name from a STOMP message by getting it via the StompHeaderAccessor in the #MessageMapping Controller params.
stompHeaderAccessor.getUser().getName() would give the Spring Security authenticated user's principal name (usually their username or id, username is default).
#MessageMapping("/agents/start")
public void start(StompHeaderAccessor stompHeaderAccessor) {
log.info("Subscriber Start! {}-{}", stompHeaderAccessor.getUser() != null ? stompHeaderAccessor.getUser().getName() : "ANON", stompHeaderAccessor.getSessionId());
mysessionstore.addSessionId(stompHeaderAccessor.getSessionId());
}
If you then want to edit the user's session attributes, you will then need to fetch their session id from the SPRING_SESSION table and you can use the Spring SessionRepository to fetch it.
https://docs.spring.io/spring-session/docs/current/api/org/springframework/session/SessionRepository.html

Add multiple authentication mechanism for different api groups springboot

We have an existing spring-boot application that supports basic Authentication with spring-security. this application uses spring templating so it accepts form data as input and saves sessions by doing authentication.
the login page is at /login after successful login it redirects to the home page of the website.
in the same spring boot service, we want to start supporting JSON based API which would be used by the Mobile app.
we were thinking of adding login API at /api/login which will be served for mobile devices.
is there a way where we can say
for /login use default authentication class and for /api/login use some other custom class which will read JSON data and will do Authentication.
we also want to use different page for unAuthorized access. as existing one renders custom HTML page. but with API we want to send JSON response with HTTP code.
Not a Java person, but generally speaking this can be done by creating a different controller that would handle the /api/login route.
Here's snippet below that might help :
[Source : https://spring.io/guides/gs/spring-boot/]
package com.example.springboot;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
#RestController
public class HelloController {
#RequestMapping("/api/login")
public String LoginAPI() {
// handle your authentication logic here.
}
}
I would strongly recommend against using the session based authentication. It's simply not designed for modern applications. If the option is open, you can take a upgrade to token based authentication (jwt.io), that would make your life much much easier.
If you must implement the session based authentication for mobile app as well. here's what you need to do
create the session on server
set the session key as a part of cookies in the response header
store the cookies in your app.
Include the cookies in request header in subsequent API calls from mobile app.
Again, not a Java person. Just hoping to help.

Regarding Cross site Scripting Forgery

I am working on csrf and using spring 5. Spring 5 automatically provide supports for csrf and on enabling csrf protection on the server side I am getting
403: Invalid X-CSRF token
So this means a token needs to come from frontend?
My understanding is that backend generates csrf token and sends as a response to frontend browser and then it uses this token and send it as cookies to the backend server and then backend will validate it. is my understanding is correct?
when manually generating the hidden token for csrf, How backend will know it is a valid csrf token?
Second Scenario: Suppose two users are logged in to my website and frontend is sending this token to backend then how the application will differentiate which token is for which user?
Also please explain how it works internally means we enabled csrf protection in the backend and manually generated a token on the front end then what it does behind the scenes?
consider my frontend is JS pages
Is there is any specialty of Spring 5 which take care's of sessions for each user and validate tokens automagically for each user?. I tried finding it on the official website but didn't get it anywhere
Hi Zaib the csrf token is generated from back-end as you stated, once it is generated is automatically sent to the front-end which must take care to retrieve from the model and re-post for each "POST" requests.
You can share the csrf token via different way mostly i used header or html parameter.
A token is related to a specific session so is not really important if you have a logged user or not , even not authenticated users must send the csrf token for "POST".
The csrf token is validated via a filter placed in the front of the filter chain defined by Spring security itself, if you search in the documentation there is a table showing you the position of each "default" filter enabled by Spring security. Moreover if you enable debug on Spring ( </debug> is enough in your xml configuration) you will have printed all the filters used while processing an http request.
So each time a request with "POST" method pass through that filter , it will check if in the parameters there is the csrf token or header.
I never used as cookie so it may a different case for you if specifically need that but it does not differ on how it works.
Here is the details of csrf implementation on Spring:
https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/reference/htmlsingle/#csrf-configure
I said "POST" method but actually the token is checked for any method that is related to a change of state , you can refer to doc here:
https://docs.spring.io/spring-security/site/docs/4.2.5.RELEASE/apidocs/org/springframework/security/web/csrf/CsrfFilter.html
Hope this help clarifying a bit the usage of the csrf token.

Spring Security - REST API - token vs. cookie

I have written a REST- API in Java and I have secured this API with Spring Security. The procedure is like this:
Frontend invokes /login RestService in Backend
Backend gives back token to frontend
at each REST- API Backend invokation the token has to be placed in header
This works fine, but I have read that it is also possible (with Node.JS/Passport.js/Express.js) that the session object with the cookie inside can be transfered out of the box without any custom code.
My question now would be if there is a better approach so that the frontend/client do not need to set the token into the header all the time for any request.
Usually token based authentication has advantages over cookie based.
You can achieve this using middle-ware layer
Here is a good Post - https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/
Server side, I usually first check in the headers if there is an auth token. If not, I then check in the cookies as a fallback.
If you want to use cookies, then at your step 2, you need to add a Set-Cookie header to the response, so that browsers know they must store a cookie. Once done, no need to add a header client-side, since browsers will send cookies each request. You'll need to add a CSRF protection though (here is a good example).

How do I send spring csrf token from Postman rest client?

I have csrf protection in spring framework. So in each request I send csrf token in header from ajax call, which is perfectly working.
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
In ajax
beforeSend: function(xhr) {
xhr.setRequestHeader(header, token),
xhr.setRequestHeader("username", "xxxx1"),
xhr.setRequestHeader("password", "password")
}
I haven't any idea to generate csrf token and include in header section of Postman Rest Client ? Would you please help me to send csrf token from Postman Rest Client?
The Easiest way to do this consistently so you don't have to get the token each time:
NOTE:you need to install PostMan Interceptor and activate it to have access to the browsers cookies
Create a new environment so environment variables can be stored
Create a login method with a test to store the XSRF cookie in an environment variable, in the test tab post this code
//Replace XSFR-TOKEN with your cookie name
var xsrfCookie = postman.getResponseCookie("XSRF-TOKEN");
postman.setEnvironmentVariable("xsrf-token", xsrfCookie.value);
EDIT
For anyone using the 5.5.2 postman or later you will also have to decode the cookie, and they have also provided alternative ways to obtain cookies as #Sacapuces points out
pm.environment.set("xsrf-token", decodeURIComponent(pm.cookies.get("XSRF-TOKEN")))
Now you will have an environment variable with xsrf-token in it.
Save your login method
Create the new post you want to create and in the headers add your XSRF-Token-Header Key, and the environment variable in handle bars to access it{{}}
Now before running your new request make sure you run your login, it will store the environment variable, and then when you run the actually request it will automatically append it.
I am able to send REST with csrf token by following the steps below:
The CSRF token generated automatically by spring security when you logged in. It will be shown at the response header.
The CSRF token can be used on subsequent request by setting X-CSRF-TOKEN with CSRF token on header.
Firstly you need to install PostMan Interceptor and activate it to have access to the browsers cookies.
You have to fetch the CSRF Token by making a GET Request:
Header: "XSRF-TOKEN" and Value: "Fetch"
You should see the Token in the cookie tab and can copy it (Notice: You can configure spring how the cookie should be named. Maybe your cookie has another name than "XSRF-TOKEN". Attention: You have the remove this blank char in the token from the newline)
Now make your POST Request and set the header to: Header: "X-XSRF-TOKEN" and Value: "Your copied Token without blanks"
For me works variant with adding X-CSRF-TOKEN to headers.
Please put X-CSRF-Token as key and FETCH as the value in the GET request header and you will receive the token in the response header
If you don't want to configure environment variables etc. here is the quickest solution
https://stackoverflow.com/a/49249850/3705478
I've used csrfTokenRepository() to allow spring security to generate csrf token
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// your code
}
}
After adding these lines of code, use GET request to generate csrf token. I've used postman and I got token in the response cookies section. Copy the token and use it in POST call.
Official documentation link :
https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/csrf.html

Resources