SM_USER for every requests from SIteminder (Static requests as well) - spring-boot

Siteminder sending SM_USER (userId) back to application for all requests including static resource requests. we dont need userId in every request header except for specified Url.
Can anyone help how can avoid this in Spring boot+Siteminder configuration?
below using Siteminder filter,
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(siteminderFilter(), RequestHeaderAuthenticationFilter.class)
......
......
}
#Bean
public RequestHeaderAuthenticationFilter siteminderFilter() throws Exception {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setPrincipalRequestHeader(smEmpID);
filter.setAuthenticationManager(authenticationManagerBean());
filter.setCheckForPrincipalChanges(true);
filter.setExceptionIfHeaderMissing(false);
return filter;
Thanks.

You can set DisableUserNameVars=No in your ACO and it will stop sending SM_USER
For the urls you need userid you can setup custom header with userid like HTTP_SM_USER.
Please note, as per documentation, using SM_USER inside the application is not advisable.
For few other applications (like integrating with SAP or other apps) you must have SM_USER, so there you have no escape.

Related

How to disable specific headers in Spring Boot

Is it possible to disable following headers in Spring Boot?
X-Forwarded-Host:
X-Host:
X-Forwarded-Server:
Following did not work for me
class MyFilter extends OncePerRequestFilter {
#Override
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
public void setHeader(String name, String value) {
if (!name.equalsIgnoreCase("X-Forwarded-Host")) {
super.setHeader(name, value);
}
}
});
}
Let's try to have a look broader and start to think about request-response lifecycle.
Once a request has been initiated by a client, there are sort of stops and layers that the request/response goes through between client and the application. There might be a firewall, load-balancer, reverse proxy, middleware etc. On the other hand, based on the application server which serves the application, those headers might be added as well. If there is a mechanism which adds or removes or rewrites the headers apart from the application, those headers should have been managed out of the application.
That being said, if headers were added by the application, they could have been managed within the application. But if headers were added by another stop or layer, they should have been managed in a particular configuration.
Apart from the headers in general, if we think about these specific headers: Based on my general experience, the headers you provided are added when there is a reverse proxy between client and application.
You can leverage more information about them: https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#x-headers
To sum up, you should be managing those headers according to how and why they have been added.
If you want to disable all default headers you can do the folowing:
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.headers(headers -> headers
// do not use any default headers unless explicitly listed
.defaultsDisabled()
.cacheControl(withDefaults())
);
return http.build();
}
}
To disable specific ones you can follow the same strategy.
Reference: https://docs.spring.io/spring-security/reference/5.8/servlet/exploits/headers.html#page-title

Integrate Spring Boot Security SAML with Azure AD Gallery app as multi tenant

