Springboot REST - Expose same service as secured & non-secured - rest

I want to enable basic authentication for my Spring https REST services(currently unsecured) in springboot application. Multiple clients are consuming these services, while some are ok to move to secured one, some still want to consume the unsecured version for few more months. How can I expose same service as both secured & unsecured in same spring boot application?
Though I had done this for Apache cxf REST services by hosting same service in different ports & securing only one port, don't know how to accomplish this in springboot.

Create RequestMapping with two endPoints as below. Clients who want to use Basic Authentication will be served using /secure/** (can't be accessed without Authentication ) and others who are going to migrate to secure after a few months will be served using /unsecure/** (anyone can access). You can use class level RequestMapping to avoid change in every endPoint at method level
#GetMapping(value= {"/secure/users","/unsecure/users"})
public ResponseEntity<List<User>> findAllUsers()
{
...
}
Now Configure the Security as below. for that, you need Client Roles stored in DB
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/unsecure/**").permitAll()
.antMatchers("/secure/**").hasRole("CLIENT_SECURE")
.anyRequest().authenticated();
}
Working Git Example
Secure endPoint: GET http://localhost:8088/secure/users Status 403
UnSecure endPoint: GET: http://localhost:8088/unsecure/users Status 200

Related

Whitelist web application for API access without API key

We're developing a web application (SPA) consisting of the following parts:
NextJS container
Django backend for user management
Data API (FastAPI) protected with API keys, for which we also provide 3rd party access
The NextJS container uses an API key to access the data API. We don't want to expose the API key to the client (browser), so the browser sends the API requests to the NextJS container, which then relays it to the data API, see here. This seems secure, but is more complicated and slower than sending requests from the browser to the data API directly.
I'm wondering if it's possible to whitelist the web application in the data API, so that the client (browser) can call the data API directly without API key, but 3rd parties can't. FastAPI provides a TrustedHostMiddleware, but it's insecure because it's possible to spoof the host header. It has been suggested to whitelist IPs instead, but we don't have a dedicated IP for our web application. I looked into using the referer header, but it's not available in the FastAPI request object for some reason (I suspect some config problem in our hosting). Also, the referer header could be spoofed as well.
Is there even a safe way to whitelist our web application for data API access, or do we need to relay the request via NextJS container and use an API key?
Is there even a safe way to whitelist our web application for data API
access,
No, you need in all case an Authentication mechanism, something before the backend that check if the client is an authorize client.
The simplest pattern is using the NextJS container as the proxy. Proxy that have an api-key to call the backend ( what you are currently doing ).
There is many way to implement a secured proxy for a backend, but this authentication logic should not be inside the backend but in a separate service ( like envoy , nginx ... )

How to use JWT Auth0 token for Cloud Run Service to Service communication if the Metaserver Token is overriding the Auth0 Token

Prerequisites
I have two Cloud Run services a frontend and a backend. The frontend is written in Vue.js/Nuxt.js and is using a Node backend therefore. The backend is written in Kotlin with Spring Boot.
Problem
To have an authenticated internal communication between the frontend and the backend I need to use a token thttps://cloud.google.com/run/docs/authenticating/service-to-service#javahat is fetched from the google metaserver. This is documented here: https://cloud.google.com/run/docs/authenticating/service-to-service#java
I did set it all up and it works.
For my second layer of security I integrated the Auth0 authentication provider both in my frontend and my backend. In my frontend a user can log in. The frontend is calling the backend API. Since only authorized users should be able to call the backend I integrated Spring Security to secure the backend API endpoints.
Now the backend verifies if the token of the caller's request are valid before allowing it to pass on to the API logic.
However this theory does not work. And that simply is because I delegate the API calls through the Node backend proxy. The proxy logic however is already applying a token to the request to the backend; it is the google metaserver token. So let me illustrate that:
Client (Browser) -> API Request with Auth0 Token -> Frontend Backend Proxy -> Overriding Auth0 Token with Google Metaserver Token -> Calling Backend API
Since the backend is receiving the metaserver token instead of the Auth0 Token it can never successfully authorize the API call.
Question
Due the fact that I was not able to find any articles about this problem I wonder if it's simply because I am doing it basically wrong.
What do I need to do to have a valid Cloud Run Service to Service communication (guaranteed by the metaserver token) but at the same time have a secured backend API with Auth0 authorization?
I see two workarounds to make this happen:
Authorize the API call in the Node backend proxy logic
Make the backend service public available thus the metaserver token is unnecessary
I don't like any of the above - especially the latter one. I would really like to have it working with my current setup but I have no idea how. There is no such thing like multiple authorization token, right?
Ok I figured out a third way to have a de-facto internal service to service communication.
To omit the meta-server token authentication but still restrict access from the internet I did the following for my backend cloud run service:
This makes the service available from the internet however the ingress is preventing any outsider from accessing the service. The service is available without IAM but only for internal traffic.
So my frontend is calling the backend API now via the Node backend proxy. Even though the frontend node-backend and the backend service are both somewhat "in the cloud" they do not share the same "internal network". In fact the frontend node-backend requests would be redirected via egress to the internet and call the backend service just like any other internet-user would do.
To make it work "like it is coming from internal" you have to do something similar like VPN but it's called VPC (Virtual Private Cloud). And luckily that is very simple. Just create a VPC Connector in GCP.
BUT be aware to create a so called Serverless VPC Access (Connector). Explained here: https://cloud.google.com/vpc/docs/serverless-vpc-access
After the Serverless VPC Access has been created you can select it in your Cloud Run Service "Connection" settings. For the backend service it can be simply selected. For the frontend service however it is important to select the second option:
At least that is important in my case since I am calling the backend service by it's assigned service URL instead of a private IP.
After all that is done my JWT token from the frontend is successfully delivered to the backend API without being overwritten by a MetaServer token.

Spring MVC with Security & REST endpoint gives a 403 "Could not verify the provided CSRF token because your session was not found"

I have a Java Spring-boot microservice where I have an admin website and REST endpoints. The website is accessed at myserver.com/serverwebpages and the REST web service is at myserver.com/api/restendpoints. I am using Spring MVC with login security and CSRF on the web side and HMAC Authorization on the REST endpoint side. I have #Controller annotation on the website side and #RestController annotation for the REST web service. The website works fine and the webservice works fine with GET's. However, I just tried a DELETE and got a 403, error:forbidden, message:"Could not verify the provided CSRF token because your session was not found". It looks like the MVC security is seeing REST webservice requests and calling out the CSRF error because I have not provided a CSRF number. Of course, I do provide CSRF's on the web page side for MVC cross-scripting security. Anyone know how to fix this? Do I need to create a separate microservice with a different port?
When you call DELETE method, they need CSRF token. Maybe your DELETE API doesn't carry CSRF token. You need to add CSRF token in Request Header.
Adding CSRF in Header is depends on Ajax framework. If your combination is Spring-Security + Thymeleaf + jQuery, then refer Spring Security Document : https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-include-csrf-token-ajax
I could not figure out a solution to the problem using Spring MVC. So I just created 2 separate microservices on the same AWS instance. One microservice with port number 443 handles the web services and uses Spring MVC. The other microservice with port 8443 handles all of the REST API calls with no Spring MVC. This worked perfectly. Disappointed that I could not find an answer but had to move on.

How to secure REST APIs in Spring Boot web application?

I have two Spring Boot web applications. Both applications have different databases and different sets of users. Also, both applications use Spring Security for authentication and authorisation which works properly.
At any given point I will have one instance of the first application running and multiple instances of the 2nd web application running.
I want to expose REST APIs from 1st web application (one instance running) and be able to use that REST APIs from 2nd web application (multiple instances running).
How do I make sure that REST APIs can be accessed securely with proper authentication and by instances of the 2nd applications only.
If you could change your security, I would recommend you to use OAUTH2. Basically it generates a token that is used in your APP2 instances to make the API calls.
You can see more here.
https://spring.io/guides/tutorials/spring-boot-oauth2/
http://websystique.com/spring-security/secure-spring-rest-api-using-oauth2/
But if you can't change your APP's security, you can continue using your current schema. In the APP1 you can create an user for the API calls, this user only has access to the API services. In your APP2 you need to store the credentials to access the APP1. Finally you do login into APP1 and invoke the API using HTTP client, you can use Spring RestTemplate or Apache HttpComponents Client.
SSL based authentication could be an option, if you seriously thinking about the security aspects.
Assume that you REST api exposed by App 1 is over HTTPs, then you can configure the App 1 to ask the client to give their SSL/TLS certificate when they try to access this REST API (exposed by App 1).
This will help us identify that the client is indeed a client from app 2.
Two More Cents:
In case if your App 1 REST API calls needs load balancing, NGINX should be your chose. The SSL client certificate based authentication can be offloaded to NGINX and Your Spring boot app no more worry about the SSL related configurations.
The solution we went with was to secure both using an OAuth2 client_credentials workflow. That is the OAuth2 flow where clients request a token on behalf of themselves, not a calling User.
Check out Spring Cloud Security
1) Secure your services using #EnableResourceServer
#SpringBootApplication
#EnableResourceServer
public class Application ...
2) Make calls from one service to another using an OAuth2RestTemplate
Check out Resource Server Token Relay in http://cloud.spring.io/spring-cloud-security/spring-cloud-security.html which will specify how to configure an Oauth2RestTemplate to forward on security context details (token) from one service to another.
3) Service A and Service B should be able to communicate using these techniques if they are configured using the same Oauth2 Client and Secret. This will be configured in the applications' application.properties file, hopefully injected by the environment. Oauth2 Scopes can be used as role identifiers. You could therefore say that only a Client with Scopes (api-read, api-write) should have access to Endpoint A in Service A. This is configurable using Spring Security's Authorization configuration as well as #EnableGlobalMethodSecurity

Call REST service while impersonating a user that is already authorized to the glasfish server

There are two web-applications deployed on a glassfish server.
Both web applications provide a REST web service.
the access to both web-services is secured via glassfish security constraints (at the moment BASIC Auth and file-realm).
Let's say a user is accessing the service of web application A. After he is authorized, service A wants to call service B via REST client.
Is there a way for a service to impersonate a user that is already authorized to the glasfish server? Maybe something like forwarding the security context or editing the headers? Is there another Filter?
#Context
private SecurityContext securityContext;
username = securityContext.getUserPrincipal().getName();
password = ???
client.addFilter(new com.sun.jersey.api.client.filter.HTTPBasicAuthFilter(username, password));
Thanks!
It will depend on the authentication scheme, but in some cases you could consider extracting the Authorization header received by Service A and pass it to Service B. That would work with basic authentication.
You should be able to use #HeaderParam to do that, like in the following snippet:
#GET
#Path("/resourceA")
public void doSomething(#HeaderParam("Authorization") String authorization) {
// now I can call resourceB and use 'authorization'
}