views:

292

answers:

3

I have to set up an XML "web service" that receives a POST where the 'Content-type header will specify “text/xml”.'

What is the simplest way to get the XML into an XDocument for access by VB.NET's axis queries?

I don't believe the web service is guaranteed to follow any protocol (e.g. SOAP, etc); just specific tags and sub-tags for various requests, and it will use Basic Authentication, so I will have to process the headers.

(If it matters:
* the live version will use HTTPS, and
* the response will also be XML.)

A: 

When you create a Web Service you define the format of the XML you will receive, and it is up to the sender to meet your format.

I usually mock up the information my web service will receive as DataTables in a DataSet (since that closely mimics how I might store them in a database) then do a DataSet.getXML() on my mocked up DataSet (possibly also getting the default schema as well) to use as the template for the XML I am expecting to be "posted" to my web service.

Then, when my web service receives a post, I can simply take the XML sent and use the DataSet.readXML() on the XML posted ... and deal with the information sent in the DataSet.

Most of my web service "return" values are the results of queries based on the information posted, so I do the same to format the return values ... get the results of my return queried data in a DataSet, DataSet.getXML() .. and return it.

Ron Savage
Nope, we're implementing a "web service" against an existing specification, so *we* are *not* defining the XML format.
Mark Hurd
Ahh, I see what you mean - I usually refer to that as "using" or "consuming" a web service ... just terminology. :-)
Ron Savage
+1  A: 

I want to apologize in advance for not answering your question here, but I want to give a little warning. Perhaps it is already something you're taking into account, but you don't take the appropriate counter measures, your system can be easily shut down using a denial of service attack, when processing XML from an unknown source (both over HTTP and HTTPS).

There is a technique called XML Entity Expansion attacks. Look for instance at this innocent looking peace of XML that will bring your server to its knees when it tries to process it:

<!DOCTYPE foo [ 
<!ENTITY a "1234567890" > 
<!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;" > 
<!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;" > 
<!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;" > 
<!ENTITY e "&d;&d;&d;&d;&d;&d;&d;&d;" > 
<!ENTITY f "&e;&e;&e;&e;&e;&e;&e;&e;" > 
<!ENTITY g "&f;&f;&f;&f;&f;&f;&f;&f;" > 
<!ENTITY h "&g;&g;&g;&g;&g;&g;&g;&g;" > 
<!ENTITY i "&h;&h;&h;&h;&h;&h;&h;&h;" > 
<!ENTITY j "&i;&i;&i;&i;&i;&i;&i;&i;" > 
<!ENTITY l "&k;&k;&k;&k;&k;&k;&k;&k;" > 
<!ENTITY m "&l;&l;&l;&l;&l;&l;&l;&l;" > 
]> 
<foo>&m;</foo>

This little XML document of less than 500 bytes will make your server try to allocate at least 160 GB of memory.

You can protect yourself against this by validating the incoming XML (with an DTD) before processing it.

You can read more information about this attack, here.

Good luck.

Steven
+1 for the useful information. I know you've linked to your blog, but the direct link is http://blogs.msdn.com/tomholl/archive/2009/05/21/protecting-against-xml-entity-expansion-attacks.aspx .
Mark Hurd
You are right. That was laziness from my part. I changed the link to directly point to Tom Hollander's blog.
Steven
I removed my +1 vote for this "answer" because it removed my question from the unanswered view. Steven, edit it again so I can give you back your +1 :-)
Mark Hurd
@Mark: I of course love to get credits :-). I edited my answer.
Steven
+2  A: 

Given Steven's warning, the answer may be to parse Request.InputStream manually with Tom Holland's test first, followed by XDocument.Load in the Page_Load event.

A Google search initiated before I asked the question, but only checked after, found this, also suggesting I'm on the right track.

Also I was going to ask the question implied by my point that the response had to be XML too, as to what is the best way for that, but I've found an answer here.

In summary, the final code is:

 Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    If Request.ContentType <> "text/xml" Then _
        Throw New HttpException(500, "Unexpected Content-Type")

    Dim id = CheckBasicAuthentication

    Dim textReader = New IO.StreamReader(Request.InputStream)

    CheckXmlValidity(textReader)

    ' Reset the stream & reader '
    Request.InputStream.Seek(0, IO.SeekOrigin.Begin)
    textReader.DiscardBufferedData()

    Dim xmlIn = XDocument.Load(textReader)

    ' process XML in xmlIn '

    Dim xmlOut = <?xml version="1.0" encoding="UTF-8" ?>
                 <someresult>
                     <header>
                         <id><%= id.ToString() %></id>
                         <datestamp>To be inserted</datestamp>
                     </header>
                     <result/>
                 </someresult>

    ' Further generation of XML for output '

    xmlOut.<someresult>.<header>.<datestamp>.Value = Date.UtcNow.ToString(xmlDateFormat)
    xmlText.Text = xmlOut.ToString
End Sub

Private Function CheckBasicAuthentication() As Integer
    Dim httpAuthorisation = Request.Headers("Authorization")
    If Left(httpAuthorisation, 6).ToUpperInvariant <> "BASIC " Then _
        Throw New HttpException(401, "Basic Authentication Required")
    Dim authorization = Convert.FromBase64String(Mid(httpAuthorisation, 7))
    Dim credentials = Text.Encoding.UTF8.GetString(authorization).Split(":"c)
    Dim username = credentials(0)
    Dim password = credentials(1)

    Return ConfirmValidUser(username, password)
End Function

Private Shared Sub CheckXmlValidity(ByVal textReader As System.IO.StreamReader)
    Try
        ' Check for "interesting" xml documents. '
        Dim settings = New System.Xml.XmlReaderSettings()
        settings.XmlResolver = Nothing
        settings.MaxCharactersInDocument = 655360
        ' Successfully parse the file, otherwise an XmlException is to be thrown. '
        Dim reader = System.Xml.XmlReader.Create(textReader, settings)
        Try
            While reader.Read()
                'Just checking. '
            End While
        Finally
            reader.Close()
        End Try
    Catch ex As Exception
        Throw New HttpException(500, "Invalid Xml data", ex)
    End Try
End Sub

and the ASP.NET webpage.aspx is:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="webpage.aspx.vb" Inherits="WebPage" ContentType="text/xml" %>

<asp:Literal ID="xmlText" runat="server" Mode="PassThrough"></asp:Literal> 

NB Throwing HTTPException is not a valid final solution for unwanted scenarios.

Mark Hurd
+1. You deserve it.
Steven