I am Developing Java Spring Boot System and trying to Integrate with Azure non-gallery app using SAML Single Sign-On.
I found how to create Non-gallery applications, how to apply non-gallery app to Azure Gallery list etc. For example this link is about configuring SAML SSO:
Configure SAML-based single sign-on
So I understood Azure side configurations and procedures.
I am using Spring Security SAML Extensions. But I can't find the Spring Boot side configuration even I made a lot of research except official SAML Extension documentation which is XML based.
By the way, my main goal is adding our organization app to Azure gallery app list. Our app used by a multiple companies so if we add our organization app to Azure Gallery App list our customers can configure their Azure AD account as SSO integration.
My question is below:
How to integrate Azure Non-Gallery App to Spring Boot app?
How to handle multiple Azure AD tenants?
Is anybody help me with this?
EDIT:
Currently I made a single tenant SSO login with Spring Boot and Azure AD non-gallery app. I configured IdP metadata using Azure AD Federation XML Metadata URL. You can see source code below:
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Value("${security.saml2.metadata-url}")
private String IdPMetadataURL;
#Value("${server.ssl.key-alias}")
private String keyStoreAlias;
#Value("${server.ssl.key-store-password}")
private String keyStorePassword;
#Value("${server.port}")
String port;
#Value("${server.ssl.key-store}")
private String keyStoreFile;
#Autowired
private SAMLUserService samlUserService;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/saml/**", "/", "/login", "/home", "/about").permitAll()
.anyRequest().authenticated()
.and()
.apply(saml())
.webSSOProfileConsumer(getWebSSOProfileConsumerImpl())
.userDetailsService(samlUserService)
.serviceProvider()
.keyStore()
.storeFilePath(this.keyStoreFile)
.password(this.keyStorePassword)
.keyname(this.keyStoreAlias)
.keyPassword(this.keyStorePassword)
.and()
.protocol("https")
.hostname(String.format("%s:%s", "localhost", this.port))
.basePath("/")
.and()
.identityProvider()
.metadataFilePath(IdPMetadataURL)
.and();
}
public WebSSOProfileConsumerImpl getWebSSOProfileConsumerImpl(){
WebSSOProfileConsumerImpl consumer = new WebSSOProfileConsumerImpl();
consumer.setMaxAuthenticationAge(26000000); //300 days
return consumer;
}
}
From now I need to generate IdP Metadata XML instead of using IdP metadata URL. Using fields such as:
IdP Entity ID
IdP SSO URL
IdP Public certificate
The process is I am thinking about is:
Our customers register their Azure AD IdP fields above
My Spring Boot system automatically generate IdP Metadata XML
Then customer's Azure AD SSO can integrated to our system
If is there anything wrong please teach me out.
I'm using Spring Security SAML extension with Spring Boot. It's irrelevant which SAML IdP you are using as you only need the IdP meta data. You generate your SP meta data and use it as mentioned in the MS docs. You may check Spring Security SAML docs.
In order to list your application to Azure Gallery application list kindly go through the document. Kindly go through the entire procedure as mentioned in the document in order to list your application in azure Gallery.
Only for application which is already exists in the gallery the configuration side for the application is mentioned. For non-gallery application you need to configure the azure AD metadata value in the application end.
finally I did my solution for dynamic IDP. I used spring-boot-security-saml this simplified project.Thank you for ulisesbocchio this guy who implemented it.
Also big thanks to ledjon who shared me with his experience.
Here is how I'm configuring the saml part of the http security
http.apply(saml)
.serviceProvider()
.metadataGenerator()
.entityId(LocalSamlConfig.LOCAL_SAML_ENTITY_ID)
.entityBaseURL(entityBaseUrl)
.includeDiscoveryExtension(false)
.and()
.sso()
.successHandler(new SendToSuccessUrlPostAuthSuccessHandler(canvasAuthService))
.and()
.metadataManager(new LocalMetadataManagerAdapter(samlAuthProviderService))
.extendedMetadata()
.idpDiscoveryEnabled(false)
.and()
.keyManager()
.privateKeyDERLocation("classpath:/saml/localhost.key.der")
.publicKeyPEMLocation("classpath:/saml/localhost.cert")
.and()
.http()
.authorizeRequests()
.requestMatchers(saml.endpointsMatcher())
.permitAll();
The important part here is the
.metadataManager(new LocalMetadataManagerAdapter(samlAuthProviderService)) which is what we're trying to solve for here. The object samlAuthProviderService is a Bean-managed object and it contains the logic to actually retrieve the metadata from the database, so there's not a lot that is specially about it. But here is what my LocalMetadataManagerAdapter roughly looks like:
#Slf4j
public class LocalMetadataManagerAdapter extends CachingMetadataManager {
private final SamlAuthProviderService samlAuthProviderService;
public LocalMetadataManagerAdapter(SamlAuthProviderService samlAuthProviderService) throws MetadataProviderException {
super(null);
this.samlAuthProviderService = samlAuthProviderService;
}
#Override
public boolean isRefreshRequired() {
return false;
}
#Override
public EntityDescriptor getEntityDescriptor(String entityID) throws MetadataProviderException {
// we don't really want to use our default at all, so we're going to throw an error
// this string value is defined in the "classpath:/saml/idp-metadata.xml" file:
// which is then referenced in application.properties as saml.sso.idp.metadata-location=classpath:/saml/idp-metadata.xml
if("defaultidpmetadata".equals(entityID)) {
throw exNotFound("Unable to process requests for default idp. Please select idp with ?idp=x parameter.");
}
EntityDescriptor staticEntity = super.getEntityDescriptor(entityID);
if(staticEntity != null)
return staticEntity;
// we need to inject one, and try again:
injectProviderMetadata(entityID);
return super.getEntityDescriptor(entityID);
}
#SneakyThrows
private void injectProviderMetadata(String entityID) {
String xml =
samlAuthProviderService.getMetadataForConnection(entityID)
.orElseThrow(() -> exRuntime("Unable to find metadata for entity: " + entityID));
addMetadataProvider(new LocalMetadataProvider(entityID, xml));
// this will force a refresh/re-wrap of the new entity
super.refreshMetadata();
}
}
The important part here is the override of getEntityDescriptor() which will get called to get the metadata object at runtime. I'm also disabling refreshes by overriding isRefreshRequired() to return false. You can determine if this makes sense for your use case or not.
The referenced LocalMetadataProvider is just a wrapper class to store/return the xml string when required:
public class LocalMetadataProvider extends AbstractReloadingMetadataProvider {
private final String Id;
private final String xmlData;
public LocalMetadataProvider(String id, String xmlData) {
this.Id = id;
this.xmlData = xmlData;
setParserPool(LocalBeanUtil.getBeanOrThrow(ParserPool.class));
}
#Override
protected String getMetadataIdentifier() {
return this.Id;
}
#Override
protected byte[] fetchMetadata() throws MetadataProviderException {
return xmlData.getBytes();
}
}
Finally we can pass idp metadata entityID as a parameter. And retrieve entityID metadata from DB etc:
/saml/login?idp=X where X is the entityID value we want to get passed to getEntityDescriptor().

Spring Security that needs username and password on every request

What I wanted to do is build a Rest backend application that needs the Authorization header on every request, validate and return the data or 401 Unauthorized if the user or password is wrong.
I have Spring Security in my classpath with this simple configuration:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("user")
.password("pass")
.roles("USER");
}
}
But one thing is not working correctly: when I make a request with valid username and password with Postman, the server responds the data correctly, but if I change the password to a wrong password and keep the correct username, the server stills responds with the data and OK status, as if it is using a JSESSIONID Cookie to check the further requests.
Is that a way of using spring security only for checking the header Authorization (with no cookies, sessions nor saving user informations or login and logout pages), or is better to just use Filters for doing that instead?
Thank you for any help !
Just add the following to your configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
This will configure spring security to never create a cookie, every request must reauthenticate.

Spring Security OAuth - how to disable login page?

I want to secure my application with Spring Security, using OAuth 2. However, I don't want the server to redirect incoming unauthorized requests, but instead to respond with HTTP 401. Is it possible?
Example: this code redirects requests to a default login page.
application.properties
spring.security.oauth2.client.registration.google.client-id=...
spring.security.oauth2.client.registration.google.client-secret=...
AuthConfig.java
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login();
// https://stackoverflow.com/questions/31714585/spring-security-disable-login-page-redirect
// deos not work
// .and()
// .formLogin().successHandler((request, response, authentication) -> {});
}
}
You need to create new authentication entry point and set it in configuration.
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new AuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login();
}
}
public class AuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public AuthenticationEntryPoint() {
super("");
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendError(401, "Unauthorized");
}
}
You need to set oauth2Login.loginPage in your HttpSecurity config and create a controller mapping to return whatever you want. Here's a simple example.
So in your security config
http
.authorizeRequests()
.antMatchers("/noauth").permitAll()
.oauth2Login()
.loginPage("/noauth")
In a controller
#GetMapping("/noauth")
public ResponseEntity<?> noAuth() {
Map<String, String> body = new HashMap<>();
body.put("message", "unauthorized");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(body);
}
You can pass a map or pojo to the body method.
I would like to expand on Petr's answer by explaining that apparently for the time being first of all, the default login page is shown when there are more than one OAuth2 configured providers. I would expect that Spring Boot would have a smart trick to bypass this page easily and choose the right provider automatically, basing e.g. on the existence of the provider's client ID in the original request. I found out the hard way that this is not the case. So the way to do this is.. this not very apparent trick of providing a custom handler for failures - that will REDIRECT the user to the correct OAuth2 endpoint for each provider, based on the original HTTP request URL. I tried this and it works and I spent a whole day trying all manners of other solutions - my original scenario was to pass additional parameters to OAuth2 scheme in order to be able to get them back on successful authentication - they used to do this appending Base64 encoded information to the "state" URL request parameter, but Spring Security does not allow this at the moment. So the only alternative was to call a Spring Security-protected URL with those parameters already there, so when the successful authentication happens, this URL is accessed again automatically with those parameters intact.
Related: Multiple Login endpoints Spring Security OAuth2

configuring interceptors to perform "pre-tasks" on hitting endpoints in spring based web app

I am required to perform some pre-task whenever few specific endpoints are hit in my spring based web app. I came across the interceptor component provided by the spring-security framework. I searched various forums but didn't find relevant answers for configuring and adding interceptor.
Consider a situation where I am required to set some key=value in a database by sending POST request in the database whenever the user hits following endpoints.
/endpoint1
/endpoint2
/endpoint3
/endpoint4
After completion of the pre-task user should be redirected to the origin endpoint.
How can this be achieved using an interceptor in the spring-security framework?
Spring Security is for security stuff related to Authentification and Authorization. You can trigger some action if somebody logged in, but if you just need to trigger action for each request than Spring Security is not a good place for that (according to business logic), better add just filter. Anyway answering to your question:
The best way is to add custom filter to Spring Security Filter Chain:
You have to overwrite:
#Configuration
public class CustomWebSecurityConfigurerAdapter
extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(
new CustomFilter(), BasicAuthenticationFilter.class);
}
}
and create your custom filter:
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//your logic here
chain.doFilter(request, response); //it's needed to pass your request father
}
}
Code taken from baeldung.com see for more information

Resources