InResponseToField of the Response doesn't correspond to sent message: SAML error SpringSecurity - 4.2.13-RELEASE - spring

My web application is deployed on Amazon ECS and uses an ALB and access this application from a bastion host. I am using Okta for SSO. The login page is redirected successfully to Okta and after authentication when the request comes back to the application server, I get the following error -
Caused by: org.opensaml.common.SAMLException: InResponseToField of the Response doesn't correspond to sent message a491gda80cgh3a2b5bb3j8ebd515d2
at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:139)
I am using a CustomSAMLContextProvider and setting the MessageStorageFactory to EmptyStorageFactory as suggested in other answers.
I am not sure why this check is still happening.
Here is my custom SAMLContextProviderImpl class -
public class SAMLMultipleEndpointContextProvider extends SAMLContextProviderImpl {
/**
* Creates a SAMLContext with local entity values filled. LocalEntityId is set to server name of the request. Also
* request and response must be stored in the context as message transports.
*
* #param request request
* #param response response
* #return context
* #throws MetadataProviderException in case of metadata problems
*/
#Override
public SAMLMessageContext getLocalEntity(HttpServletRequest request, HttpServletResponse response) throws MetadataProviderException {
SAMLMessageContext context = new SAMLMessageContext();
populateGenericContext(request, response, context);
populateLocalEntityId(context, request.getServerName());
populateLocalContext(context);
return context;
}
/**
* Creates a SAMLContext with local entity and peer values filled. LocalEntityId is set to server name of the
* request. Also request and response must be stored in the context as message transports. Should be used when both
* local entity and peer entity can be determined from the request.
*
* #param request request
* #param response response
* #return context
* #throws MetadataProviderException in case of metadata problems
*/
#Override
public SAMLMessageContext getLocalAndPeerEntity(HttpServletRequest request, HttpServletResponse response) throws MetadataProviderException {
SAMLMessageContext context = new SAMLMessageContext();
populateGenericContext(request, response, context);
populateLocalEntityId(context, request.getServerName());
populateLocalContext(context);
populatePeerEntityId(context);
populatePeerContext(context);
return context;
}
/**
* Populate LocalEntityId with retrieved entityId from metadata manager using given localAlias parameter value.
*/
#Override
public void populateLocalEntityId(SAMLMessageContext context, String localAlias) throws MetadataProviderException {
String entityId = metadata.getEntityIdForAlias(localAlias);
QName localEntityRole = SPSSODescriptor.DEFAULT_ELEMENT_NAME;
if (entityId == null) {
throw new MetadataProviderException("No local entity found for alias " + localAlias + ", verify your configuration.");
} else {
logger.debug("Using SP {} specified in request with alias {}", entityId, localAlias);
}
context.setLocalEntityId(entityId);
context.setLocalEntityRole(localEntityRole);
}
/**
* Disable the check for InResponseToField from SSO message response.
*/
#Override
public void setStorageFactory(SAMLMessageStorageFactory storageFactory) {
super.setStorageFactory(new EmptyStorageFactory());
}
}

In order to comply with the rules defined in the SAML spec, the SAML response has to be validated against the SAML AuthNRequest in SP-initiated SSO flow. By default Spring SAML stores the SAML AuthNRequest in memory, hence the HTTP POST request containing the SAML response as payload MUST hit the same JVM where the AuthNRequest was created. If the LB can not guarantee stickiness, then you need to implement a message store ( org.springframework.security.saml.storage.SAMLMessageStorage , org.springframework.security.saml.storage.SAMLMessageStorageFactory) that can share the messages with multiple instances. Make sure that you delete the message from the store after consumption to circumvent replay attacks as SAML resonses are meant for one-time-usage.

Related

Spring security 6.0 AuthorizationFilter - questionable default for shouldFilterAllDispatcherTypes

