Skip to content

Commit c201358

Browse files
Document the 2.0.2 hardening in the threat model
SECURITY.md described the state before this round of work, so a reader would have taken the WS-Addressing egress path, the unbounded request builders and the uneven metadata gating as still current. Four additions to the hardening list: the response-endpoint policy and why decoupled responses are declined by default, the request-body ceilings and temp-file lifecycle, the OpenAPI and Swagger UI output handling, and the now uniform enforcement of exposeServiceMetadata. The attack-surface and URL tables are updated to match -- the OpenAPI routes were listed as plain anonymous, and the metadata row named only three of the routes that gate on the parameter. The response-endpoint entry states its own limitation rather than leaving it implied: the destination is resolved to check it and resolved again to connect, which pinning would close but the transport cannot currently do, so cloud deployments still want egress controls alongside it. Every parameter named here was checked against the code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 5ec4d15 commit c201358

1 file changed

Lines changed: 70 additions & 5 deletions

File tree

SECURITY.md

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,10 @@ Axis2 exposes the following URL patterns from the servlet mapping:
121121
| `/services/{ServiceName}?wsdl` | WSDL metadata retrieval | Anonymous (if `exposeServiceMetadata=true`) |
122122
| `/services/{ServiceName}?xsd` | XML Schema retrieval | Anonymous (if `exposeServiceMetadata=true`) |
123123
| `/services/` | Service listing | Anonymous (if `exposeServiceMetadata=true`) |
124-
| `/openapi.json` | OpenAPI 3.0 schema (if OpenAPI module engaged) | Anonymous |
125-
| `/swagger-ui` | Swagger UI (if OpenAPI module engaged) | Anonymous |
126-
| `/openapi-mcp.json` | MCP tool catalog (if OpenAPI module engaged) | Anonymous |
124+
| `/services/{ServiceName}/{name}.xsd` or `.wsdl` | Packaged metadata by file name | Anonymous (if `exposeServiceMetadata=true`) |
125+
| `/openapi.json` | OpenAPI 3.0 schema (if OpenAPI module engaged) | Anonymous; per-service `exposeServiceMetadata` respected |
126+
| `/swagger-ui` | Swagger UI (if OpenAPI module engaged) | Anonymous; per-service `exposeServiceMetadata` respected |
127+
| `/openapi-mcp.json` | MCP tool catalog (if OpenAPI module engaged) | Anonymous; per-service `exposeServiceMetadata` respected |
127128

128129
### Attack Surface by Component
129130

@@ -133,11 +134,14 @@ Axis2 exposes the following URL patterns from the servlet mapping:
133134
| **WSDL/XSD import resolution** (wsdl4j, xmlschema-core) | XXE in imported documents; SSRF via `file://`/`gopher://` schemes | `SecureWSDLLocator` pre-validates with hardened SAX parser; protocol whitelist (HTTP/HTTPS only); size limit (10MB default); connect/read timeouts; relative-path SSRF bypass blocked |
134135
| **JSON parser** (Gson) | Deep nesting stack exhaustion, large payload DoS | Fuzz-tested (1.7M+ iterations); Gson nesting limits |
135136
| **JSON-RPC dispatch** | Method name injection; unexpected operation invocation | Method names validated against deployed operations; unknown methods return fault |
136-
| **Multipart/file upload** (commons-fileupload2) | Unbounded file count DoS (CVE-2023-24998 pattern) | Migrated from commons-fileupload 1.x to commons-fileupload2 which enforces file count limits |
137+
| **Multipart/file upload** (commons-fileupload2) | Unbounded file count DoS (CVE-2023-24998 pattern); unbounded body size; temp-file accumulation | commons-fileupload2 enforces the file count limit; `multipartMaxRequestSize` / `multipartMaxFileSize` bound the body; temp files are deleted immediately for form fields and tracked to collection for file parts |
138+
| **Form-urlencoded builder** | Unbounded body read into an in-memory map | `formUrlEncodedMaxRequestSize` bounds the read; the stream fails rather than truncating |
137139
| **Service dispatchers** | Routing to unintended service; header spoofing | Dispatchers validate service existence; unknown services return fault |
138140
| **Hot-deployment** (DeploymentEngine) | Malicious AAR/MAR deploys arbitrary code | Trust boundary is filesystem access; no signature verification (admin operation) |
139141
| **Context externalization** (SafeObjectInputStream) | Java deserialization gadget chains | Whitelist-based `SafeObjectInputStream`; restricted to known Axis2 context classes |
140-
| **Metadata endpoints** (`?wsdl`, `?xsd`, `/services/`) | Service enumeration, schema disclosure | Controllable via `exposeServiceMetadata` parameter |
142+
| **Metadata endpoints** (`?wsdl`, `?xsd`, `/services/`, `.xsd`/`.wsdl` by name, OpenAPI/MCP) | Service enumeration, schema disclosure | `exposeServiceMetadata` enforced uniformly across the servlet and standalone HTTP paths and the OpenAPI/MCP generators |
143+
| **WS-Addressing response endpoints** (`wsa:ReplyTo`, `wsa:FaultTo`) | SSRF: an inbound header names the destination of a server-initiated send | Non-anonymous response endpoints refused by default (`allowNonAnonymousResponseEndpoints`); when enabled, scheme restricted to HTTPS, destination screened at both the header-parsing and transport-selection layers, and redirects not followed |
144+
| **OpenAPI / Swagger UI surface** | Reflected XSS from request-controlled values; Host reflected into published URLs | Host validated, values encoded for their output context, CSP with a per-response script nonce; the published `servers[].url` is relative unless `openapi.serverBaseUrl` pins it |
141145
| **MTOM/attachment handling** | Large attachment DoS, temp file exhaustion | Streaming processing; `TempFileManager` cleanup |
142146
| **`?fields=` query parameter** (field selection, if enabled) | Reflection-based field filtering on response objects | Field names validated against declared response type; no dynamic class loading |
143147

