Spring Security: How to force https with flag? - https

With Spring security I configure the parts of my web app that need to be secured by https with security:intercept-url tag.
For example:
<security:intercept-url pattern="/**/*secure*" requires-channel="https"/>
<security:intercept-url pattern="/**" requires-channel="http" />
This way all requests with "secure" in the file name or directories containing "secure" on the lowest level get https, the rest will get http.
Is there a way to use a flag to make Spring force https?
I could not get anything to work that takes stuff after the "?" of an url.
http://domain/abc?secure=true => https
http://domain/abc => http
Spring would then redirect transparently every request going to http://domain/abc?secure=true to https://domain/abc?secure=true.
Is there a way to achieve that?
The first pattern in the example achieves the result for a "secure" occuring before the "?", but I am looking for a solution/pattern to have a flag after the "?" only.

To make Spring consider fragments after the "?" in urls, the stripQueryStringFromUrls has to be set to false.
Here is a solutation I found in the Spring forum:
When this BeanPostProcessor is loaded, the property will be set.
public class BeanPostProcessorImpl implements BeanPostProcessor
{
final static private Logger log = Logger.getLogger(BeanPostProcessorImpl.class);
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
{
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
{
if (bean instanceof DefaultFilterInvocationSecurityMetadataSource)
{
log.info("********* Post-processing " + beanName);
((DefaultFilterInvocationSecurityMetadataSource) bean).setStripQueryStringFromUrls(false);
}
return bean;
}
}
This way pattern
<security:intercept-url pattern="/**/*?secure=true*" requires-channel="https"/>
finally works.

As far as I can tell you want to have a URL param which to tell spring to redirect you to an https channel.
So did you try:
<security:intercept-url pattern="/**/*?secure*" requires-channel="https"/>
or if that doesn't work try an escaped '?' :
<security:intercept-url pattern="/**/*\?secure*" requires-channel="https"/>
I just had a look at the source for the FilterChainProxy (in 3.0.5.RELEASE) and it uses regular expressions to match the patterns. So you can have a look at:
org.springframework.security.web.util.RegexUrlPathMatcher#pathMatchesUrl(Object compiledPath, String url)
or
org.springframework.security.web.FilterChainProxy#getFilters(String url)
to see exactly what pattern you need.
So even if spring currently doesn't support the '?' sign you can extend your own UrlPathMatcher and inject it into the FilterChainProxy.

Related

Spring: forwarding to /oauth/token endpoint loses authentication

I'm building a Spring Boot authorization server which needs to generate Oauth2 tokens with two different auth methods. I want to have a different endpoint for each method, but by default Spring only creates /oauth/token, and while it can be changed, I don't think it is possible to have two different paths for it.
As an alternative, I'm trying to create two methods in a controller which do an internal forward to /oauth/token, adding a parameter to the request so I can know where it came from.
I have something like this:
#RequestMapping(value = "/foo/oauth/token", method = RequestMethod.POST)
public ModelAndView fooOauth(ModelMap model) {
model.addAttribute("method", "foo");
return new ModelAndView("forward:/oauth/token", model);
}
This performs the forward correctly, but the auth fails with:
There is no client authentication. Try adding an appropriate authentication filter.
The same request works correctly when sent to /oauth/token directly, so I'm guessing that the problem is that the BasicAuthenticationFilter is not running after the forward.
How can I make it work?
I had exactly the same issue. After some research I found out that the problem was caused by Spring Boot 2, not by Spring Security configurations. According to the Spring Boot 2.0 migration guide:
Spring Security and Spring Session filters are configured for ASYNC, ERROR, and REQUEST dispatcher types.
and the Spring Boot's SecurityFilterAutoConfiguration source code:
#Bean
#ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(
SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name())).collect(Collectors
.collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
}
where the defaults for securityProperties.getFilter().getDispatcherTypes() are defined in SecurityProperties as:
private Set<DispatcherType> dispatcherTypes = new HashSet<>(
Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
Thus by default, Spring Boot configures Spring Security so that its filters will not be applied to FORWARD requests (but only to ASYNC, ERROR and REQUEST), and therefore no security filter will be applied to authenticate the requests when forwarding them to /oauth/token.
The solution is simple. You can either add the following line to your application.properties in order to apply default filters to ALL forwarded requests
spring.security.filter.dispatcher-types=async,error,request,forward
or create your own custom filter chain with a path matcher and dispatcherType=FORWARD to only filter requests that are forwared to /oauth/token.
Looking carefully to the filter chains created for the Oauth endpoints, and for the forwarding controllers, it's easy to see that the latter are missing the BasicAuthenticationFilter, because they aren't authenticated, and auth isn't performed again after the forward.
To solve it, I created a new config like this:
#Configuration
public class ForwarderSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
#Autowired
private FooClientDetailsService fooClientDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
for (AuthorizationServerConfigurer configurerBit : configurers) configurerBit.configure(configurer);
http.apply(configurer);
http
.authorizeRequests()
.antMatchers("/foo/oauth/token").fullyAuthenticated()
.and()
.requestMatchers()
.antMatchers("/foo/oauth/token");
http.setSharedObject(ClientDetailsService.class, fooClientDetailsService);
}
}
This code mimics what Spring Oauth does behind the scenes (here), running identical filter chains with the same authentication options on both endpoints.
When the /oauth/token endpoint finally runs, it finds the auth results that it expects, and everything works.
Finally, if you want to run a different ClientDetailsService on two forwarding endpoints, you just have to create two configuration classes like this one, and replace the ClientDetailsService on the setSharedObject call in each of them. Note that for this, you'll have to set different #Order values in each class.