I spent a few hours today on a migration issue to Spring security 6.0 by replacing the deprecated authorizeRequests() method with authorizeHttpRequests(). I learned that under the hood, this implies replacing the FilterSecurityInterceptor with the new AuthorizationFilter in the security chain.
However, I got some unexpected results already for my unauthenticated register endpoint, that uses a JPA-validated #Valid request body and also answers with BadRequest = 400, if you try to register a user that already exists in the database.
When moving towards AuthorizationFilter, a valid register request still worked as expected, but the error cases (validation failure as well as already existing user) both replied with Unauthorized = 401, which is not acceptable for an unauthenticated endpoint...
I could solve this (eventually !) by chaining
.shouldFilterAllDispatcherTypes(false)
to authorizeHttpRequests().
But now I started to wonder, if the new default behaviour makes sense...
The rather unspectacular code snippets are:
The controller mapped call, where the service can throw a #ResponseStatus(HttpStatus.BAD_REQUEST) annotated UserAlreadyExistsException:
#PostMapping("/api/register")
public ResponseEntity<Void> registerUser(#Valid #RequestBody UserDto userDto) {
service.registerUser(mapper.toEntity(userDto));
return ok().build();
}
The relevant part of the SecurityFilterChain bean:
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http,
AuthenticationManager authenticationManager) throws Exception {
http.authenticationManager(authenticationManager)
//.authorizeRequests() <-- deprecated, but working, using SecurityFilterInterceptor
.authorizeHttpRequests()
.shouldFilterAllDispatcherTypes(false) // without this line weird behavior since default is true
.requestMatchers(HttpMethod.POST,"/api/register").permitAll()
// ... more requestMatchers and other stuff
}
So I digged deeper into the AuthorizationFilter - and there already the Javadoc is contradictory, if you look at the following snippet from AuthorizationFilter of spring security 6.0.1. The default of the first, new method contradicts the 3 method defaults below:
/**
* Sets whether to filter all dispatcher types.
* #param shouldFilterAllDispatcherTypes should filter all dispatcher types. Default
* is {#code true}
* #since 5.7
*/
public void setShouldFilterAllDispatcherTypes(boolean shouldFilterAllDispatcherTypes) {
this.observeOncePerRequest = !shouldFilterAllDispatcherTypes;
this.filterErrorDispatch = shouldFilterAllDispatcherTypes;
this.filterAsyncDispatch = shouldFilterAllDispatcherTypes;
}
//...
/**
* Sets whether this filter apply only once per request. By default, this is
* <code>true</code>, meaning the filter will only execute once per request. Sometimes
* users may wish it to execute more than once per request, such as when JSP forwards
* are being used and filter security is desired on each included fragment of the HTTP
* request.
* #param observeOncePerRequest whether the filter should only be applied once per
* request
*/
public void setObserveOncePerRequest(boolean observeOncePerRequest) {
this.observeOncePerRequest = observeOncePerRequest;
}
/**
* If set to true, the filter will be applied to error dispatcher. Defaults to false.
* #param filterErrorDispatch whether the filter should be applied to error dispatcher
*/
public void setFilterErrorDispatch(boolean filterErrorDispatch) {
this.filterErrorDispatch = filterErrorDispatch;
}
/**
* If set to true, the filter will be applied to the async dispatcher. Defaults to
* false.
* #param filterAsyncDispatch whether the filter should be applied to async dispatch
*/
public void setFilterAsyncDispatch(boolean filterAsyncDispatch) {
this.filterAsyncDispatch = filterAsyncDispatch;
}
Even worse, there seems to be a related vulnerability to bypass authorization as described in the link below, if you use the default. So I am wondering, if the default=true for shouldFilterAllDispatcherTypes is making sense - or do I miss a point here?
https://security.snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKSECURITY-3092126
I'm not sure that answers your question but you are using AuthorizationManagerRequestMatcherRegistry and you are looking at AuthorizationFilter. Check this link here:
source
/**
* Sets whether all dispatcher types should be filtered.
* #param shouldFilter should filter all dispatcher types. Default is {#code true}
* #return the {#link AuthorizationManagerRequestMatcherRegistry} for further
* customizations
* #since 5.7
*/
public AuthorizationManagerRequestMatcherRegistry shouldFilterAllDispatcherTypes(boolean shouldFilter) {
this.shouldFilterAllDispatcherTypes = shouldFilter;
return this;
}

