Why and is it necessary to turn off csrf in SpringSecurity when using authorization through a custom token - spring-security

I watched a training video on authorization and authentication by token, and there is an entry http.csrf().disable() in the method that connects the filters. Why do we disable csrf protection, the person did not explain? Here's the full class for a better idea of ​​what I'm talking about:
#Configuration
#RequiredArgsConstructor
public class SecurityConfig{
private final JwtTokenProvider jwtTokenProvider;
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests(authz ->
{
try {
authz
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/sensors/**").hasAuthority("ROLE_ADMIN")
.requestMatchers("/api/sensors/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_USER")
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
);
return http.build();
}
}

CSRF is a vulnerability where an attacker exploits the way browsers work. In more traditional web applications, your session token (which allows the app to authenticate requests) is sent in a cookie. Cookies are sent by browsers with requests based on where the request goes to, and regardless of the origin the request was made from. So if you are logged in to such an application, and visit a malicious site, that malicious site can have you make inadvertent requests to the victim site, and your session cookie will be sent automatically. So to prevent an attacker from setting up such a malicious site and have you make requests to the victim one potentially performing things you didn't want to, the victim site needs to implement protection against CSRF. There are multiple techniques to achieve this, for example the website can create a random CSRF token, include it in forms and store it server-side, expecting it back with every form submission. This works, because the attacker, when making a request from the malicious site will have no way to include the correct token. (Also the token belongs to an authenticated user, so even if the attacker does download a page to get a CSRF token, that will not be valid for another logged on user.)
Notice that all of the above relied on the fact that a cookie will be sent automatically. If, as in your example, the authentication token is sent as something lese (like as an Authorization header), that will not be automatically included by the browser. The attacker on a malicious website can still have you make a request to the victim site, however, you auth info cannot be included, so it will not work anyway - whatever such a request can do, the attacker could do by himself anyway (except some very special cases).
For this reason, CSRF protection can be disabled, if authencation is based on something that's not sent automatically.

Related

Spring Authorization Server: how to continue authorization after login

I am trying to integrate Authorization Server into my app with form login. I'm using my own login page.
Samples suggest to use the following configuration:
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
//...
http
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling((exceptions) ->
exceptions
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));
//...
)
Now when I try to authorize (/oauth2/authorize?...) I got redirection to my login page. If I logged in before I see OAuth consent page and able to submit consent. But 99% of times I see my /login page, able to log in and stuck here. How to continue to consent page there? Should I write my own logic for that?
Solved issue myself by removing custom .successHandler(...) from my custom form login configuration. Default SavedRequestAwareAuthenticationSuccessHandler correctly handle all redirects as expected.

Quickbooks OAuth identify user of callback using state - Is this good practice and are there any security concerns?

I'm working on an application using OAuth2.0 for Quickbooks. I'm basing my code on the example provided by intuit. Here I have looked a bit on the "state" parameter of the oauthClient.authorizeUri.
oauthClient = new OAuthClient({
clientId: req.query.json.clientId,
clientSecret: req.query.json.clientSecret,
environment: req.query.json.environment,
redirectUri: req.query.json.redirectUri,
});
OAuthClient;
var authUri = oauthClient.authorizeUri({
scope: [OAuthClient.scopes.Accounting],
state: "userid:1234", //Encoded as a JWT
});
res.send(authUri);
});
I was thinking of encoding an internal user- and projectid in the state through a JWT only for this purpose. I need it for the callback to make sure that I'm connecting the right internal user with the QB token.
I have two questions here:
Is it "safe" to encode an internal userid in the "state" like that? Or is there a better way of doing what I want? I'm working with a distributed service (GCP CloudRun), so it might not be the same instance receiving the callback as the one that created the authUri.
I'm not an expert here, but a read of the spec seems to indicate this should not be a guessable value like user_id would be.
From the spec ( https://datatracker.ietf.org/doc/html/rfc6749#page-26 ):
state
RECOMMENDED. An opaque value used by the client to maintain
state between the request and callback. The authorization
server includes this value when redirecting the user-agent back
to the client. The parameter SHOULD be used for preventing
cross-site request forgery as described in Section 10.12.
and:
10.12. Cross-Site Request Forgery
Cross-site request forgery (CSRF) is an exploit in which an attacker
causes the user-agent of a victim end-user to follow a malicious URI
(e.g., provided to the user-agent as a misleading link, image, or
redirection) to a trusting server (usually established via the
presence of a valid session cookie).
A CSRF attack against the client's redirection URI allows an attacker
to inject its own authorization code or access token, which can
result in the client using an access token associated with the
attacker's protected resources rather than the victim's (e.g., save
the victim's bank account information to a protected resource
controlled by the attacker).
The client MUST implement CSRF protection for its redirection URI.
This is typically accomplished by requiring any request sent to the
redirection URI endpoint to include a value that binds the request to
the user-agent's authenticated state (e.g., a hash of the session
cookie used to authenticate the user-agent). The client SHOULD
utilize the "state" request parameter to deliver this value to the
authorization server when making an authorization request.
Once authorization has been obtained from the end-user, the
authorization server redirects the end-user's user-agent back to the
client with the required binding value contained in the "state"
parameter. The binding value enables the client to verify the
validity of the request by matching the binding value to the
user-agent's authenticated state. The binding value used for CSRF
protection MUST contain a non-guessable value (as described in
Section 10.10), and the user-agent's authenticated state (e.g.,
session cookie, HTML5 local storage) MUST be kept in a location
accessible only to the client and the user-agent (i.e., protected by
same-origin policy).
A CSRF attack against the authorization server's authorization
endpoint can result in an attacker obtaining end-user authorization
for a malicious client without involving or alerting the end-user.
The authorization server MUST implement CSRF protection for its
authorization endpoint and ensure that a malicious client cannot
obtain authorization without the awareness and explicit consent of
the resource owner.
Given that the spec recommends the state parameter be used for CSRF protection, and that the user_id is going to be easily guessable (i.e. the opposite of what you'd want for CSRF protection) it doesn't seem like a good idea to be putting the user_id into the state parameter.
Some other sources seem to confirm this:
https://auth0.com/docs/secure/attack-protection/state-parameters
https://medium.com/keycloak/the-importance-of-the-state-parameter-in-oauth-5419c94bef4c

How The Spring Security is redirecting to Login page if User is unauthorized

What is the Internal mechanism of authorization by spring-security?
How it is redirecting the URL to /Login if a user does not enters valid credentials?
Who is responsible for this Action?
What is the internal flow of project ?
All the important magic is in the security filter chain.
At some point some component checks whether the request is authenticated. Usually it is the last filter - FilterSecurityInterceptor (checking access conditions based on request path), but it can be MethodSecurityInterceptor (#Secured annotation check) or even your own custom code. The important thing is that this check will end in AuthenticationException or AccessDeniedException being thrown.
The next important component is ExceptionTranslationFilter. This filter checks if the thrown exception is AuthenticationException or AccessDeniedException and calls configured AuthenticationEntryPoint. Depending on the configured authentication mechanism, this might send redirect to login page or 401 response with WWW-Authenticate header (e.g. BASIC auth).
So the overall process for FORM based login looks like this (it might look like a mess, but it is actually pretty well designed and not that complex once you understand the principles):
unauthenticated user makes request to /foo.html
request is checked by FilterSecurityInterceptor against your security expression isAuthenticated() and throws AccessDeniedException
thrown exception is catched by ExceptionTranslationFilter, which in turn calls LoginUrlAuthenticationEntryPoint
entry point sends 301 redirect response to /login.html
user makes another request to /login.html
request is checked by FilterSecurityInterceptor and for this path, anonymous access is allowed
login form is presented to user
user fills the login form and submits it as POST /login.html
this request is intercepted by authentication processing filter UsernamePasswordAuthenticationFilter
username and password is extracted and AuthenticationManager is called to perform actual authentication (actual authentication is delegated to other components - usually DaoAuthenticationProvider and UserDetailsService)
authentication is successful and the Authentication token is marked as authenticated and put on SecurityContextHolder (so it is available to other components)
AuthenticationSuccessHandler is being called, which might just redirect user back to /foo.html
on the way up, the successful authentication is being picked up by SecurityContextPersistenceFilter (checks SecurityContextHolder), which stores authentication on the HTTP session
user makes yet another request to /foo.html
SecurityContextPersistenceFilter restores authentication from session onto SecurityContextHolder
FilterSecurityInterceptor checks your access rule again and allows further processing for the freshly authenticated request (i.e. dispatcher servlet is called)
user enjoys his /foo.html

DotNetOpenAuth OAuth2.0 state parameter

I'm using DotNetOpenAuth to connect to Facebook and Google via OAuth2. The OAuth specs ask that no additional parameters be supplied in the request_uri and Google actually enforces this somewhat by forcing to to specify an exact call back uri when you define your Google App with them.
What I want to accomplish is to be able to return the user to a specific URL after they have been authenticated with Facebook or Google. The flow is this, the user clicks on a protected link, they get forwarded to my login page with a returnUrl parameter and then I kick off the authorization process based on the OAuth2 authorization server they choose.
Since the request_uri can't have any parameters in it (though Facebook lets you get away with this), I can't send the returnUrl parameter to the authorization server and get it back such that when the user is returned to my site, I forward them to the protected page they were trying to access. The best I can do is to forward them to the homepage or a member welcome page.
The way to fix this is to use the "state" parameter which the authorization server will send back to the request_uri, but I can't find a way to specify this with DotNetOpenAuth.
By default, it looks like the code uses the SessionID as the state parameter to verify the request coming back from the authorization server. Specifying an IClientAuthorizationTracker on the WebServerClient class lets me plug in my logic when the response is coming back from the authorization server but it's not called when the authorization request is being prepared, so I can't plug in my additional state.
This is code from WebServerClient.cs's PrepareRequestUserAuthorization:
// Mitigate XSRF attacks by including a state value that would be unpredictable between users, but
// verifiable for the same user/session.
// If the host is implementing the authorization tracker though, they're handling this protection themselves.
if (this.AuthorizationTracker == null) {
var context = this.Channel.GetHttpContext();
if (context.Session != null) {
request.ClientState = context.Session.SessionID;
} else {
Logger.OAuth.WarnFormat("No request context discovered, so no client state parameter could be set to mitigate XSRF attacks.");
}
}
There is no else block here which is what I would have expected to be able to play along and plug in my own data.
Any tips on what I'm missing?
The state parameter is necessarily preoccupied with mitigating XSRF attacks. Since you already have a user session, can you just store the returnUrl in the session dictionary?
Alternatively, you can file an issue with DotNetOpenAuth asking that you be allowed to bundle your own data into the state parameter (along with DNOA's own XSRF mitigation code).

Securing the Forms Authentication Cookie when offloading SSL

I am attempting to secure a website I am currently developing using ASP.NET MVC 2.0 and forms authentication. In order to secure the forms authentication cookie I want to set the requiresSSL property to true so the cookie is only send by browsers when the connection is under SSL, and obviously ensure that all resources which require authorization are under SSL.
My problem is that we are using Application Request Routing to fulfil a number of functions, one being SSL offloading, therefore by the time a request hits any web server in our farm the request is no longer under SSL and the FormsAuthentication.SetAuthCookie method fails because an SSL connection is required to set the cookie when requiresSSL is specified.
Anyone have any ideas as to a work around here!
Thanks
So I have a work around for this, however if anyone has any better ideas please feel free to comment. Essentially you need to intercept the response at the end of the request and manually set the Secure property on the forms authentication cookie, pretty obvious really, you will also need to set the requireSSL property in the forms authentication configuration to false. Also bear in mind we do not want to enable HTTPS for the entire site for authenticated users hence this work around.
There are a couple of caveats to this approach and a few things to be aware of.
I found during testing that the forms authentication cookie was always written to in the response, so I kept overwriting the valid authentication cookie in the browser with an empty authentication cookie, to get around this I included some logic in the HTTP module to work around this, see code snippet below.
All requests to the application which require authorization must be under SSL, otherwise the request will not contain the authentication cookie in order to authenticate the user.
Because you are only passing the authentication cookie for SSL requests you will need another mechanism to tell your application that the current user is authenticated when they browse the non SSL areas of the site, I have implemented this with an additional cookie which is set when the user logs in, and does not have an expiry date set, so will expire at the end of the users session, of course this cookie is removed if the user logs out.
Below is the logic implemented in an HTTP Module to affect the above, I have been testing this the last couple of hours and have not come across any problems yet, I will be sure to update this post if I do!
We should only ever send an authentication cookie to the client if the user has just logged in here's the logic
If the request has an auth cookie the user is already authenticated
and under SSL so ensure we do not send a new auth cookie in the
response.
If the request does not have an auth cookie but there is a valid
auth cookie in the response, set the response auth cookie to secure,
so it is only transmitted by the browser under SSL.
If the request does not have an auth cookie and the response has an
invalid or empty auth cookie, ensure we remove the response cookie
so we dont overwrite the valid cookie in the client browser.
private void EndRequest(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
if (ValidRequest(application.Request) && application.Response.Cookies.Count > 0)
{
//only do the below if the user is not logging out the site, if the user is logging out we can
//leave the default forms authentication behaviour which is to expire the auth cookie
if (application.Request.AppRelativeCurrentExecutionFilePath != "~/authentication/logoff")
{
var requestAuthCookie = application.Request.Cookies[FormsAuthentication.FormsCookieName];
var responseAuthCookie = application.Response.Cookies[FormsAuthentication.FormsCookieName];
if (requestAuthCookie != null && responseAuthCookie != null && responseAuthCookie.Value.IsNullOrEmpty())
{
application.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
}
else if (responseAuthCookie != null && !responseAuthCookie.Value.IsNullOrEmpty())
{
responseAuthCookie.Secure = true;
application.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
application.Response.Cookies.Add(responseAuthCookie);
}
else if (responseAuthCookie == null || responseAuthCookie.Value.IsNullOrEmpty())
{
application.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
}
}
}
}
SSL offload should allow you to make a SSL connection from the SSL offloader to the web server.
The SSL connection from the SSL Offloader to the web server should use the lightest and fastest (and probably weakest) encryption available.
This allows you to use secure cookies, reduce the encryption load on the servers and avoid the modification of your code.

Resources