Disable CSRF protection by property

Spring security offers an XML way to configure CSRF protection by applying the <csrf /> tag into the <http> element configuration.
For development purposes, I want to occasionally switch off such protection. Normally I would edit my security-context.xml file and change to <csrf disabled="true">.
I tried to use an expression such as <csrf disabled="${someProperty:false}" /> but it's not valid because XML schema accepts only a raw boolean value.
I don't want to use an entire bean profile (for the entire <http> element) just for a single parameter that has to be switched on/off sometimes.
Any advice?
Additional info
In order to perform some unit testing with RESTClient against authenticated controllers (when I am too lazy to use JUnit with MockMvc) I both need to bypass form authentication (e.g. using <http-basic /> and instructing RESTClient on the credentials) and disable CSRF, otherwise all requests will be blocked for missing token.
My application is a WAR application and by design it uses XML configuration instead of code-based configuration
Use request-matcher-ref and create your own custom RequestMatcher.
<csrf token-repository-ref="csrfTokenRepository" request-matcher-ref="csrfRequestMatcher" />
Here is for spring bean definition:
<spring:bean class="com.faizalsidek.security.CsrfRequestMatcher" id="csrfRequestMatcher" />
<spring:bean class="org.springframework.security.web.csrf.CookieCsrfTokenRepository" id="csrfTokenRepository" />
Here is the custom CsrfRequestMatcher:
public class CsrfRequestMatcher implements RequestMatcher {
/**
* Enable CSRF flag.
*/
#Value("${csrf.enabled}")
private boolean csrfEnabled;
#Override
public final boolean matches(final HttpServletRequest request) {
return csrfEnabled;
}
}
Because I stumbled upon this Question with the same problem I'd like to complement Faizal Sidek's answer, because his answer removes the default matching of Spring security.
To keep the default behaviour when csrf is enabled you need to match the requests HTTP method like this:
Your custom request matcher:
package my.example;
import org.springframework.security.web.util.matcher.RequestMatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.regex.Pattern;
public class CustomRequestMatcher implements RequestMatcher {
private Pattern allowedMethods = Pattern.compile("^(GET|TRACE|HEAD||OPTIONS)$");
private boolean enabled;
public CustomRequestMatcher(boolean enabled) {
this.enabled = enabled;
}
#Override
public boolean matches(HttpServletRequest httpServletRequest) {
return enabled && !allowedMethods.matcher(httpServletRequest.getMethod()).matches();
}
}
Then use it in your security.xml:
<http ...>
<csrf request-matcher-ref="requestMatcher"/>
</http>
<beans:bean id="requestMatcher" class="my.example.CustomRequestMatcher">
<beans:constructor-arg value="${myproperty}" />
</beans:bean>
Now if you set "myproperty" to false csrf is disabled, but if "myproperty" is true the default behaviour is preserved.

Spring Security whitelisting approach using annotations and a global denyAll

At the moment I try to figure out how Spring Security evaluates the given URL, expression and annotations. So far it seems like it always checks the entries from security-context.xml first. And if that's a denyAll it will simply stop further processing of the request.
Maybe I forgot to set some configuration option, but (in my eyes) it's not possible to build a nice whitelist using Spring Security's annotations (like #Secured, #PermitAll, etc.).
What I want is basically to annotate the methods inside a #Controller for allowing access. For example:
#Controller
#RequestMapping("/test")
public MyController {
#RequestMapping("")
public void tryToGetSomething() {
// no security annotation -> denyAll
}
#RequestMapping("/public")
#PermitAll
public void tryToGetSomethingPublic() {
// this will always have access allowed
}
#RequestMapping("/admin")
#Secured({"ROLE_ADMIN"})
public void tryToGetSomethingReallyImportant() {
// this can only be accessed by admins
}
}
The main reason for this approach is: Security ;-). It's always possible to forget some annotations while writing code. And with this method such a mistake won't affect security of sensitive data.
So my question is: How can I achieve this?
You can try to use security pointcuts in conjuction with annotations:
<global-method-security pre-post-annotations="enabled">
<!-- Disable access to all controller methods -->
<protect-pointcut expression="execution(* com.mycompany.controllers.*Controller.*(..))"
access="ROLE_THAT_DOES_NOT_EXIST"/>
</global-method-security>
#Controller
#RequestMapping("/test")
public MyController {
#RequestMapping("")
public void tryToGetSomething() {
// pointcut rule -> no one has ROLE_THAT_DOES_NOT_EXIST -> no one can call this code
}
#RequestMapping("/public")
#PreAuthorized("permitAll")
public void tryToGetSomethingPublic() {
// annotations take precedence over pointcuts, so anyone can call this code due to #PreAuthorized("permitAll") rule
}
}
See corresponding entry from the official documentation. Maybe you can use denyAll instead of ROLE_THAT_DOES_NOT_EXIST.
Hope this helps.
I tried to achieve the same, but the problem is that method security level applies to every method called through AOP. If you deny access by default, you will will have to annotate pretty much everything :)
With URL based security, you can proceed by whitelist:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.anyRequest().denyAll();
}
The obvious drawback, unfortunately, is that every URL has to be authorized here, creating a kind of dependency magnet. But maybe it is a good thing to centralize URL path mapping ?

