Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can not add the security headers on WebLogic 12c #5945

Closed
btkitsunedukam opened this issue Oct 12, 2018 · 13 comments
Closed

Can not add the security headers on WebLogic 12c #5945

btkitsunedukam opened this issue Oct 12, 2018 · 13 comments
Assignees
Labels
status: declined A suggestion or change that we don't feel we should currently apply

Comments

@btkitsunedukam
Copy link

Summary

We can not add the security headers (e.g. Cache-Controll) via the DelegatingRequestMatcherHeaderWriter with the AntPathRequestMatcher on WebLogic 12c.

The AntPathRequestMatcher use the HttpServletRequest#getServletPath to match the path, and that method return the JSP's path (forwarded by the JstlView) at the HeaderWriterResponse#writeHeaders.

Actual Behavior

  1. The controller with @RequestMapping(value = "/foo/bar") invoked, and return the view name "welcome/home". (prefix is "/WEB-INF/views/" and suffix is ".jsp")
  2. The JstlView forward to "/WEB-INF/views/welcome/home.jsp".
  3. The HeaderWriterResponse#onResponseCommitted is called and the DelegatingRequestMatcherHeaderWriter is invoked, but the AntPathRequestMatcher, that configured with the path "/foo/bar/**", said the path is not match because servlet path pointed to JSP, and not invoked the HeaderWriter.

For that reason, we can not add the security headers to the response on the WebLogic servler.

Expected Behavior

Spring Security should be able to add the security headers to the response via the DelegatingRequestMatcherHeaderWriter with the AntPathRequestMatcher on any servlet container implementation.

Configuration

    <bean id="cacheControlHeadersWriter" 
      class="org.springframework.security.web.header.writers.CacheControlHeadersWriter" />
    <bean id="secureCacheControlHeadersWriter"
      class="org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter">
      <constructor-arg>
        <bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
          <constructor-arg value="/foo/bar/**" />
        </bean>
      </constructor-arg>
      <constructor-arg ref="cacheControlHeadersWriter" />
    </bean>

    <sec:http>
      <sec:headers defaults-disabled="true">
        <sec:header ref="secureCacheControlHeadersWriter" />
      </sec:headers>
      <sec:form-login />
      <sec:logout />
    </sec:http>

Version

Spring Security 5.0.7.RELEASE
Spring IO Platform Cairo-SR3
WebLogic 12.2.1.2.0

@rwinch
Copy link
Member

rwinch commented Oct 12, 2018

This sounds everything is working as expected. The AntPathRequestMatcher uses the servletPath + pathInfo intentionally because otherwise there is no way for it to distinguish between two different servlets with the same path. For example, if you could create two servlets /spring and /struts and then have a mapping internally that is /foo/bar. If AntPathRequestMatcher ignored the servlet path there would be no way to distinguish between /spring/foo/bar and /struts/foo/bar.

To fix the issue, include the servlet mapping in the AntPathRequestMatcher constructor.

@rwinch
Copy link
Member

rwinch commented Oct 12, 2018

Closing as this sounds it is expected behavior and I provided the correction to the configuration. If you are still having problems, please reopen this issue

@rwinch rwinch closed this as completed Oct 12, 2018
@rwinch rwinch self-assigned this Oct 12, 2018
@btkitsunedukam
Copy link
Author

@rwinch Thanks for the response and I have a bit question.

I executed the same application which have only one servlet "/" with above configuration on Tomcat 8.5 and WebLogic 12c.
And I got an expected response which has a Cache-Control header on the Tomcat because the AntPathRequestMatcher#matches returned true. The servletPath was the original request path("/foo/bar") at the HeaderWriterResponse#writeHeaders invoked.
But not expected response which has no Cache-Control header on the WebLogic because the AntPathRequestMatcher#matches returned false. The servletPath was the forwarded path("/WEB-INF/views/welcome/home.jsp") at the HeaderWriterResponse#writeHeaders invoked.

Is that a expected design?
I should configure AntPathRequestMatcher with forwarded path on the WebLogic?

@rwinch
Copy link
Member

rwinch commented Oct 15, 2018

I'm confused what is happening. The HeaderWriterFilter will not be executed on forwards because it is a OncePerRequestFilter. So it should only be checking for the original request and not the forwarded URL.

@btkitsunedukam
Copy link
Author

btkitsunedukam commented Oct 16, 2018

@rwinch Sorry for my poor English.

