My system uses AngularJS 1.6 and Spring Boot2.0. My front end is just a simple form that customers can use to buy tickets for an event. When the page loads it will GET the details of the current active event, then the users can fill the form to be POSTed. No login/registration so it's just like a google form. I've built them as separate projects so they will be running in different ports. Now I want to enable csrf protection but I can't seem to make it work.
I tried to follow this tutorial but without the authentication part: https://spring.io/blog/2015/01/12/the-login-page-angular-js-and-spring-security-part-ii. When I did this I encountered a "CORS header ‘Access-Control-Allow-Origin’ missing" on the GET event details part so I added a CORS filter:
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type");
chain.doFilter(request, response);
After adding that, my GET works but I encounter the same CORS error when I POST the customer's details. I can already see the XSRF-TOKEN in the cookie when I use postman but somehow my backend blocks the incoming POST. From my understanding, angular automatically uses a received XSRF token so I didn't modify anything in the frontend when implementing spring security.
I've also tried this one: https://sdqali.in/blog/2016/07/20/csrf-protection-with-spring-security-and-angular-js/. And the exact same result, if I just follow the tutorial, CORS error on GET then when I add simple CORS filter, CORS error on POST. My filters seems to get mixed up on run time.
I've tried playing around the codes in these tutorials along with some answers to related questions here in stack but all of them don't have to deal with the CORS problem.
To begin, you should check my comment on how to enable CORS with spring-boot and spring-security.
As for CSRF protection, spring-boot 2 with spring-security enable that directly. Yet, to use it with AngularJS, you need to follow this guide:
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#csrf-cookie
We are also working on a project with a frontend AngularJS 1.6 and a backend spring-boot 2, but, as we didn't deploy our frontend and our backend on the same url, we got into a problem.
Example:
https://project.company.com/frontend
https://project.company.com/backend
The cookie generated by the server was set on the domain https://project.company.com with context /backend, which was unreadable from our frontend application in AngularJS.
To fix that, we forced our server to send a cookie with a context / so that it could be read from both applications.
#Configuration
#EnableWebSecurity
public class LdapAuthenticationSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
final CookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
csrfTokenRepository.setCookiePath("/"); // Make sure that the cookie can be read from the backend and the frontend on the same domain.
http.csrf().csrfTokenRepository(csrfTokenRepository);
http.cors();
}
}
In the case where your applications are deployed on different domains, but with the same base like this:
https://frontend.company.com
https://backend.company.com
I would suggest to force the cookie send by the server with a domain like .company.com. But to do so, you will need to modify the way spring-security manage the cookie creation.
Related
This question already has an answer here:
CORS Unauthorized 401 error when calling spring rest api
(1 answer)
Closed 3 years ago.
I'm trying to send http requests from my React client app to a RestfulApi (built with Spring), but I'm facing CORS issues.
As I understand it to solve cors issue there are several ways, 2 of them that I know:
1) Install a CORS extension on your browser (your browser will be exposed to security risks, but if it's only during development and it can be enabled/disabled then I can live with it)
2) Allow headers from your server rest API- I found a spring annotation called #CrossOrigin(origins = "", allowedHeaders = "")
https://howtodoinjava.com/spring5/webmvc/spring-mvc-cors-configuration/
Using the first approach:
First call that I perform is GET login request and it works, but I'm getting back an empty response with empty headers!
Second call is another GET request to get some projects details, but I'm getting 401!
From some investigation I understood that there is a need to send the browser sessionId (stored in cookies) and pass it to the HTTP request as param in order to be authorized... but didn't manage yet making it works.
I reached this page:
https://www.baeldung.com/spring-security-cors-preflight
Adding another class to my rest server app didn't help as well:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// ...
http.cors();
}
}
Install a CORS extension on your browser (your browser will be exposed to security risks, but if it's only during development and it can be enabled/disabled then I can live with it)
CORS extensions tend to just inject Access-Control-Allow-* headers into responses.
They don't do everything else that needs to be done to enable CORS.
In particular, they tend not to handle the preflight requests that need before credentials can be sent in a request. So:
the browser sends a preflight request asking for permission to make a request with credentials
the server responds with an Unauthorized error because the credentials weren't sent
the extension injects Access-Control-Allow-* headers
the browser denied access to the response to the JS because the response had an Unauthorised status (the Access-Control-Allow-* headers not being sufficient to override that)
Implement a real solution when you need CORS. Browser extensions are a waste of time because the will need replacing with real solutions in the end and only work in a subset of cases in the first place.
We have an intranet application we are working on with an AngularJS front end and a Web Api back end. It's basically a redesign of an existing ASP.NET/WCF app and it's only used by a few of our employees.
We had intended to enforce some security in this version (the old one had none) using Windows Authentication and we've had some problems getting that to work with browsers other than Internet Explorer. Chrome, for example always comes back with a 401-Unauthorized error.
I've been instructed to skip the security features for now (yeah I know, not a good idea) so I've disabled them but now I'm seeing an error when I attempt to talk to the API (in a separate project), but in Chrome and not in IE.
XMLHttpRequest cannot load http://localhost:62415/api/Emails. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:50900' is therefore not allowed access. The response had HTTP status code 401.
We've enabled CORS in our API project so I am not sure what I need to do here. We are using localhost in our local development but in QA and production these sites will be using URLs with different subdomains within the same domain.
I am using a Startup class in my project and it has this method:
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(config);
}
I must be missing something. We originally had this code in our WebApiConfig but restoring it didn't fix anything:
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
Any help is appreciated.
Don't need to add:
var cors = new EnableCorsAttribute("", "", "*");
config.EnableCors(cors);
For security reasons, browsers restrict cross-origin HTTP requests initiated from within scripts.
For example, XMLHttpRequest follows the same-origin policy. So, a web application using XMLHttpRequest could only make HTTP requests to its own domain.
However, You can pass that by:
install Microsoft.Owin.Cors package in nuget
add this method in Startup.cs
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
}
I am building a Portal using angularjs and ATG Rest API, It is giving an Error When I am trying to get Session confirmation Number using API:rest/model/atg/rest/SessionConfirmationActor/getSessionConfirmationNumber
Error:XMLHttpRequest cannot load http://IPNUMBER:Port/rest/model/atg/rest/SessionConfirmationActor/getSessionConfirmationNumber. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
API is working fine in POSTMAN, and from the direct browser query.
Please help me on this.
You best bet is to write a simple Pipeline servlet and add it to the RestPipeline configuration. The servlet would just inject the cors headers to all Rest requests.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import atg.servlet.*;
import atg.servlet.pipeline.*;
public class CORSHeaderServlet extends InsertableServletImpl{
public CORSHeaderServlet () {}
public void service (DynamoHttpServletRequest request,
DynamoHttpServletResponse response)
throws IOException, ServletException
{
//add headers to response.
response.addHeader("Access-Control-Allow-Origin" ,"*");
passRequest (request, response);
}
}
I didn't use this API, but problem is quite common. Have a look for example here (or any other source about CORS):
How does Access-Control-Allow-Origin header work?
If your web application and service have different domains (origins), this will not work until the service allows your application to request data. When you use Postman it works, because Postman does not send the header or uses origin, which is allowed. I don't really know how it works, but it does and it's normal.
If you are using locally hosted application just for testing purposes and both service and app will have the same origin, you have two easy solutions:
You can run web browser (e.g. Chrome) with web security disabled:
Disable same origin policy in Chrome. This disables CORS and eliminates the problem.
You can install Chrome extension called Allow-Control-Allow-Origin: *. When it's enabled, it sends origin which will be allowed by the service.
However, if your service will have different origin, then you will have to configure it to allow your application to request it.
Edit
Note one thing. If you send a request different than GET or with some custom headers, browsers will firstly send an OPTIONS request. It's called preflight request. You're service will have to handle it in order to work properly.
I read a lot about Spring Securitys CSRF protection, but i still struggle a little bit. Now the documentation is great as usual, but it's completely based on the idea that you render html code on the server and are able to add a hidden field to every form. Now since i use AngularJS and JavaScript to call the backend this is not really an option.
So what is the best way to actually get the Token to the client in this case (Rest Backend / AngularJS frontend)? AngularJS seems to have built in support for CSRF in $resource and expects a Cookie called "XSRF-TOKEN" to retrieve the Token and send it as http header "X-XSRF-TOKEN" in further requests. So every request will contain the http header, as well as the cookie. Now on server side i could read the header and compare it to the Token i stored in the session.
The problem i have with this, it that it seems a bit complicated. Since the login itself has to be protected it would require creating a temporary session, just for the CSRF token. Is this really necessary?
Maybe this is just a stupid question, but why can't i just create a random-token on client side and set it as HTTP header and cookie on client side. This would be similar to "OWASP double submit cookie", but generate the Token on client-side. That way the server would not require to have a session before login, since he could just compare the 2 submitted tokens. Now while the attacker could send the HTTP header, he would per same-origin-policy have no way of reading or setting the cookie and could not get a match as long as the number is practically unguessable.
Now instinctly generating a secure token on client side seems dangerous to me and i guess i coul avoid it.. but WHY? I feel like i am missed something, surely there is a good reason why SpringSecurity stores the token in the session, right?
Please enlighten me :)
I ended up using spring-security-csrf-token-interceptor-extended, which reads the CSRF-Token from the http-header "X-CSRF-TOKEN" (name is configurable) and sends it as http-header on further requests.
Now the only thing i had to to was getting Spring-Security to send the Token as HTTP Header (since i don't render html code on serverside and therefor can't add it as a hidden field).
<security:http ....
<security:custom-filter ref="csrfTokenResponseHeaderBindingFilter" after="CSRF_FILTER"/>
....
</security:http>
The filter basically runs after the normal CSRF_FILTER and reads the "_csrf" request-attribute (which is put there by CSRF_FILTER) and sets it as header "X-CSRF-TOKEN"
public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter {
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
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 token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
if (token != null) {
response.setHeader(RESPONSE_TOKEN_NAME, token.getToken());
}
filterChain.doFilter(request, response);
}
}
I'm building a RESTful API with the Restlet framework and need it to work with cross domain calls (CORS) as well as basic authentication.
At the moment I'm using the CorsFilter which does the job of making my webservice support CORS requests. But, when I try to use this with a simple ChallengeAuthenticator with HTTP Basic Authentication it won't work as I want it to (from a web site).
When I access the webservice directly via Chrome it works as intended, but when I try it in a small web application written in angularjs (jquery/javascript) and try to access the webservice it does not.
Basically what happens is that when a OPTIONS request is sent to my webservice it will not respond with the headers: 'Access-Control-Allow-Origin', 'Access-Control-Allow-Credentials', etc. as it should. Instead it is sending a respond with HTTP status code 401 saying that the authentication failed.. Is this because the authenticator is overriding the CorsFilter somehow?
My createInboundRoot method can be seen below.
#Override
public Restlet createInboundRoot() {
ChallengeAuthenticator authenticator = createAuthenticator();
RoleAuthorizer authorizer = createRoleAuthorizer();
Router router = new Router(getContext());
router.attach("/items", ItemsServerResource.class);
router.attach("/items/", ItemsServerResource.class);
Router baseRouter = new Router(getContext());
authorizer.setNext(ItemServerResource.class);
authenticator.setNext(baseRouter);
baseRouter.attach("/items/{itemID}", authorizer);
baseRouter.attach("", router);
// router.attach("/items/{itemID}", ItemServerResource.class);
CorsFilter corsFilter = new CorsFilter(getContext());
corsFilter.setNext(authenticator);
corsFilter.setAllowedOrigins(new HashSet(Arrays.asList("*")));
corsFilter.setAllowedCredentials(true);
return corsFilter;
}
(The authorizer and authenticator code is taken from the "official" restlet guide for authorization and authentication)
I've tried alot of changes to my code but none which given me any luck. But I noticed that when setting the argument "optional" in ChallengeAuthenticator to true (which "Indicates if the authentication success is optional") the CorsFilter does its job, but obviously the ChallengeAuthenticator does not care about authenticating the client and lets anything use the protected resources..
Has anyone had a similar problem? Or have you solved this (CORS + Authentication in Restlet) in any other way?
Thanks in advance!
I think that it's a bug of the Restlet CORS filter. As a matter of fact, the filter uses the method afterHandle to set the CORS headers. See the source code: https://github.com/restlet/restlet-framework-java/blob/4e8f0414b4f5ea733fcc30dd19944fd1e104bf74/modules/org.restlet/src/org/restlet/engine/application/CorsFilter.java#L119.
This means that the CORS processing is done after executing the whole processing chain (authentication, ...). So if your authentication failed, you will have a status code 401. It's actually the case since CORS preflighted requests don't send authentication hints.
For more details about using CORS with Restlet, you could have a look at this link: https://templth.wordpress.com/2014/11/12/understanding-and-using-cors/. This can provide you a workaround until this bug was fixed in Restlet itself.
I opened an issue in Github for your problem: https://github.com/restlet/restlet-framework-java/issues/1019.
Hope it helps,
Thierry
The CorsService (in 2.3.1 coming tomorrow) contains also a skippingResourceForCorsOptions property, that answers directly the Options request without transmitting the request to the underlying filters and server resources.