tags:

views:

27

answers:

0

I am having a problem validating some XML input data on my WebService. I am including everything you should need to reproduce the error, so hopefully someone will be able to help me. Thank you in advance!

I have some hacker who is sending me some invalid XML to my public interface and instead of being detected in my XSD validation, it crashes the entire IIS WebApp before it even gets to the XSD, and I CAN'T GET AROUND IT! Here is the XML that he sends:

<?xml version="1.0"?>
<Security xmlns="">
    <SecurityPermissions>
        <SecurityPermission ActionID="90" Value="1"></SecurityPermission>
        <SecurityPermission ActionID="80" Value="1"></SecurityPermission>
    </SecurityPermissions>
    <C><C><C><C><C>...</C></C></C></C></C>
</Security>

The thing that is invalid is the nested <C> nodes. I have shortened the XML here, but they are like 1000 levels deep.

For the full XML, please download it HERE.

For the XSD file, please download it HERE.

Here is the XSD schema:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema targetNamespace="UpdatePermissions.xsd" xmlns="UpdatePermissions.xsd" xmlns:mstns="UpdatePermissions.xsd" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" >
    <xs:element name="Security">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="SecurityPermissions" minOccurs="1" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="SecurityPermission" minOccurs="0" maxOccurs="unbounded">
                                <xs:complexType>
                                    <xs:attribute name="ActionID" type="xs:int" use="required" />
                                    <xs:attribute name="Value" type="xs:int" use="required" />
                                </xs:complexType>
                            </xs:element>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

IMPORTANT POINT:

Since the XML input has xmlns="" (and this is how ALL of my clients pass it in), I am forced to modify the inputted XML with a new xmlns value (I set it to xmlns="UpdatePermissions.xsd") in order for the XSD validation to work. Since you can't just set the xmlns on an existing XmlElement, I have to copy the XML to a XmlDocumentFragment. This - along with the nested nodes - is crashing my app with a StackOverflowException which can't be caught in a try..catch. Here's what it looks like:

    [WebMethod]
    public XmlElement UpdatePermissions(XmlElement xInput)
    {
        var errors = ValidateUpdatePermissionsXml(xInput);

        if (errors.Count > 0)
            throw new Exception(" I hate everything");

        // do other processing

        return xInput;
    }


    public List<string> ValidateUpdatePermissionsXml(XmlElement xInput)
    {
        // set xmlns since it isn't provided by caller but we need it to validate against XSD
        xInput.SetAttribute("xmlns", "UpdatePermissions.xsd");

        // transfer XML to a fragment otherwise xmlns attribute we set wouldn't take effect
        var fragment = xInput.OwnerDocument.CreateDocumentFragment();
        var xmlString = xInput.OuterXml; //  <---- THIS IS WHERE THE APP CRASHES
        fragment.InnerXml = xmlString;

        //setup errors array
        var errors = new List<string>();

        //read in schema
        var schemaStream = File.OpenRead(@"c:\UpdatePermissions.xsd");
        var schema = XmlSchema.Read(schemaStream, null);
        var schemaSet = new XmlSchemaSet();
        schemaSet.Add(schema);

        //setup validation settings
        var readerSettings = new XmlReaderSettings();
        readerSettings.ValidationType = ValidationType.Schema;
        readerSettings.Schemas = schemaSet;
        readerSettings.ValidationEventHandler += (s, e) => errors.Add(e.Exception.Message);

        //create readers
        var nodeReader = new XmlNodeReader(fragment);
        var reader = XmlReader.Create(nodeReader, readerSettings);

        //read
        while (reader.Read()) ;

        return errors;
    }

Here is my unit test which always fails:

    [TestMethod]
    public void TestXSDValidation_Invalid_DeeplyNestedNodes()
    {
        //load Xml from file into XmlElement
        var badXml = File.ReadAllText(@"c:\UpdatePermissions_BadRequest.xml");
        var doc = new XmlDocument();
        doc.LoadXml(badXml);
        var badElement = doc.DocumentElement;

        var errors = ValidateUpdatePermissionsXml(badElement);

        Assert.AreEqual(1, errors.Count);
        Assert.AreEqual("The element 'SecurityPermissions' in namespace 'UpdatePermissions.xsd' has invalid child element 'C' in namespace 'UpdatePermissions.xsd'. List of possible elements expected: 'SecurityPermission' in namespace 'UpdatePermissions.xsd'.", errors[0]);
    }

It appears that the call to .OuterXml fails with a StackOverflowException because of how deeply the nodes are nested. All of my attempts to write the XML to a string by other means have failed as well.

The Problem

What I can't figure out is how I can accept the XML which has the EMPTY xmlns attribute value, and still do the validation without having to copy the XML to the fragment. Everything I have tried that writes the XML out in some way fails and crashes the app. Try it and you will see what I mean!