@@ -240,6 +244,67 @@ migration from `commons-fileupload` 1.x to `commons-fileupload2` in
240244
findings. See `src/site/xdoc/docs/OSS-FUZZ.md`. Axis2/C has an
241245
active OSS-Fuzz integration.
242246

247+
9. **WS-Addressing response endpoints (2.0.2):** A non-anonymous
248+
`wsa:ReplyTo` or `wsa:FaultTo` makes the server open a connection to an
249+
address the caller chose. Unless WS-Security is engaged to bind that
250+
endpoint reference to a trusted issuer, the WS-Addressing specification
251+
leaves it to the receiver to decide whether to honour it, so Axis2 now
252+
declines by default. `allowNonAnonymousResponseEndpoints` is `false`;
253+
replies and faults travel back down the inbound connection only. Apache
254+
CXF made the same choice in
255+
`org.apache.cxf.ws.addressing.decoupled.enabled`.
256+
257+
Deployments that genuinely use decoupled responses — the separate-listener
258+
"Dual" clients, or a third-party callback endpoint — set it to `true`, and
259+
should also set `httpFrontendHostUrl` so the generated reply address is the
260+
real external URL rather than the local one. With the feature enabled:
261+
262+
- `allowedResponseEndpointSchemes` permits HTTPS only. Widen it to name a
263+
transport actually used for replies.
264+
- The destination is screened both where the inbound header is parsed and
265+
where a server-side response acquires its transport, so the check cannot
266+
be reached around by setting the endpoint reference another way.
267+
- Link-local, wildcard and multicast destinations are always refused.
268+
`blockPrivateNetworkResponseEndpoints` additionally refuses loopback and
269+
private ranges; it is off by default because a callback inside the same
270+
private network is how most decoupled deployments are wired.
271+
- Redirects are not followed, so a reply endpoint cannot hand the sender a
272+
destination the policy already refused.
273+
- Name resolution is bounded (`responseEndpointResolveTimeoutMillis`) and
274+
runs on a capped pool, so a slow resolver cannot tie up request threads.
275+
276+
Known limitation: the destination is resolved once to check it and again to
277+
connect, so a hostile DNS server could answer differently the second time.
278+
Closing that requires connecting to a pinned address, which the transport
279+
does not currently support. Operators in cloud environments should pair
280+
these settings with network egress controls.
281+
282+
10. **Request body ceilings (2.0.2):** The `multipart/form-data` and
283+
`application/x-www-form-urlencoded` builders read the transport stream
284+
directly, so a servlet container's post-size limit never sees the body.
285+
`multipartMaxRequestSize` and `multipartMaxFileSize` (100 MB) and
286+
`formUrlEncodedMaxRequestSize` (2 MB) bound them; `-1` restores the
287+
previous unbounded behaviour, and either may be set per service.
288+
Multipart temp files are now deleted rather than accumulating: form-field
289+
parts as soon as their text is read, file parts once the item backing the
290+
`DataHandler` is unreachable.
291+
292+
11. **OpenAPI and Swagger UI output (2.0.2):** Request-controlled values are
293+
validated and encoded for the context they are written into, the served
294+
page carries a Content-Security-Policy with a per-response script nonce,
295+
and the published `servers[].url` is relative — resolved by the client
296+
against wherever it fetched the document — rather than derived from the
297+
request Host. `openapi.serverBaseUrl` pins an absolute URL where a
298+
deployment needs one.
299+
300+
12. **Uniform metadata exposure (2.0.2):** `exposeServiceMetadata` is now
301+
honoured by every anonymous metadata route: the `?wsdl`, `?wsdl2` and
302+
`?xsd` queries as before, plus the `.xsd`/`.wsdl` file routes on both the
303+
servlet and standalone HTTP paths, the named-WSDL route, and the
304+
OpenAPI/Swagger/MCP generators. A service with exposure disabled is
305+
skipped rather than refused, so it stays indistinguishable from one that
306+
is not deployed.
307+
243308
## Reporting Security Issues
244309

245310
Report vulnerabilities to: **security@apache.org**

0 commit comments

Comments
 (0)