Conversation
A <filter-mapping> that names no <dispatcher> applies to REQUEST only, so a RequestDispatcher forward or include, an async dispatch or an error-page dispatch reaches the endpoint with the filter skipped. Declare all five types on the four narrowly mapped security filters (AuthNFilter and AuthZFilter on /ws/*, JAXRPCRequestFilter on /jaxrpc/*, NotificationsWebSocketFilter on /notifications) and pin them with a test that parses the shipped web.xml. JAXRPCRequestFilter can now re-enter on a nested dispatch, so it restores the previously bound request on exit instead of clearing it.
FSUtils.forwardRequest handed the raw request-derived path to the RequestDispatcher once ForwardPathValidator had accepted it. It now dispatches ForwardPathValidator.forwardTarget(): the form the container maps - path parameters stripped, escapes decoded once, '+' literal, the query string as given - or answers 400 when there is none. A WEB-INF segment anywhere in the decoded path is refused, not only at the root, and the decoded form is derived and re-checked in the shape static analysis recognises as a sanitised forward target (CodeQL java/unvalidated-url-forward OpenIdentityPlatform#196).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two hardenings around
RequestDispatcherforwards, following up on #1128 ("aRequestDispatcher… does not run theweb.xmlfilters").Security filters on every dispatch type
Per the Servlet spec a
<filter-mapping>that names no<dispatcher>applies toREQUESTonly: aRequestDispatcherforward or include, anAsyncContext.dispatchor an<error-page>dispatch reaches the servlet with the filter skipped, and naming any type replaces that default. The four narrowly mapped security filters now declare all five types:AuthNFilterandAuthZFilteron/ws/*,JAXRPCRequestFilteron/jaxrpc/*,NotificationsWebSocketFilteron/notifications. No forward or include in the tree targets these roots (the/wsmatches are/wsfederation/jsp/*, outside/ws/*), so nothing legitimate changes.SecurityFilterDispatchersTest(new,openam-server-only) parses the shippedweb.xmland fails if any mapping of those filters loses a type — a security filter added with the default dispatch set will not pass silently.JAXRPCRequestFilterbinds the request in aThreadLocal; mapped forFORWARD/INCLUDEit can re-enter on a nested dispatch, so it restores the previous binding on exit instead ofremove()(nestedDispatchRestoresTheOuterRequestOnExit).Left as they are: the
/*-mapped filters (amSetupFilter,SecurityHeaders,AuditContextFilter,ResponseValidationFilter) andFQDNValidationFilter(/XUI/*,/oauth2/authorize). Running those on every internal forward changes behaviour product-wide (double auditing, FQDN redirects on internal dispatch) and is a separate change.Container-mapped forward target (
java/unvalidated-url-forward#196)FSUtils.forwardRequestpassed the raw request-derived path togetRequestDispatcheronceForwardPathValidator.isSafeForwardPathhad accepted it. It now dispatchesForwardPathValidator.forwardTarget(path): the form the container maps — path parameters stripped, percent-escapes decoded once,+literal, the query string as given — or answers 400 when there is none. For every path the validator accepts this maps to the same resource (Tomcat strips, decodes and normalises the dispatcher path itself; the trailing slash is kept,//and/./are left to the container). One addition beyondisSafeForwardPath: aWEB-INFsegment anywhere in the decoded path is refused, not only at the root — no in-app path of the product carries one...is still judged per segment, so/a/b..c/dstays an ordinary name as #1128 pinned it.The decoded form is built and re-checked in the shape CodeQL's
UrlForwardbarriers recognise (URLDecoderuntil no%remains, a..check preceded by aWEB-INFcheck on the same value, the query appended after a constant?); the validator itself was opaque to the query, which is why #196 stayed open after #1128 while #197/#198 closed. Whether the scan closes it is for this PR's CodeQL run to show; the behaviour is pinned byForwardPathValidatorTest(14forwardTargetsrows, everyunsafeForwardPathsrow →null, 4WEB-INF-below-root rows) andFSUtilsForwardPathTest.dispatchesTheFormTheContainerMapseither way.Verification
openam-core2131 tests,openam-federation-library249 tests, 0 failures (mvn -o test).SecurityFilterDispatchersTest4/4, run standalone against the module'sweb.xml(the war module's surefire run needs the full reactor — CI). Every new test was watched failing first: the fourweb.xmlrows withfound [[]], the nested-dispatch test withexpected [outer] but found [null], theforwardTargetrows on the missing method, the dispatch-form test on the raw path.