Yes, the HeaderWriterFilter was executed only once.
But the HeaderWriterFilter$HeaderWriterResponse#writeHeaders called before executing finally closure in HeaderWriterFilter#doFilterInternal.
https://github.com/spring-projects/spring-security/blob/5.0.7.RELEASE/web/src/main/java/org/springframework/security/web/header/HeaderWriterFilter.java#L69

Because servlet response will be flush or close when forwarding by the JstlView to the JSP, and then the HeaderWriterFilter$HeaderWriterResponse#doOnResponseCommitted was invoked immediately.

Stacktrace on Tomcat as follows:

AntPathRequestMatcher.matches(HttpServletRequest) line: 156	
DelegatingRequestMatcherHeaderWriter.writeHeaders(HttpServletRequest, HttpServletResponse) line: 61	
HeaderWriterFilter$HeaderWriterResponse.writeHeaders() line: 101	
HeaderWriterFilter$HeaderWriterResponse.onResponseCommitted() line: 92	
HeaderWriterFilter$HeaderWriterResponse(OnCommittedResponseWrapper).doOnResponseCommitted() line: 246	
OnCommittedResponseWrapper.access$000(OnCommittedResponseWrapper) line: 34	
OnCommittedResponseWrapper$SaveContextPrintWriter.close() line: 274	
ApplicationDispatcher.doForward(ServletRequest, ServletResponse) line: 421	
ApplicationDispatcher.forward(ServletRequest, ServletResponse) line: 316	
JstlView(InternalResourceView).renderMergedOutputModel(Map<String,Object>, HttpServletRequest, HttpServletResponse) line: 170	
JstlView(AbstractView).render(Map<String,?>, HttpServletRequest, HttpServletResponse) line: 314	
DispatcherServlet.render(ModelAndView, HttpServletRequest, HttpServletResponse) line: 1325	

Stacktrace on WebLogic as follows:

AntPathRequestMatcher.matches(HttpServletRequest) line: 156	
DelegatingRequestMatcherHeaderWriter.writeHeaders(HttpServletRequest, HttpServletResponse) line: 61	
HeaderWriterFilter$HeaderWriterResponse.writeHeaders() line: 101	
HeaderWriterFilter$HeaderWriterResponse.onResponseCommitted() line: 92	
HeaderWriterFilter$HeaderWriterResponse(OnCommittedResponseWrapper).doOnResponseCommitted() line: 246	
HeaderWriterFilter$HeaderWriterResponse(OnCommittedResponseWrapper).flushBuffer() line: 158	
RequestDispatcherImpl.forward(ServletRequest, ServletResponse) line: 325	
JstlView(InternalResourceView).renderMergedOutputModel(Map<String,Object>, HttpServletRequest, HttpServletResponse) line: 170	
JstlView(AbstractView).render(Map<String,?>, HttpServletRequest, HttpServletResponse) line: 314	
DispatcherServlet.render(ModelAndView, HttpServletRequest, HttpServletResponse) line: 1325	

At this timing, the AntPathRequestMatcher#matches returns different result on Tomcat(true) and WebLogic(false) though I executed the same application.

Is this a problem of the servlet container implementation or Spring Security implementation or my configuration?

(Sorry, I have no permission to reopen the issue.)

@rwinch rwinch reopened this Oct 16, 2018
@rwinch
Copy link
Member

rwinch commented Oct 16, 2018

