This has been bugging me for a couple days. I'm trying to load a XML from an uploaded file to into an XmlDocument object and get the following yellow-screen-of-death:
For security reasons DTD is prohibited in this XML document. To enable DTD processing set the ProhibitDtd property on XmlReaderSettings to false and pass the settings into XmlReader.Create method.
Here's my code. You can clearly see I'm setting ProhibitDtd to false.
public static XmlDocument LoadXml(FileUpload fu)
{
var settings = new XmlReaderSettings
{
ProhibitDtd = false,
ValidationType = ValidationType.DTD
};
var sDtdPath = string.Format(#"{0}", HttpContext.Current.Server.MapPath("/includes/dtds/2.3/archivearticle.dtd"));
settings.Schemas.Add(null, sDtdPath);
var r = XmlReader.Create(new StreamReader(fu.PostedFile.InputStream), settings);
var document = new XmlDocument();
document.Load(r);
return document;
}
Add XmlResolver=null to your XmlReaderSettings. This will prevent the xmlDocument from trying to access the DTD. If you need to validate, do that in a separate operation.
Related
I want to change an attribute inside an xml file using C#.
Here is a sample XML file
<?xml version="1.0" encoding="us-ascii"?>
<Client>
<Age>25</Age>
<Weight>50</Weight>
</Client>
I tried loading the xml file using both XmlDocument and XDocument. They both take so much time (more than 5 minutes) to load.
Here is the code I am using to load the file:
string filePath = #"myFile.xml";
XmlDocument xmlData = new XmlDocument();
As per Google, the problem is that XDocument and XmlDocument will load all the DTDs for XML file, and this is why it takes much time. Is there a workaround for this? or maybe any alternative that allows me to change an attribute without loading all the DtDs?
You can control how DTDs are cached, parsed or used for validation with XmlReaderSettings and still use XDocument.
If you can take the time to cache the DTDs and changing them isn't part of your test, you could take the hit once and cache them.
If that's too much time or they aren't available and they aren't needed for your tests, you could skip DTD processing.
using (var reader = XmlReader.Create(_,
new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Ignore,
ValidationType = ValidationType.None,
//DtdProcessing = DtdProcessing.Parse,
//ValidationType = ValidationType.DTD,
XmlResolver = new XmlUrlResolver
{
CachePolicy = new RequestCachePolicy(RequestCacheLevel.CacheIfAvailable),
//CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore),
}
}))
{
var doc = XDocument.Load(reader);
//…
}
XmlReaderSettings has many other properties that sometimes come in handy.
Let's say we want to load an xml (cXML) and validate it against a DTD that we have stored locally. Here's the code for this:
XmlPreloadedResolver resolver = new XmlPreloadedResolver(XmlKnownDtds.None);
resolver.Add(new Uri(DocTypeSystemId), File.ReadAllText(#"C:\cXml.dtd"));
XmlReaderSettings settings = new XmlReaderSettings
{
ValidationType = ValidationType.DTD,
DtdProcessing = DtdProcessing.Parse
};
settings.ValidationEventHandler += Settings_ValidationEventHandler;
XmlParserContext context = new XmlParserContext(null, null, "cXML", null,
DocTypeSystemId, null, null, null, XmlSpace.None);
XmlReader reader = XmlReader.Create(stream, settings, context);
XDocument doc = XDocument.Load(reader);
Unfortunately in case the cXML input already comes with a DTD definition, the XmlReader will throw an XmlException stating: Message Cannot have multiple DTDs. Line 2, position 1.
If we remove the DOCTYPE from the input a warning is shown No DTD found. and the xml isn't validated.
It seems that XmlReader has hard time using an XmlParserContext.
If instead the reader is an instance of the obsolete XmlTextReader:
XmlTextReader textReader = new XmlTextReader(stream, XmlNodeType.Document, context);
XmlValidatingReader reader = new XmlValidatingReader(textReader);
reader.ValidationType = ValidationType.DTD;
reader.ValidationEventHandler += Settings_ValidationEventHandler;
Then there is no exception for multiple DTDs and the xml is validated.
Obviously there is a difference between how XmlTextReader and XmlReader function. They both seem to output a warning when the xml is missing a DOCTYPE which halts validation. The following calls are involved in the misunderstanding XmlValidatingReaderImpl.ProcessCoreReaderEvent() and DtdValidator.Validate() (where schemaInfo.SchemaType == SchemaType.DTD is false maybe because it's no DTD exists).
With all this in mind it seems better to just try to change/add the DOCTYPE element in the input xml than battle with XmlParserContext and the different reader implementations.
I am working with Mismo 2.3.1, dtd based schema. I converted the dtd to xsd and then generated c# code to serialize/deserialze object representations of the xml doc.
Given a valid mismo 2.3.1 xml doc, I can deserialize into my generated C# class.
I have code working to use XmlSerializer along with XmlReaderSettings and XmlSchmeas collection, reading in my converted xsd.
If I put xmlns="http://mySchema..." in the root element, and try to validate intentionally invalid xml, works as expected, my validation event gets pinged with accurate description.
If I take out the xmlns attribute, then i get "could not find schema information for element [my root element]"
Any idea on how to validate xml that comes in without the xmlns spec? Any settings to say to the serializer "use this schema when you come across this element"?
Thanks in advance!
static void Main() {
var settings = new XmlReaderSettings();
settings.NameTable = new NameTable();
var nsMgr = new XmlNamespaceManager(settings.NameTable);
nsMgr.AddNamespace("", "http://example.com/2013/ns"); // <-- set default namespace
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add(null, #"C:\XSDSchema.xsd"); // <-- set schema location for the default namespace
var parserCtx = new XmlParserContext(settings.NameTable, nsMgr, XmlSpace.Default);
using (var reader = XmlReader.Create(#"C:\file.xml", settings, parserCtx)) {
var serializer = new XmlSerializer(typeof(Foo));
Foo f = (Foo)serializer.Deserialize(reader);
}
}
We had a security audit on our code, and they mentioned that our code is vulnerable to EXternal Entity (XXE) attack. I am using following code -
string OurOutputXMLString=
"<ce><input><transaction><length>00000</length><tran_type>Login</tran_type></transaction><user><user_id>ce_userid</user_id><subscriber_name>ce_subscribername</subscriber_name><subscriber_id>ce_subscriberid</subscriber_id><group_id>ce_groupid</group_id><permissions></permissions></user><consumer><login_details><username>UnitTester9</username><password>pDhE5AsKBHw85Sqgg6qdKQ==</password><pin>tOlkiae9epM=</pin></login_details></consumer></input></ce>"
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(OurOutputXMLString);
In the audit report they say that it's failing because an XML entity can contain URLs that can resolve outside of intended control. XML entity resolver will attempt to resolve and retrieve external references. If attacker-controlled XML can be submitted to one of these functions, then the attacker could gain access to information about an internal network, local filesystem, or other sensitive data.
To avoid this I wrote the following code but it doesn't work.
MemoryStream stream =
new MemoryStream(System.Text.Encoding.Default.GetBytes(OurOutputXMLString));
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.MaxCharactersFromEntities = 6000;
XmlReader reader = XmlReader.Create(stream, settings);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(reader);
But I can see here that reader does not have any value to load into xmlDoc(XmlDocument).
Can anyone help where I am missing things?
External resources are resolved using the XmlResolver provided via XmlDocument.XmlResolver property. If your XML documents **should not contain any external resource **(for example DTDs or schemas) simply set this property to null:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = null;
xmlDoc.LoadXml(OurOutputXMLString);
If you want to filter where these URLs come from (for example to allow only certain domains) just derive your own class from XmlUrlResolver and override the ResolveUri() method. There you can check what the URL is and sanitize it (for example you can allow only URLs within your local network or from trusted sources).
For example:
class CustomUrlResovler : XmlUrlResolver
{
public override Uri ResolveUri(Uri baseUri, string relativeUri)
{
Uri uri = new Uri(baseUri, relativeUri);
if (IsUnsafeHost(uri.Host))
return null;
return base.ResolveUri(baseUri, relativeUri);
}
private bool IsUnsafeHost(string host)
{
return false;
}
}
Where IsUnsafeHost() is a custom function that check if the given host is allowed or not. See this post here on SO for few ideas. Just return null from ResolveUri() to save your code from this kind of attacks. In case the URI is allowed you can simply return the default XmlUrlResolver.ResolveUri() implementation.
To use it:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = new CustomUrlResolver();
xmlDoc.LoadXml(OurOutputXMLString);
For more details about how XML external resources are resolved just read Resolving External Resources on MS Docs. If your code is more complex than this example then you should definitely read Remarks section for XmlDocument.XmlResolver property.
So its better to use
new XmlDocument { XmlResolver = null };
Interestingly from .net 4.5.2 and 4.6, the default resolver behaves differently and does not use an XmlUrlResolver upfront implicitly to resolve any urls or locations as i seen.
//In pre 4.5.2 it is a security issue.
//In 4.5.2 it will not resolve any more the url references in dtd and such,
//Still better to avoid the below since it will trigger security warnings.
new XmlDocument();
Setting the XmlReaderSettings.DtdProcessing to DtdProcessing.Prohibit works totally fine in .NET 4.7.2. Here is what i used to test.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE demo
[
<!ELEMENT demo ANY >
<!ENTITY % extentity SYSTEM "https://www.hl7.org/documentcenter/public/wg/structure/CDA.xsl">
%extentity;
]>
<test>
Some random content
</test>
Saved the above content in a file and read the file from the following fragment of c# code.
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.MaxCharactersFromEntities = 6000;
//The following stream should be the filestream of the above content.
XmlReader reader = XmlReader.Create(stream, settings);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(reader);
I get the following exception.
For security reasons DTD is prohibited in this XML document. To enable DTD
processing set the DtdProcessing property on XmlReaderSettings to Parse and
pass the settings into XmlReader.Create method.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.ParseDoctypeDecl()
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlLoader.LoadNode(Boolean skipOverWhitespace)
at System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc)
at System.Xml.XmlDocument.Load(XmlReader reader)
I have an XML file without a Schema in the XML, and I need to validate the XML against a XSD schema. I have seen many examples where you inject the XSD in to the XML and then validate the XML. I don't want to change the XML, is it possible to validate the XML, against the schema without change the XML?
It's easy to code with a few lines in C#.
I created a simple command line interface utility that takes two parameters: XML, XSD and does verification.
You can download it here.
Here's the main code:
// 1- Read XML file content
reader = new XmlTextReader(XMLPath);
// 2- Read Schema file content
StreamReader SR = new StreamReader(XSDPath);
// 3- Create a new instance of XmlSchema object
XmlSchema Schema = new XmlSchema();
// 4- Set Schema object by calling XmlSchema.Read() method
Schema = XmlSchema.Read(SR,
new ValidationEventHandler(ReaderSettings_ValidationEventHandler));
// 5- Create a new instance of XmlReaderSettings object
XmlReaderSettings ReaderSettings = new XmlReaderSettings();
// 6- Set ValidationType for XmlReaderSettings object
ReaderSettings.ValidationType = ValidationType.Schema;
// 7- Add Schema to XmlReaderSettings Schemas collection
ReaderSettings.Schemas.Add(Schema);
// 8- Add your ValidationEventHandler address to
// XmlReaderSettings ValidationEventHandler
ReaderSettings.ValidationEventHandler +=
new ValidationEventHandler(ReaderSettings_ValidationEventHandler);
// 9- Create a new instance of XmlReader object
XmlReader objXmlReader = XmlReader.Create(reader, ReaderSettings);
// 10- Read XML content in a loop
while (objXmlReader.Read())
{ /*Empty loop*/}
You can add the schema to the xml doc
doc.Schemas.Add(schema);
And then validate it
bool xmlvalid = true;
string lastXmlError = "";
doc.Validate(new System.Xml.Schema.ValidationEventHandler(
delegate(object sender, System.Xml.Schema.ValidationEventArgs args)
{
if (args.Severity == System.Xml.Schema.XmlSeverityType.Error)
{
xmlvalid = false;
lastXmlError = args.Message;
}
}));
if (!xmlvalid)
//raise error
Provide a ValidationEventHandler only if you want to keep validating the document beyond the first validation error. Otherwise, just do this:
private bool ValidateDocument(string xmlFile, string xsdFile)
{
XmlReaderSettings settings = new XmlReaderSettings{ValidationType
= ValidationType.Schema};
settings.Schemas.Add(XmlSchema.Read(XmlReader.Create(xsdFile)));
XmlReader reader = XmlReader.Create(xmlFile, settings);
try
{
while(reader.Read());
return true;
}
catch (XmlException ex)
{
// XmlException indicates a validation error occurred.
return false;
}
}
The following links provide more information:
http://msdn.microsoft.com/en-us/library/1xe0740a.aspx
http://support.microsoft.com/kb/307379