Skip to content

Security: apache/wicket

Security

SECURITY.md

Security Policy

Apache Wicket follows the Apache Software Foundation security process.

Reporting a Vulnerability

Please do not report security vulnerabilities through GitHub issues, GitHub discussions, pull requests, JIRA, or the public mailing lists. Doing so discloses the issue publicly before a fix is available.

Report suspected vulnerabilities privately to:

  • [email protected] — the ASF Security Team, who will forward the report to the Wicket PMC.

A useful report includes:

  • the affected Wicket version(s) and the module (e.g. wicket-core),
  • the affected class and method, ideally with a source reference,
  • a concrete description of how an attacker reaches the code, including what the attacker is assumed to control (see Security Model),
  • the impact you believe follows from that, and
  • a reproducer where possible — a failing test is ideal.

Please state clearly whether you have published anything about the issue, and whether you are requesting a CVE.

We ask reporters to keep the issue confidential until a fixed release is published and the PMC has announced it. In return, we will keep you informed of our assessment and of the release timeline, and credit you in the announcement unless you ask us not to.

Note that reports are assessed against the security model below. A report that depends on the framework distrusting something this model treats as trusted may be closed as a deployment or configuration issue rather than a framework vulnerability. If you believe the model itself is wrong, that is a legitimate and useful thing to report — please say so explicitly, so we discuss the model rather than the individual code path.

Conversely, a demonstrated bypass of a boundary this model does claim — for example the package resource guard, or an authorization strategy — is a vulnerability, and we want to hear about it. The boundaries below describe what Wicket intends to enforce; where the code falls short of them, the code is what needs fixing.

Supported Versions

Security fixes are applied to the actively maintained release lines. Refer to the download page for the current status and the latest release of each line.

Version Status
11.x In development (master) — not yet released
10.x Current, supported
9.x Supported
8.x Security fixes only — upgrade to 9.x or 10.x
≤ 7.x Discontinued — no security fixes

If you are running a discontinued version, the fix is to upgrade. See the Migration to Wicket 10.0 guide on our wiki, which links the guides for the earlier lines.

Security Model

Wicket is a framework, not a deployed application. It runs inside a servlet container, usually behind a reverse proxy, and it inherits its view of the outside world from that container. This section documents which of those inputs Wicket treats as trusted, so that operators know what they are responsible for and reporters know what the framework does and does not claim to defend.

Wicket trusts the container-reported host, port and scheme

Wicket derives its own public identity — the scheme, host and port it believes it is being served on — from the servlet container, via HttpServletRequest#getScheme(), #getServerName() and #getServerPort(). There is no hostname allowlist in the framework and no attempt to verify the Host header, in any of the places this identity is used:

  • ServletWebRequest#setParameters sets the host, port and protocol on the client URL from these three values. That URL backs UrlRenderer, and so every absolute URL Wicket renders.
  • HttpsMapper#createRedirectUrl builds the scheme-switch redirect for @RequireHttps pages from the same values.
  • OriginResourceIsolationPolicy#getTargetUriFromRequest builds the trusted target URI that incoming Origin and Referer headers are compared against.

This is a deliberate design decision, not an oversight. Only the deployment knows its own canonical hostnames; the framework cannot infer them. Note in particular that the third item means the container-reported host is a trusted input to Wicket's own request-forgery defences — a deployment that lets arbitrary Host values through weakens more than URL rendering.

Therefore the deployment is responsible for ensuring that only expected Host values reach the application. Concretely:

  1. Configure the container or virtual host to reject requests carrying an unrecognised Host — return a 400 or 404 rather than routing them to the application. Tomcat, Jetty and the common reverse proxies all support this.
  2. If TLS is terminated at a proxy, have the proxy set or overwrite Host to the canonical name rather than forwarding whatever the client sent.
  3. Do not expose a Wicket application through a catch-all or default virtual host that accepts any Host.
  4. Serve the application over HTTPS and enable HSTS, so that plaintext requests — including the ones HttpsMapper exists to upgrade — are not part of the normal flow.

