Skip to content

Commit 7ac84d8

Browse files
Harden schema import resolution against SSRF (CWE-918)
Companion fix to the XXE hardening. xmlschema-core's DefaultURIResolver follows absolute schemaLocation URLs with no scheme or host restriction, enabling blind SSRF from the Axis2 JVM. Three vectors addressed: 1. WSDLToAxisServiceBuilder.getXMLSchema(): when no custom resolver is set, install a restrictive URIResolver that blocks http/https/ftp/jar/ file schemes on schemaLocation targets. Callers who need remote resolution can supply their own resolver via setCustomResolver(). 2. AARFileBasedURIResolver: block remote URLs when falling through to super.resolveEntity() for absolute URIs. Return empty InputSource instead of fetching. 3. WarFileBasedURIResolver: same fix as AAR resolver. Updated URIResolverTest to verify remote URLs are blocked (was previously asserting they were fetched — that was the SSRF). 403 kernel tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1d3457d commit 7ac84d8

4 files changed

Lines changed: 70 additions & 4 deletions

File tree

modules/kernel/src/org/apache/axis2/deployment/resolver/AARFileBasedURIResolver.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ public InputSource resolveEntity(
5858

5959
lastImportLocation = URI.create(baseUri).resolve(schemaLocation);
6060
if (isAbsolute(lastImportLocation.toString())) {
61+
// Block remote URLs to prevent SSRF. Only allow resolution
62+
// of absolute URIs that are local file paths.
63+
String loc = lastImportLocation.toString();
64+
if (loc.regionMatches(true, 0, "http:", 0, 5)
65+
|| loc.regionMatches(true, 0, "https:", 0, 6)
66+
|| loc.regionMatches(true, 0, "ftp:", 0, 4)
67+
|| loc.regionMatches(true, 0, "jar:", 0, 4)) {
68+
log.warn("Blocked remote schema resolution in AAR deployment: " + loc);
69+
return new InputSource(new java.io.ByteArrayInputStream(new byte[0]));
70+
}
6171
return super.resolveEntity(
6272
targetNamespace, schemaLocation, baseUri);
6373
} else {

modules/kernel/src/org/apache/axis2/deployment/resolver/WarFileBasedURIResolver.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ public InputSource resolveEntity(
4343
String baseUri) {
4444
//no issue with
4545
if (isAbsolute(schemaLocation)) {
46+
// Block remote URLs to prevent SSRF. Only allow resolution
47+
// of absolute URIs that are local file paths.
48+
if (schemaLocation.regionMatches(true, 0, "http:", 0, 5)
49+
|| schemaLocation.regionMatches(true, 0, "https:", 0, 6)
50+
|| schemaLocation.regionMatches(true, 0, "ftp:", 0, 4)
51+
|| schemaLocation.regionMatches(true, 0, "jar:", 0, 4)) {
52+
log.warn("Blocked remote schema resolution in WAR deployment: " + schemaLocation);
53+
return new InputSource(new java.io.ByteArrayInputStream(new byte[0]));
54+
}
4655
return super.resolveEntity(
4756
targetNamespace, schemaLocation, baseUri);
4857
} else {

modules/kernel/src/org/apache/axis2/description/WSDLToAxisServiceBuilder.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,31 @@ protected XmlSchema getXMLSchema(Element element, String baseUri) {
141141

142142
if (customResolver != null) {
143143
schemaCollection.setSchemaResolver(customResolver);
144+
} else {
145+
// Install a restrictive resolver that blocks remote schema
146+
// resolution (SSRF via absolute schemaLocation URLs). The
147+
// default URIResolver in xmlschema-core follows any absolute
148+
// URI including http://, https://, ftp://, and jar://.
149+
// Local file:// and relative paths are allowed for co-packaged
150+
// schemas in .aar/.war deployments.
151+
schemaCollection.setSchemaResolver(
152+
new org.apache.ws.commons.schema.resolver.URIResolver() {
153+
private final org.apache.ws.commons.schema.resolver.DefaultURIResolver
154+
delegate = new org.apache.ws.commons.schema.resolver.DefaultURIResolver();
155+
public org.xml.sax.InputSource resolveEntity(
156+
String ns, String loc, String base) {
157+
if (loc != null
158+
&& (loc.regionMatches(true, 0, "http:", 0, 5)
159+
|| loc.regionMatches(true, 0, "https:", 0, 6)
160+
|| loc.regionMatches(true, 0, "ftp:", 0, 4)
161+
|| loc.regionMatches(true, 0, "jar:", 0, 4))) {
162+
throw new RuntimeException(
163+
"Remote schemaLocation blocked: " + loc
164+
+ " (use setCustomResolver to opt in)");
165+
}
166+
return delegate.resolveEntity(ns, loc, base);
167+
}
168+
});
144169
}
145170

146171
return schemaCollection.read(element);

modules/kernel/test/org/apache/axis2/deployment/URIResolverTest.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,39 @@
2727

2828
public class URIResolverTest extends TestCase {
2929

30-
public void testResolveEntity() {
30+
/**
31+
* Verify that remote http/https URLs are blocked by the SSRF
32+
* hardening in AAR and WAR resolvers. The resolvers should return
33+
* an empty InputSource instead of fetching the remote URL.
34+
*/
35+
public void testRemoteUrlBlocked() {
3136
AARFileBasedURIResolver aar = new AARFileBasedURIResolver(null);
32-
WarFileBasedURIResolver war = new WarFileBasedURIResolver(null);
3337
InputSource inputSource = aar.resolveEntity(null,
3438
"http://www.test.org/test.xsd",
3539
"http://www.test.org/schema.xsd");
3640
assertNotNull(inputSource);
37-
assertEquals(inputSource.getSystemId(), "http://www.test.org/test.xsd");
41+
// Should return empty InputSource, not one with the remote URL
42+
assertNull("AAR resolver must block remote http URLs (SSRF)",
43+
inputSource.getSystemId());
44+
45+
WarFileBasedURIResolver war = new WarFileBasedURIResolver(null);
3846
inputSource = war.resolveEntity(null, "http://www.test.org/test.xsd",
3947
"http://www.test.org/schema.xsd");
4048
assertNotNull(inputSource);
41-
assertEquals(inputSource.getSystemId(), "http://www.test.org/test.xsd");
49+
assertNull("WAR resolver must block remote http URLs (SSRF)",
50+
inputSource.getSystemId());
51+
}
52+
53+
/**
54+
* Verify that https URLs are also blocked.
55+
*/
56+
public void testHttpsUrlBlocked() {
57+
AARFileBasedURIResolver aar = new AARFileBasedURIResolver(null);
58+
InputSource inputSource = aar.resolveEntity(null,
59+
"https://www.test.org/test.xsd",
60+
"https://www.test.org/schema.xsd");
61+
assertNotNull(inputSource);
62+
assertNull("AAR resolver must block remote https URLs (SSRF)",
63+
inputSource.getSystemId());
4264
}
4365
}

0 commit comments

Comments
 (0)