How to access the request body in SpringBoot's AccessDecisionVoter?

So we have a Authorisation server with which we create OAuth2 access token. All sub-systems verify the access token and may check the request path for permissions, however, in one of the sub-systems we need to look into the request body and extract the 'id' to check if the user has proper permission to submit the request. The request message is in JSON format and this is a POST request with client-provided id.
The id in the request is a process id and some users may not have right permission to some processes therefore we need the id to verify.
So while in AccessDecisionVoter, we only can get request URI but I can't get HttpServletRequest to read the message. (Note: We have a Request wrapper that allows us to read request body multiple times)
I tried to auto-wire HttpServletRequest, no luck. There is an error that no thread has been bound to the request
I was also thinking about implementing UserDetailService but again no luck as this is not being invoked by Spring boot. Remember that we are using a custom AuthorizationServerTokenServices and that is in a common library.
How do I get Http servlet request or the request body in AccessDecisionVoter?
You should be able to implement an AccessDecisionVoter<FilterInvocation> where you can get the request. Does this not work:
public class MyAccessDecisionVoter implements AccessDecisionVoter<FilterInvocation> {
#Override
public boolean supports(ConfigAttribute attribute) {
return false;
}
#Override
public boolean supports(Class<?> clazz) {
return true;
}
#Override
public int vote(Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes) {
int result = ACCESS_ABSTAIN;
fi.getRequest() // this is the request
// decide the outcome and set result
return result;
}
}

Spring Social facebookConnected.jsp

After successful authentication facebook redirects to facebookConnected.jsp. I want it to be redirected to the home page instead. How can it be achieved? I tried doing with controller to redirect but didn't work well.
In ConnectController you got these methods
/**
* Returns the view name of a page to display for a provider when the user is connected to the provider.
* Typically this page would allow the user to disconnect from the provider.
* Defaults to "connect/{providerId}Connected". May be overridden to return a custom view name.
* #param providerId the ID of the provider to display the connection status for.
*/
protected String connectedView(String providerId) {
return getViewPath() + providerId + "Connected";
}
/**
* Returns a RedirectView with the URL to redirect to after a connection is created or deleted.
* Defaults to "/connect/{providerId}" relative to DispatcherServlet's path.
* May be overridden to handle custom redirection needs.
* #param providerId the ID of the provider for which a connection was created or deleted.
* #param request the NativeWebRequest used to access the servlet path when constructing the redirect path.
*/
protected RedirectView connectionStatusRedirect(String providerId, NativeWebRequest request) {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
String path = "/connect/" + providerId + getPathExtension(servletRequest);
if (prependServletPath(servletRequest)) {
path = servletRequest.getServletPath() + path;
}
return new RedirectView(path, true);
}
Concretely they say that methods could be overriden to get our specific redirect url. But honestly I don't know how overriding them. I guess you could create a new #Config class that extends it and override #RequestMapping("/connect") too.
If you know some way please write here because I'm on the same situation because I use Spring Webflow and I would like maintaining the flow.
In my case controller /WEB-INF/connect/facebookConnected.xhtml which throw Not Found in ExternalContext as a Resource because I configured my app to look for on folder /WEB-INF/flows.
I had the same problem and I fixed the issue by overriding the ConnectController class
#Controller
#RequestMapping("/connect")
public class CustomController extends ConnectController {
public CustomController(ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
super(connectionFactoryLocator, connectionRepository);
}
#Override
protected String connectedView(String providerId) {
return "redirect:/facebook";
}
}
This link has more explanation about how to change the default spring social redirect flow .

Apache Shiro: How to enforce password change?