A consequence worth stating plainly: on a deployment that accepts arbitrary Host values, absolute URLs and redirects generated by Wicket will contain the host the client supplied. That is the documented behaviour of trusting the container. It is not treated as a framework vulnerability, because the host in such a response is always the same authority the client had already connected to — it grants an attacker no origin they did not already control. The fix belongs at the container or proxy, per the points above.

X-Forwarded-* headers are not trusted by default

Wicket ignores X-Forwarded-For and X-Forwarded-Proto unless you explicitly enable XForwardedRequestWrapperFactory. When enabled, it overrides getRemoteAddr(), getRemoteHost(), getScheme() and getServerPort() from those headers, subject to its internalProxies and trustedProxies configuration.

Only enable it when a trusted proxy in front of the application appends to these headers and strips any client-supplied copies; otherwise the headers are attacker-controlled. Wicket does not implement X-Forwarded-Host at all, and XForwardedRequestWrapper does not override getServerName() — the host always comes from the container as described above.

Client-supplied URLs are not trusted for authority

For Ajax requests Wicket reads a client-supplied base URL — the Wicket-Ajax-BaseURL header, falling back to the wicket-ajax-baseurl request parameter — in order to resolve relative URLs against the page the client is actually on. The host, port and protocol of that URL are always overwritten with the container-reported values before use. The client can influence the path Wicket renders relative to, never the authority.

Deployment configuration is the operator's responsibility

RuntimeConfigurationType.DEVELOPMENT enables debugging aids, verbose error reporting and development-only components, and disables some caching. It is not intended for production and is not hardened. Always run production deployments with the configuration type set to RuntimeConfigurationType.DEPLOYMENT. Issues only reachable in DEVELOPMENT mode are treated as configuration errors rather than vulnerabilities.

Likewise, wicket-devutils is a development aid. Do not deploy it in production.

Serialized data is trusted

Wicket serializes page instances and session data to its page store. Java deserialization is not a safe operation on untrusted input, and by default Wicket's page store does not defend against it. Treat the page store and the session store as trusted, private storage: do not point them at storage that untrusted parties can write to, and do not accept externally supplied serialized page or session data.

The exception is a page store configured with encryption (StoreSettings#setEncrypted(true)), and then only as far as the configured ICrypter implementation documents. Encryption does not by itself imply tamper detection: whether the stored bytes are merely confidential or are also protected against modification depends entirely on the implementation in use, and the default implementation is not authenticated. Consult the javadoc of the ICrypter you configure and rely on no more than it states.

Another origin may not invoke a listener

Where ResourceIsolationRequestCycleListener is registered, a request originating from another origin must not be able to invoke a listener on a page — a Link.onClick(), a Form.onSubmit(), or an AJAX behaviour. A demonstrated way for another origin to reach one is a vulnerability.

Two things sit deliberately outside that boundary:

  • Rendering a page is allowed. A page may be reached by a simple top-level navigation from anywhere, so that pages remain linkable from other sites. Only the invocation of a listener is refused. Requests that are not top-level navigations — subresource loads, fetch, <object> and <embed> — are refused for renders too.
  • Sibling origins may be trusted explicitly. Sec-Fetch-Site: same-site means a different origin on the same registrable domain and scheme, such as another subdomain, and is refused by default. A deployment that trusts every origin on its own site can allow it; sibling-origin actions are then that deployment's decision rather than a framework vulnerability.

This listener is opt-in and is not registered by default. Without it Wicket enforces no cross-origin boundary on listener invocation at all. CryptoMapper raises the cost of forging a URL but is not a substitute for it, for the reason below.

CryptoMapper is not an authorization mechanism

CryptoMapper encrypts URLs so that page and component identifiers are not guessable. It raises the cost of forging a URL, but it is not an access-control mechanism. Authorization must be enforced with IAuthorizationStrategy (or equivalent) so that it holds regardless of whether a URL was guessed, replayed, leaked through a referrer, or found in a log.

Reporting Something That Is Not a Vulnerability

Findings that are real but not vulnerabilities are still welcome — please raise them publicly in JIRA or as a pull request rather than through the private security channel, so they can be discussed and fixed in the open. Hardening suggestions, defence-in-depth improvements, and clarifications to this document all fall into that category.

If you are unsure which channel applies, use the private one — we would rather receive a non-issue privately than a real issue publicly.

There aren't any published security advisories