The request should not be modified when forwarding happens so the URL should stay the same. It sounds like a container issue to me. Can you put together a sample and instructions on how to run it in WebLogic (I haven't used WebLogic much) so I can take a look? With lots of things to do on my end, the easier you can make it for me, the faster I can take a look at this.

@btkitsunedukam
Copy link
Author

@rwinch Thanks for co-operation!

I pushed sample app :
https://github.com/btkitsunedukam/HeaderSampleApp

Please install WebLogic and deploy it.

  1. Install WebLogic.
    https://www.oracle.com/technetwork/middleware/weblogic/downloads/wls-for-dev-1703574.html

  2. Open <WEBLOGIC_DOMAIN_HOME>/bin/setDomainEnv.sh and add
    -DANTLR_USE_DIRECT_CLASS_LOADING=true to JAVA_OPTIONS.

  3. Start weblogic server as debug mode (for remote debug).

  export DEBUG_PORT=<port>
  export debugFlag=true
  <WEBLOGIC_DOMAIN_HOME>/bin/startWebLogic.sh
  1. Access [http://<host>/console] and login to weblogic console.

  2. Deploy sample app as follows.
    Deployment -> Install -> Select war and Next -> Install as an application and Next -> Finish
    (Reference: https://www.oracle.com/webfolder/technetwork/tutorials/obe/fmw/wls/12c/03-DeployApps/deployapps.htm)

  3. After deployment succeeded, access to sample app [http://<host>/header-sample-app/foo/bar].

Best regards.

@rwinch
Copy link
Member

rwinch commented Oct 18, 2018

I download and ran "Quick Installer intended for Oracle WebLogic Server and Oracle Coherence development only." and it did not have a file named setDomainEnv.sh nor did it have a file named startWebLogic.sh.

Any suggestions? Below are the first three levels of the directory structure (if I do the entire thing it is pretty enormous).

wls12213 tree -L 3 -d
.
├── cfgtoollogs
│   ├── opatchauto
│   │   └── core
│   └── oui
├── coherence
│   ├── bin
│   ├── lib
│   │   └── security
│   └── plugins
│       ├── jdeveloper
│       ├── jvisualvm
│       └── maven
├── inventory
│   ├── Actions
│   │   ├── fileActions
│   │   └── propertiesActions
│   ├── backup
│   │   └── 2018-10-18_10-17-48AM
│   ├── Clone
│   ├── Components
│   │   ├── com.bea.core.xml.xmlbeans
│   │   ├── oracle.ant.contrib.ant.contrib.vb3
│   │   ├── oracle.apache.commons.collections.mod
│   │   ├── oracle.apache.commons.lang.mod
│   │   ├── oracle.as.customqna.jdkqna
│   │   ├── oracle.as.install.common.help
│   │   ├── oracle.as.install.common.prerequisite.files
│   │   ├── oracle.as.install.ui.framework
│   │   ├── oracle.as.install.wls
│   │   ├── oracle.as.install.wls.prerequisite
│   │   ├── oracle.bali.ice
│   │   ├── oracle.bali.jewt
│   │   ├── oracle.bali.share
│   │   ├── oracle.cglib.cglib.nodep
│   │   ├── oracle.coherence
│   │   ├── oracle.coherence.discovery
│   │   ├── oracle.com.fasterxml_classmate
│   │   ├── oracle.com.fasterxml.jackson.core.jackson.annotations
│   │   ├── oracle.com.fasterxml.jackson.core.jackson.core
│   │   ├── oracle.com.fasterxml.jackson.core.jackson.databind
│   │   ├── oracle.com.fasterxml.jackson.dataformat.jackson.dataformat.xml
│   │   ├── oracle.com.fasterxml.jackson.jaxrs.jackson.jaxrs.base
│   │   ├── oracle.com.fasterxml.jackson.jaxrs.jackson.jaxrs.json.provider
│   │   ├── oracle.com.fasterxml.jackson.module.jackson.module.jaxb.annotations
│   │   ├── oracle.com.fasterxml.jackson.module.jackson.module.jsonschema
│   │   ├── oracle.com.google.guava.guava
│   │   ├── oracle.com.ibm.jbatch.com.ibm.jbatch.container
│   │   ├── oracle.com.ibm.jbatch.com.ibm.jbatch.spi
│   │   ├── oracle.com.jcraft.jsch
│   │   ├── oracle.commons.fileupload.commons.fileupload
│   │   ├── oracle.diagnostics.common
│   │   ├── oracle.fasterxml.jackson
│   │   ├── oracle.fmw.common.wizard.resources
│   │   ├── oracle.fmwconfig.common.config.shared
│   │   ├── oracle.fmwconfig.common.shared
│   │   ├── oracle.fmwconfig.common.wls.external
│   │   ├── oracle.fmwconfig.common.wls.help
│   │   ├── oracle.fmwconfig.common.wls.internal
│   │   ├── oracle.fmwconfig.common.wls.shared.external
│   │   ├── oracle.fmwconfig.common.wls.shared.internal
│   │   ├── oracle.fmwconfig.wls
│   │   ├── oracle.fmwconfig.wls.shared
│   │   ├── oracle.fmwplatform.fmwprov
│   │   ├── oracle.fmwplatform.ocp
│   │   ├── oracle.fmw.upgrade.fmwconfig
│   │   ├── oracle.glcm.comdev
│   │   ├── oracle.glcm.dependency
│   │   ├── oracle.glcm.encryption
│   │   ├── oracle.glcm.logging
│   │   ├── oracle.glcm.opatchauto.core
│   │   ├── oracle.glcm.opatchauto.fmw
│   │   ├── oracle.glcm.opatch.common.api
│   │   ├── oracle.glcm.wizard
│   │   ├── oracle.glcm.xmldh
│   │   ├── oracle.help.ohj
│   │   ├── oracle.help.share
│   │   ├── oracle.http_client
│   │   ├── oracle.java.activation
│   │   ├── oracle.java.jaxws
│   │   ├── oracle.java.servlet
│   │   ├── oracle.javavm.jrf
│   │   ├── oracle.java.xml.bind
│   │   ├── oracle.jaxb.core
│   │   ├── oracle.jaxb.impl
│   │   ├── oracle.jaxb.tools
│   │   ├── oracle.joda.time.joda.time
│   │   ├── oracle.jrf.maven.plugins.sync
│   │   ├── oracle.jrf.thirdparty.toplink
│   │   ├── oracle.jrf.toplink
│   │   ├── oracle.jse.dms
│   │   ├── oracle.log4j.log4j
│   │   ├── oracle.mysqlconn
│   │   ├── oracle.nginst.common
│   │   ├── oracle.nginst.core
│   │   ├── oracle.nginst.thirdparty
│   │   ├── oracle.nlsoramapping.jrf
│   │   ├── oracle.nlsrtl.jrf
│   │   ├── oracle.ons.generic
│   │   ├── oracle.org.apache.ant.ant.bundle
│   │   ├── oracle.org.apache.httpcomponents.httpclient
│   │   ├── oracle.org.apache.httpcomponents.httpcore
│   │   ├── oracle.org.bouncycastle
│   │   ├── oracle.org.codehaus.jackson.jackson.core.asl
│   │   ├── oracle.org.codehaus.jackson.jackson.jaxrs
│   │   ├── oracle.org.codehaus.jackson.jackson.mapper.asl
│   │   ├── oracle.org.codehaus.jackson.jackson.xc
│   │   ├── oracle.org.jboss.logging.jboss.logging.vfinal
│   │   ├── oracle.osdt.core
│   │   ├── oracle.pki
│   │   ├── oracle.rcu.ciestb
│   │   ├── oracle.rdbms.jrf
│   │   ├── oracle.rsa.crypto
│   │   ├── oracle.swd.opatch
│   │   ├── oracle.thirdparty.maven
│   │   ├── oracle.toplink.coherence
│   │   ├── oracle.webservices.base
│   │   ├── oracle.webservices.orawsdl
│   │   ├── oracle.webservices.wls
│   │   ├── oracle.wls.admin.console.en
│   │   ├── oracle.wls.clients
│   │   ├── oracle.wls.common.cam
│   │   ├── oracle.wls.common.cam.wlst
│   │   ├── oracle.wls.common.nodemanager
│   │   ├── oracle.wls.core.app.server
│   │   ├── oracle.wls.core.app.server.tier1nativelib
│   │   ├── oracle.wls.evaluation.database
│   │   ├── oracle.wls.inst.only
│   │   ├── oracle.wls.jrf.tenancy.common
│   │   ├── oracle.wls.jrf.tenancy.common.sharedlib
│   │   ├── oracle.wls.jrf.tenancy.ee.only.sharedlib
│   │   ├── oracle.wls.libraries
│   │   ├── oracle.wls.rcu
│   │   ├── oracle.wls.security.core
│   │   ├── oracle.wls.security.core.sharedlib
│   │   ├── oracle.wls.shared.with.cam
│   │   ├── oracle.wls.shared.with.coh.standalone
│   │   ├── oracle.wls.shared.with.inst
│   │   ├── oracle.wls.shared.with.inst.sharedlib
│   │   ├── oracle.wls.thirdparty.javax.json
│   │   ├── oracle.wls.wlsportable.mod
│   │   ├── oracle.wls.workshop.code.completion.support
│   │   ├── oracle.xdk.jrf.jaxp
│   │   ├── oracle.xdk.jrf.xmlparserv2
│   │   ├── oracle.xerces.xercesimpl
│   │   └── org.codehaus.woodstox
│   ├── ContentsXML
│   ├── deinstall
│   ├── distributions
│   │   ├── info
│   │   └── resources
│   ├── featuresets
│   │   └── resources
│   ├── inis
│   ├── locks
│   ├── make
│   ├── oneoffs
│   │   ├── 26051289
│   │   ├── 26261906
│   │   ├── 26287183
│   │   └── 26355633
│   ├── patches
│   ├── Queries
│   │   ├── generalQueries
│   │   └── jdkGeneralQueries
│   ├── refcounts
│   │   ├── components
│   │   └── featuresets
│   ├── Scripts
│   └── Templates
│       ├── oracle_common
│       ├── oui
│       └── wlserver
├── OPatch
│   ├── auto
│   │   ├── core
│   │   └── fmw
│   ├── bin
│   ├── docs
│   ├── jlib
│   ├── modules
│   │   └── features
│   ├── ocm
│   │   ├── bin
│   │   ├── doc
│   │   └── lib
│   ├── opatchprereqs
│   │   ├── opatch
│   │   └── oui
│   ├── plugins
│   │   ├── maven
│   │   └── opatchauto
│   └── scripts
├── oracle_common
│   ├── bin
│   ├── common
│   │   ├── bin
│   │   ├── lib
│   │   ├── sql
│   │   ├── templates
│   │   └── util
│   ├── jlib
│   ├── lib
│   │   └── schematypes
│   ├── modules
│   │   ├── clients
│   │   ├── features
│   │   ├── fmwplatform
│   │   ├── internal
│   │   ├── mysql-connector-java-commercial-5.1.22
│   │   ├── oracle.bali.jewt
│   │   ├── oracle.bali.share
│   │   ├── oracle.dms
│   │   ├── oracle.fmwconfig.common.wls.help
│   │   ├── oracle.help
│   │   ├── oracle.jdbc
│   │   ├── oracle.ldap
│   │   ├── oracle.nlsrtl
│   │   ├── oracle.odl
│   │   ├── oracle.osdt
│   │   ├── oracle.pki
│   │   ├── oracle.rsa
│   │   ├── oracle.toplink
│   │   ├── oracle.xdk
│   │   ├── org.apache.maven_3.2.5
│   │   └── thirdparty
│   ├── plugins
│   │   ├── fmwplatform
│   │   ├── jdeveloper
│   │   ├── maven
│   │   ├── opatchauto
│   │   ├── rcu
│   │   ├── upgrade
│   │   └── wlst
│   └── webservices
│       └── bin
├── oui
│   ├── bin
│   ├── lib
│   │   └── linux64
│   ├── modules
│   ├── mw
│   │   ├── common
│   │   └── wls
│   ├── plugins
│   │   └── maven
│   └── prov
│       └── resources
└── wlserver
    ├── common
    │   ├── bin
    │   ├── deployable-libraries
    │   ├── derby
    │   ├── dpct
    │   ├── images
    │   ├── lifecycle
    │   ├── templates
    │   └── wlst
    ├── modules
    │   ├── clients
    │   ├── com.bea.weblogic.jms.dotnetclient
    │   ├── features
    │   └── internal
    ├── orasocket
    │   └── scripts
    ├── plugins
    │   ├── jdeveloper
    │   └── maven
    └── server
        ├── bin
        ├── db
        ├── ext
        ├── include
        ├── lib
        ├── locale
        ├── native
        └── osgi-lib

@rwinch
Copy link
Member

rwinch commented Oct 18, 2018

Also here are the files that start with start and end with .sh

find -name "start*.sh"
./inventory/Templates/wlserver/server/bin/startNodeManager.sh
./inventory/Templates/wlserver/server/bin/startRSDaemon.sh
./inventory/Templates/wlserver/common/derby/bin/startNetworkServer.sh
./wlserver/server/bin/startNodeManager.sh
./wlserver/server/bin/startRSDaemon.sh
./wlserver/common/derby/bin/startNetworkServer.sh

@btkitsunedukam
Copy link
Author

@rwinch
Copy link
Member

rwinch commented Oct 19, 2018

Thanks for the additional details. Spring Security is indeed behaving as expected.

Weblogic is changing the values of the HttpServletRequest after the forward which should not happen. When HeaderWriterResponse is created in HeaderWriterFilter the request matches new AntPathRequestMatcher("/foo/bar/**"). However, the same HttpServletRequest does not match later when it is checked by the HeaderWriterResponse. Weblogic should be creating a new HttpServletRequest after the forward vs mutating the existing instance.

This is a bug in Weblogic implementation.

You could work around it by either changing the AntPathRequestMatcher to match on the forward URL. Alternatively you can create a custom Filter that sets a request attribute with the original URL and then a custom RequestMatcher that matches on that attribute rather than the path.

@rwinch rwinch closed this as completed Oct 19, 2018
@yoshikawaa
Copy link
Contributor

I think it's better to announce in the Spring Security Reference to be aware that "When using a JSP on a specific web application server, we need to specify the path of the JSP to forward rather than the request path".

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#headers-delegatingrequestmatcherheaderwriter

Thanks.

@rwinch
Copy link
Member

rwinch commented Oct 23, 2018

This is not necessary for most containers (as you pointed out it works in Tomcat). This is something that is broken in Weblogic and only a workaround.

@rwinch rwinch added the status: declined A suggestion or change that we don't feel we should currently apply label May 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

3 participants