Php: Soap: Validate namespace of Body children
Problem
When a SOAP request is in a different namespace then is requested by the WSDL, SoapServer still processes the request. Now that I'm validating the request, if the soap-Body element contains an element in a different namespace, this element will bypass the validation but will nevertheless be processed.
For example, in the SOAP request underneath to a SoapServer with uri `http://mynamespace', the MyAction element will be validated against the generated wsdl/xsd namespace `http://mynamespace/'.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns="http://mynamespace/">
<soap:Header/>
<soap:Body>
<MyAction/>
</soap:Body>
</soap:Envelope>Whereas in the SOAP request underneath, the MyAction element will not be validated because its namespace is `http://othernamespace'. Nevertheless SoapServer *will* process the `MyAction' element.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns="http://othernamespace/">
<soap:Header/>
<soap:Body>
<MyAction/>
</soap:Body>
</soap:Envelope>Environment
- php-5.5.9
- zendframework-2.2.10
Solution
Modify the soap-envelope-schema, to allow only elements with specific namespaces within the Body element.
Download the soap-envelope-schema to a local file soap-envelope.xsd and import this file in the local schema validation:
<xsd:import namespace="http://schemas.xmlsoap.org/soap/envelope/"
schemaLocation="soap-envelope.xsd"/>The soap-envelope-schema contains this definition:
<xs:element name="Body" type="tns:Body" />
<xs:complexType name="Body" >
<xs:sequence>
<xs:any namespace="##any"
minOccurs="0"
maxOccurs="unbounded"
processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##any"
processContents="lax" />
</xs:complexType>By modifying this definition to not allow *any* namespace but only mynamespace:
<xs:element name="Body" type="tns:Body" />
<xs:complexType name="Body" >
<xs:sequence>
<xs:any namespace="http://mynamespace"
minOccurs="0"
maxOccurs="unbounded"
processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="http://mynamespace"
processContents="lax" />
</xs:complexType>the Body element may only contain elements with the namespace http://mynamespace.
Script to transform soap-envelope.xsd
With an XSLT stylesheet soap-envelope.xslt you can leave the original XML Schema (soap-envelope.orig.xsd) intact and insert your own namespace using this command:
xsltproc --stringparam namespace 'http://mynamespace/' \
soap-envelope.xslt soap-envelope.orig.xsd > soap-envelope.xsdThe soap-envelope.xslt:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<xsl:param name="namespace"></xsl:param>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xs:complexType[@name = 'Body']/xs:sequence/xs:any">
<xsl:element name="xs:any">
<xsl:attribute name="namespace"><xsl:value-of select="$namespace"/></xsl:attribute>
<xsl:attribute name="minOccurs">0</xsl:attribute>
<xsl:attribute name="maxOccurs">unbounded</xsl:attribute>
<xsl:attribute name="processContents">lax</xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template match="xs:complexType[@name = 'Body']/xs:anyAttribute">
<xsl:element name="xs:anyAttribute">
<xsl:attribute name="namespace"><xsl:value-of select="$namespace"/></xsl:attribute>
<xsl:attribute name="processContents">lax</xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>Keywords
SoapServer, SoapClient, Zend\Soap\Server