Disable Spring method security in version 3.0.x

I have a web application with spring security configured to limit access on both URLs and methods. I want to disable it entirely by-default, and allow my customers to easily turn it on if they want to (they can only access "spring-security.xml").
I managed to turn off the URL interception, but my method security is still enabled...
Any clue?
(I don't want to let the customer change my web.xml, so unfortunately modifying the "global-method-security" setting each time is not an option...)
This is my updated spring-security.xml configuration:
<http auto-config='true' use-expressions="true">
<intercept-url pattern="/**" access="permitAll" />
<http-basic />
<anonymous />
</http>
I have overriden the DelegatingFilterProxy.doFilter method like this:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String springSecured = System.getProperty("springSecured");
if (StringUtils.isNotBlank(springSecured) && springSecured.equalsIgnoreCase("true")) {
// Call the delegate
super.doFilter(request, response, filterChain);
} else {
// Ignore the DelegatingProxyFilter delegate
filterChain.doFilter(request, response);
}
}
and this is an example of the method security I have:
#RequestMapping(
value = "applications/{applicationName}/timeout/{timeout}",
method = RequestMethod.POST)
public
#ResponseBody
#PreAuthorize("isFullyAuthenticated() and hasPermission(#authGroups, 'deploy')")
Object deployApplication() {
// ...
}
If I were you I wouldn't use a custom filter chain implementation, just the one out of the box. You can enable and disable sections of bean configuration (since Spring 3.0) with nested elements, so something like this might be convenient:
<beans profile="secure">
<http auto-config='true' use-expressions="true">...</http>
</beans>
Your application is now unprotected in the default profile (and any other but the "secure" profile). You can enable the secure profile by providing a system property spring.profiles.active=secure, or by explicitly setting it in a context or servlet initializer.

Spring 3.0 MVC Handler Interceptors not working

I am trying out the HandlerInterceptors from Spring MVC 3.0.
Below is my interceptor
public class SessionInterceptor extends HandlerInterceptorAdapter {
#Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("inside preHandle");
if(request.getSession().getAttribute(SessionConsta nts.USER_SESSION_NAME) == null) {
response.sendRedirect("welcome");
return false;
}
return true;
}
}
Below is my configuration in my xml
<mvc:annotation-driven/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/services/*"/>
<bean class="com.ca.myca.interceptors.SessionInterceptor " />
</mvc:interceptor>
</mvc:interceptors>
But the interceptor is not getting called.
Please let me know if I am missing any thing.
In our application we are using double ** for any service sub-path match, so try changing it and check if it helps:
<mvc:mapping path="/services/**"/>
You are using <mvc:annotation-driven/> with mvc interceptor.
Please check on Spring reference:
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/web/servlet/DispatcherServlet.html
"When running in a Java 5+ environment, a default AnnotationMethodHandlerAdapter will be registered as well. HandlerAdapter objects can be added as beans in the application context, overriding the default HandlerAdapters. Like HandlerMappings, HandlerAdapters can be given any bean name (they are tested by type)."
<mvc:annotation-driven/> is supposed to be used for annotation-driven MVC controllers like #RequestMapping, #Controller etc, but I have seen there is no need to define "<mvc:annotation-driven/>" for supporting it.
Unless you are using jackson (for json support), you can try to remove <mvc:annotation-driven/> and use "<context:annotation-config>" instead for common use like autowiring etc.
try what is suggested in Configuration of Spring MVC and JSON using Jackson.
Put you interceptor in <mvc:interceptors> tag
<mvc:interceptors>
<bean class="xx.x..x..x...UserSessionInterceptor" />
</mvc:interceptors>
you can keep <mvc:annotation-driven/> and <context:annotation-config>
In reference to the post above by arviarya, <mvc:annotation-driven /> in the config XML results in a different handler Object being passed to the interceptor. In our interceptor method we had:
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav) throws Exception {
if (mav != null && handler instanceof HandlerMethod) {
// something we want to have happen
}
This was being called with the #Controller-derived object without the <mvc:annotation-driven />, but was called with the HandlerMethod-derivedobject when it was present. For our if block to work, I needed the tag in our config XML.

Resources