Insecure Processing of Data
This category covers the following issues:
Insecure Deserialization
Why is this important?
Serialization is the process of translating data structures storable formats. In Java, objects can be serialized into strings and vice-versa, strings can be deserialized into objects. This functionality can be accessed with methods related to YAML, JSON, CSV, and Marshalling. Insecure deserialization describes the processing of malicious data which in term allows hackers to execute arbitrary code in the context of your application. These issues are common and have been the cause of many high profile breaches.
Fixing Insecure Deserialization
Option A: Don't parse untrusted data with XMLDecoder
XMLDecoder should not be used to parse untrusted data. Deserializing user input can lead to arbitrary code execution. This is possible because XMLDecoder supports arbitrary method invocation. This capability is intended to call setter methods, but in practice, any method can be called.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code that passes untrusted data to XMLDecoder.
XMLDecoder d = new XMLDecoder(in);
try {
Object result = d.readObject();
}
[...]
- Follow the steps detailed here: Using XMLDecoder to execute server-side Java Code on a Restlet application
- Test it
- Ship it 🚢 and relax 🌴
Option B: Avoid deserializing untrusted objects with ObjectInputStream
Object deserialization of untrusted data can lead to remote code execution, if there is a class in classpath that allows the trigger of malicious operation.
Libraries developers tend to fix class that provided potential malicious trigger. There are still classes that are known to trigger Denial of Service.
Deserialization is a sensible operation that has a great history of vulnerabilities. The web application might become vulnerable as soon as a new vulnerability is found in the Java Virtual Machine.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code that passes untrusted data to ObjectInputStream.
public UserData deserializeObject(InputStream receivedFile) throws IOException, ClassNotFoundException {
try (ObjectInputStream in = new ObjectInputStream(receivedFile)) {
return (UserData) in.readObject();
}
}
- Follow the steps detailed here: Deserialization of untrusted data
- Test it
- Ship it 🚢 and relax 🌴
Option C: Avoid deserializing untrusted objects with Jackson
When the Jackson databind library is used incorrectly the deserialization of untrusted data can lead to remote code execution, if there is a class in classpath that allows the trigger of malicious operation.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
public class Example {
static class ABean {
public int id;
public Object obj;
}
static class AnotherBean {
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) // or JsonTypeInfo.Id.MINIMAL_CLASS
public Object obj;
}
public void example(String json) throws JsonMappingException {
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
mapper.readValue(json, ABean.class);
}
public void exampleTwo(String json) throws JsonMappingException {
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(json, AnotherBean.class);
}
}
- Explicitly define what types and subtypes you want to be available when using polymorphism through
JsonTypeInfo.Id.NAME
. - Test it
- Ship it 🚢 and relax 🌴
Option D: Handle untrusted XSLTs safely
XSLT (Extensible Stylesheet Language Transformations) is a language for transforming XML documents into other XML documents. It is possible to attach malicious behavior to those style sheets. Therefore, if attackers can control the content or the source of the style sheet, they might be able to trigger remote code execution.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
<x:transform xml="${xmlData}" xslt="${xsltControlledByUser}" />
or this:
Source xslt = new StreamSource(new FileInputStream(inputUserFile)); //Dangerous source
Transformer transformer = TransformerFactory.newInstance().newTransformer(xslt);
Source text = new StreamSource(new FileInputStream("/data_2_process.xml"));
transformer.transform(text, new StreamResult(...));
- And replace it with this:
TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Source xslt = new StreamSource(new FileInputStream(inputUserFile));
Transformer transformer = factory.newTransformer(xslt);
- Test it
- Ship it 🚢 and relax 🌴
More information
- Java deserialization flaws: Part 2, XML deserialization
- CWE-20: Improper Input Validation
- XSLT Hacking Encyclopedia
- The hidden dangers of XSLTProcessor - Remote XSL injection
- WASC: Path Traversal
- OWASP: Path Traversal
Insecure XML Processing
Why is this important?
XML is a powerful protocol, that if abused by attackers, can lead to a range of issues, such as:
- Access to sensitive data
- Denial of Service
- Attacks against the internal network
Fixing XML Processing Issues
Option A: Prevent XML External Entity Attacks
XML External Entity (XXE) attacks can occur when an XML parser supports XML entities while processing XML received from an untrusted source.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
public void parseXML(InputStream input) throws XMLStreamException {
XMLInputFactory factory = XMLInputFactory.newFactory();
XMLStreamReader reader = factory.createXMLStreamReader(input);
[...]
}
- And replace it with this:
public void parseXML(InputStream input) throws XMLStreamException {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
XMLStreamReader reader = factory.createXMLStreamReader(input);
[...]
}
or this:
public void parseXML(InputStream input) throws XMLStreamException {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader reader = factory.createXMLStreamReader(input);
[...]
}
- Test it
- Ship it 🚢 and relax 🌴
Option B: Prevent XEE through XPathExpression
XML External Entity (XXE) attacks can occur when an XML parser supports XML entities while processing XML received from an untrusted source.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder builder = df.newDocumentBuilder();
[...]
xPathExpr.evaluate( builder.parse(inputStream));
- And replace it with this:
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder builder = df.newDocumentBuilder();
[...]
xPathExpr.evaluate( builder.parse(inputStream));
or this:
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder builder = df.newDocumentBuilder();
[...]
xPathExpr.evaluate( builder.parse(inputStream));
- Test it
- Ship it 🚢 and relax 🌴
Option C: Prevent XEE through SAXParser
XML External Entity (XXE) attacks can occur when an XML parser supports XML entities while processing XML received from an untrusted source.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(inputStream, customHandler);
- And replace it with this:
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
SAXParser parser = spf.newSAXParser();
parser.parse(inputStream, customHandler);
or this:
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
SAXParser parser = spf.newSAXParser();
parser.parse(inputStream, customHandler);
- Test it
- Ship it 🚢 and relax 🌴
Option D: Prevent XEE through XMLReader
XML External Entity (XXE) attacks can occur when an XML parser supports XML entities while processing XML received from an untrusted source.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setContentHandler(customHandler);
reader.parse(new InputSource(inputStream));
- And replace it with this:
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
reader.setContentHandler(customHandler);
reader.parse(new InputSource(inputStream));
or this:
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setContentHandler(customHandler);
reader.parse(new InputSource(inputStream));
- Test it
- Ship it 🚢 and relax 🌴
Option E: Prevent XEE through DocumentBuilder
XML External Entity (XXE) attacks can occur when an XML parser supports XML entities while processing XML received from an untrusted source.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(input);
- And replace it with this:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input);
or this:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input);
- Test it
- Ship it 🚢 and relax 🌴
Option F: Prevent XEE through TransformerFactory
XML External Entity (XXE) attacks can occur when an XML parser supports XML entities while processing XML received from an untrusted source.
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(input, result);
- And replace it with this:
TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(XMLConstants.ACC`ESS_EXTERNAL_DTD, "all");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "all");
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(input, result);`
or this:
TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(input, result);
- Test it
- Ship it 🚢 and relax 🌴
Option F: Prevent XPATH Injection
XPath injection risks are similar to SQL injection. If the XPath query contains untrusted user input, the complete data source could be exposed. This could allow an attacker to access unauthorized data or maliciously modify the target XML.
References:
- WASC-39: XPath Injection
- OWASP: Top 10 2013-A1-Injection
- CWE-643: Improper Neutralization of Data within XPath Expressions('XPath Injection')
- CERT: IDS09-J. Prevent XPath Injection (archive)
- Black Hat Europe 2012: Hacking XPath 2.0
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression xlogin = xpath.compile("//users/user[login/text()='" + login.getUserName() + "' and password/text() = '" + login.getPassword() + "']/home_dir/text()");
Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("db.xml"));
String homedir = xlogin.evaluate(d);
- Use parameterized XPath queries such as XQuery.
- Test it
- Ship it 🚢 and relax 🌴
Option G: Prevent LDAP Injection
Just like SQL, all inputs passed to an LDAP query need to be passed in safely. Unfortunately, LDAP doesn't have prepared statement interfaces like SQL. Therefore, the primary defense against LDAP injection is strong input validation of any untrusted data before including it in an LDAP query.
References:
- WASC-29: LDAP Injection
- OWASP: Top 10 2013-A1-Injection
- CWE-90: Improper Neutralization of Special Elements used in an LDAP Query ('LDAP Injection')
- LDAP Injection Guide: Learn How to Detect LDAP Injections and Improve LDAP Security
Detailed Instructions
- Go through the issues that GuardRails identified in the PR.
- Look for code like this:
NamingEnumeration<SearchResult> answers = context.search("dc=People,dc=example,dc=com", "(uid=" + username + ")", ctrls);
- Follow the instructions provided by WASC-29.
- Test it
- Ship it 🚢 and relax 🌴