Php: Soap: Validate namespace of Body children
Contents
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.xsd
The 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