.NET XXE Prevention
Secure XML parsing configuration for .NET Framework, .NET Core, and .NET 5+ applications to prevent XXE vulnerabilities.
Overview
.NET provides multiple XML parsing APIs with varying security defaults. Understanding which API you're using and its default behavior is critical for XXE prevention.
Key .NET XML APIs:
- XmlDocument - Legacy DOM parser (vulnerable by default in .NET Framework)
- XmlTextReader - Stream-based reader (vulnerable by default)
- XmlReader - Abstract base class (secure defaults in .NET 4.5.2+)
- XDocument (LINQ to XML) - Modern API (secure defaults in .NET 4.5.2+)
- XmlSerializer - Object serialization (requires configuration)
Security Evolution:
- .NET Framework < 4.5.2: Vulnerable by default
- .NET Framework 4.5.2+: Secure defaults for XmlReader
- .NET Core / .NET 5+: Secure defaults for all parsers
Important: Even with secure defaults, explicit configuration is recommended for defense in depth.
Vulnerable XmlDocument (.NET Framework)
1using System.Xml;
2
3public class VulnerableXMLParser
4{
5 public void ParseXML(string xmlData)
6 {
7 // VULNERABLE in .NET Framework < 4.5.2
8 // XmlResolver enabled by default
9 XmlDocument doc = new XmlDocument();
10 doc.LoadXml(xmlData);
11
12 // XXE payload will be processed:
13 // External entities loaded and expanded
14 // File disclosure and SSRF possible
15
16 string content = doc.InnerText;
17 Console.WriteLine(content);
18 }
19}Secure XmlDocument Configuration
1using System.Xml;
2
3public class SecureXMLParser
4{
5 public void ParseXML(string xmlData)
6 {
7 XmlDocument doc = new XmlDocument();
8
9 // SECURE: Disable XmlResolver (prevents external entity loading)
10 doc.XmlResolver = null; // Critical security setting
11
12 // Additional security: Use safe reader settings
13 XmlReaderSettings settings = new XmlReaderSettings
14 {
15 DtdProcessing = DtdProcessing.Prohibit, // Disallow DTDs completely
16 XmlResolver = null // Disable external entity resolution
17 };
18
19 using (StringReader stringReader = new StringReader(xmlData))
20 using (XmlReader reader = XmlReader.Create(stringReader, settings))
21 {
22 doc.Load(reader);
23 }
24
25 string content = doc.InnerText;
26 Console.WriteLine(content);
27 }
28}Secure XmlReader Configuration (Recommended)
1using System.Xml;
2using System.IO;
3
4public class SecureXmlReaderParser
5{
6 public void ParseXML(string xmlData)
7 {
8 // RECOMMENDED: Use XmlReaderSettings with explicit security
9 XmlReaderSettings settings = new XmlReaderSettings
10 {
11 // Primary defense: Prohibit DTD processing
12 DtdProcessing = DtdProcessing.Prohibit, // Strongest protection
13
14 // If DTDs are required, use DtdProcessing.Parse with secure resolver:
15 // DtdProcessing = DtdProcessing.Parse,
16 // XmlResolver = null, // Disable external entities
17
18 // Additional security settings
19 XmlResolver = null, // Disable external entity resolution
20 MaxCharactersFromEntities = 0, // Prevent entity expansion
21
22 // Optional: Prevent DoS via large documents
23 MaxCharactersInDocument = 10000000 // 10MB limit
24 };
25
26 using (StringReader stringReader = new StringReader(xmlData))
27 using (XmlReader reader = XmlReader.Create(stringReader, settings))
28 {
29 while (reader.Read())
30 {
31 if (reader.NodeType == XmlNodeType.Element)
32 {
33 Console.WriteLine($"Element: {reader.Name}");
34 }
35 }
36 }
37 }
38}Secure XDocument (LINQ to XML)
1using System.Xml.Linq;
2using System.Xml;
3using System.IO;
4
5public class SecureXDocumentParser
6{
7 public void ParseXML(string xmlData)
8 {
9 // XDocument is secure by default in .NET 4.5.2+
10 // But explicit configuration is recommended
11
12 XmlReaderSettings settings = new XmlReaderSettings
13 {
14 DtdProcessing = DtdProcessing.Prohibit,
15 XmlResolver = null
16 };
17
18 using (StringReader stringReader = new StringReader(xmlData))
19 using (XmlReader reader = XmlReader.Create(stringReader, settings))
20 {
21 XDocument doc = XDocument.Load(reader);
22
23 // Process XML safely with LINQ
24 var elements = from elem in doc.Descendants()
25 where elem.Name == "data"
26 select elem.Value;
27
28 foreach (var value in elements)
29 {
30 Console.WriteLine(value);
31 }
32 }
33 }
34}Secure XmlSerializer
1using System.Xml.Serialization;
2using System.Xml;
3using System.IO;
4
5public class Person
6{
7 public string Name { get; set; }
8 public int Age { get; set; }
9}
10
11public class SecureXmlSerializerParser
12{
13 public Person DeserializeXML(string xmlData)
14 {
15 XmlSerializer serializer = new XmlSerializer(typeof(Person));
16
17 // Secure XmlReader settings
18 XmlReaderSettings settings = new XmlReaderSettings
19 {
20 DtdProcessing = DtdProcessing.Prohibit,
21 XmlResolver = null,
22 MaxCharactersFromEntities = 0
23 };
24
25 using (StringReader stringReader = new StringReader(xmlData))
26 using (XmlReader reader = XmlReader.Create(stringReader, settings))
27 {
28 // Deserialize with secure reader
29 Person person = (Person)serializer.Deserialize(reader);
30 return person;
31 }
32 }
33}DtdProcessing Options Explained
DtdProcessing.Prohibit (Recommended):
- Completely disallows DTD processing
- Parser throws XmlException if DTD found
- Strongest protection against XXE and DoS
- Use this unless DTD validation required
DtdProcessing.Ignore:
- Skips over DTD declarations without processing
- Less safe than Prohibit
- Can still be vulnerable in some scenarios
- Not recommended
DtdProcessing.Parse:
- Allows DTD processing
- Only use with XmlResolver = null
- Required if you need DTD validation
- Must be explicitly secured
Best Practice: Always use DtdProcessing.Prohibit unless you have a specific requirement for DTD validation. If DTDs are needed, use DtdProcessing.Parse with XmlResolver = null.
ASP.NET Configuration
1using Microsoft.AspNetCore.Mvc;
2using System.Xml;
3using System.IO;
4
5[ApiController]
6[Route("api/[controller]")]
7public class XmlController : ControllerBase
8{
9 [HttpPost]
10 public IActionResult ProcessXml([FromBody] string xmlData)
11 {
12 try
13 {
14 // Secure XML processing in ASP.NET Core
15 XmlReaderSettings settings = new XmlReaderSettings
16 {
17 DtdProcessing = DtdProcessing.Prohibit,
18 XmlResolver = null,
19 MaxCharactersFromEntities = 0,
20 MaxCharactersInDocument = 10000000, // 10MB limit
21
22 // Additional ASP.NET security
23 ConformanceLevel = ConformanceLevel.Document,
24 IgnoreComments = true,
25 IgnoreProcessingInstructions = true
26 };
27
28 using (StringReader stringReader = new StringReader(xmlData))
29 using (XmlReader reader = XmlReader.Create(stringReader, settings))
30 {
31 XmlDocument doc = new XmlDocument();
32 doc.XmlResolver = null; // Always set this for XmlDocument
33 doc.Load(reader);
34
35 // Process safely
36 return Ok(new { status = "success" });
37 }
38 }
39 catch (XmlException ex)
40 {
41 // Don't leak XML parse details in error messages
42 return BadRequest(new { error = "Invalid XML format" });
43 }
44 }
45}Framework-Specific Guidance
.NET Framework 4.5.1 and Earlier:
- All XML parsers vulnerable by default
- MUST explicitly set XmlResolver = null
- MUST set DtdProcessing = DtdProcessing.Prohibit
- Upgrade to 4.5.2+ if possible
.NET Framework 4.5.2 - 4.8:
- XmlReader secure by default (XmlResolver = null)
- XmlDocument still requires XmlResolver = null
- Explicit configuration still recommended
- Consider migrating to .NET Core/.NET 5+
.NET Core 2.0+ / .NET 5+:
- All XML parsers secure by default
- XmlResolver = null by default
- DTD processing disabled by default for most APIs
- Still use explicit settings for defense in depth
Web.config Settings (.NET Framework):
<configuration>
<appSettings>
<!-- These don't directly affect XML parsing -->
<!-- Must configure parsers in code -->
</appSettings>
</configuration>
Testing XXE Prevention
1using Xunit;
2using System.Xml;
3using System;
4
5public class XXEPreventionTests
6{
7 [Fact]
8 public void TestXXEBlocked()
9 {
10 string xxePayload = @"
11 <?xml version=""1.0"" encoding=""UTF-8""?>
12 <!DOCTYPE root [
13 <!ENTITY xxe SYSTEM ""file:///etc/passwd"">
14 ]>
15 <root>
16 <data>&xxe;</data>
17 </root>";
18
19 XmlReaderSettings settings = new XmlReaderSettings
20 {
21 DtdProcessing = DtdProcessing.Prohibit,
22 XmlResolver = null
23 };
24
25 // Should throw XmlException
26 Assert.Throws<XmlException>(() =>
27 {
28 using (StringReader stringReader = new StringReader(xxePayload))
29 using (XmlReader reader = XmlReader.Create(stringReader, settings))
30 {
31 while (reader.Read()) { }
32 }
33 });
34 }
35
36 [Fact]
37 public void TestValidXMLAccepted()
38 {
39 string validXml = @"<root><data>valid content</data></root>";
40
41 XmlReaderSettings settings = new XmlReaderSettings
42 {
43 DtdProcessing = DtdProcessing.Prohibit,
44 XmlResolver = null
45 };
46
47 // Should parse without error
48 using (StringReader stringReader = new StringReader(validXml))
49 using (XmlReader reader = XmlReader.Create(stringReader, settings))
50 {
51 while (reader.Read())
52 {
53 Assert.NotNull(reader);
54 }
55 }
56 }
57}.NET XXE Prevention Checklist
âś… Code Configuration:
- Set DtdProcessing = DtdProcessing.Prohibit on all XmlReaderSettings
- Set XmlResolver = null on all XmlDocument instances
- Set MaxCharactersFromEntities = 0 to prevent entity expansion
- Use XmlReader.Create() with secure settings instead of direct parsers
âś… Framework Version:
- Identify which .NET version you're using
- Understand default security posture for that version
- Upgrade to .NET Core/.NET 5+ if possible
- Apply security patches regularly
âś… Input Validation:
- Reject XML containing <!DOCTYPE if not needed
- Validate XML against expected schema
- Limit XML document size before parsing
- Sanitize error messages (don't leak XML parse details)
âś… Testing:
- Unit tests with XXE payloads (should throw exception)
- Integration tests with valid XML (should succeed)
- Security scanning with XXE detection tools
- Regular penetration testing
âś… Defense in Depth:
- Network egress filtering (block outbound connections)
- File system permissions (limit file access)
- Least privilege (run app with minimal permissions)
- Monitoring and alerting for XXE attempts