I've got a Java EE 7 (JSF, JPA) and CDI based application running, using Shiro for both Authentication and Authorization.
I've got the requirement, that users have to change their password after a certain amount of time (customizable by the application's admin, i.e. 30 days). In our User table we store the information when the password was last set and thus can calculate on Login if it's time to do so.
The plan is to redisplay the login page and represent a different form (change password instead of login). So far so good. However:
How can I enforce the password change and not letting the user navigate to a different page?
Is there a recommended (or even built in) solution?
My idea would be to implement a filter, that checks the session-scoped login object on whether the PW needs to be reset or not.
The hope would be, that this it as simple as creating a new filter, injecting login there and checking the state of the flag - and redirecting the user to the login page as long as flag is true/he does not update his pw.
(We already have a custom cdi aware EnvironmentLoaderListener in place to support our JPA realm.)
The new filter would go behind the last line?
[urls]
/javax.faces.resource/** = anon
/layout.xhtml = anon
/css/** = anon
/login.xhtml = user
/logout.xhtml = logout
/** = user
So we have:
/** = user,pwresetfilter
Suggestions on details as well as on the overall solution are welcome.
You can do with your solution, but probably better to have like that:
You make you own realm
MyRealm extends AuthorizingRealm {
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
}
}
Here in doGetAuthenticationInfo you will check credentials and throw exception if password need to be changed. Feel free to extend realm currently in use.
In your EnvironmentLoaderListener you register your realm
May be in the end you would need to have filter to redirect to correct page, or if you use REST(like Jersey), you could have an exception mapper which will response something to your browser client
I had a similar requirement that was for OTP after authentication and i used normal filter to filter out all requests.
MAke a attribute in use bean like lastPasswordChangedDate or may be a isPasswordChangerequired as you like. and compare it in filter.
My simple otpFliter Code is as follows but you can make your own according to need like jsf, etc.:
/**
* Servlet Filter implementation class OTPFilter
*/
#WebFilter(urlPatterns = {"/*"},initParams={#WebInitParam(name="enabled",value="0")})
public class OTPFilter implements Filter {
/**
* Default constructor.
*/
boolean enabled=true;
public OTPFilter() {
// TODO Auto-generated constructor stub
}
/**
* #see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* #see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
// place your code here
// pass the request along the filter chain
//System.out.println(enabled);
if(enabled){
if(SecurityUtils.getSubject().getPrincipal()!=null){
if(request instanceof HttpServletRequest ){
HttpSession session = ((HttpServletRequest) request).getSession();
LoggedInUser user = (LoggedInUser) session.getAttribute("userinfo");
String url = ((HttpServletRequest) request).getRequestURL().toString();
//System.out.println("url is "+ url);
if( !url.contains("public") && !user.isOTPverified()){
if(user.getOTP() == null)
{
user.setOTP(OTPUtils.generateOTP());
}
//user.setOTPverified(true);
((HttpServletRequest) request).getRequestDispatcher("OTP.jsp").forward(request, response);
return;
}
}
}
}
chain.doFilter(request, response);
}
/**
* #see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
//System.out.println("fConfig.getInitParameter :" + fConfig.getInitParameter("enabled"));
enabled = fConfig.getInitParameter("enabled").equals("1");
}
}

How to get request attributes in authentication-success-handler

I am trying to do few things in authentication-success-handler and I need to access few values which was part of initial request data being posted to Spring security.
I am posting following information when user trying to do login
j_username
j_password
storeCode
Spring security is able to authenticate user successfully and is calling "authentication-success-handler".
public class WebshopAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler
{
public WebshopAuthenticationSuccessHandler() {
}
#Override
public void onAuthenticationSuccess(final HttpServletRequest request,
final HttpServletResponse response, final Authentication authentication)
throws IOException, ServletException {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
request.getAttribute( "storeCode" );
attr.getRequest().getAttribute( "storeCode" );
}
}
But in all way, I am not able to get value of storeCode and its coming as null.
Not sure what I am doing wrong.
I am assuming that Spring is creating a new instance of Request and response while calling onAuthenticationSuccess, but how can I pass/ retrieve values which passed passed from the login page?
If the data is from an HTTP POST request, you should be using getParameter, not getAttribute. Attributes are server-side state only, not submitted by the client.

Resources