View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2015-2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.util.xml;
18  
19  import java.lang.reflect.Method;
20  import javax.xml.XMLConstants;
21  import javax.xml.parsers.DocumentBuilder;
22  import javax.xml.parsers.DocumentBuilderFactory;
23  import javax.xml.parsers.ParserConfigurationException;
24  import javax.xml.parsers.SAXParser;
25  import javax.xml.parsers.SAXParserFactory;
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  import org.xml.sax.SAXException;
29  
30  /**
31   * Utility classes for handling XML.
32   */
33  public final class XMLUtils {
34  
35      private static final Logger LOGGER = LoggerFactory.getLogger(XMLUtils.class);
36      private static final Object SECURITY_MANAGER;
37      private static final Integer ENTITY_EXP_LIMIT =
38              Integer.getInteger("org.forgerock.util.xml.entity.expansion.limit", 5000);
39  
40      /**
41       * When Xerces is used for XML parsing, the only way to control entityExpansionLimit is to override the default
42       * SecurityManager. The following block will ensure that a Xerces SecurityManager is created and configured to have
43       * a less permissive entityExpansionLimit.
44       * In case Xerces is not used, but the JDK's XML parser implementation is leveraged, applications should enforce
45       * entity expansion limits by following the <a href="JAXP.java.net/1.4/JAXP-Compatibility.html#JAXP_security">
46       * JAXP configuration guide</a>.
47       */
48      static {
49          Object securityManager = null;
50          try {
51              Class<?> securityManagerClass = Class.forName("org.apache.xerces.util.SecurityManager");
52              securityManager = securityManagerClass.newInstance();
53              Method setEntityExpansionLimit = securityManagerClass.getMethod("setEntityExpansionLimit", int.class);
54              setEntityExpansionLimit.invoke(securityManager, ENTITY_EXP_LIMIT);
55          } catch (ClassNotFoundException ex) {
56              LOGGER.debug("Not using Xerces");
57          } catch (Exception ex) {
58              LOGGER.debug("Unable to set expansion limit for Xerces, using default settings", ex);
59              securityManager = null;
60          }
61          SECURITY_MANAGER = securityManager;
62      }
63  
64      private XMLUtils() {
65          // No impl.
66      }
67  
68      /**
69       * Provides a secure DocumentBuilder implementation, which is protected against
70       * different types of entity expansion attacks and makes sure that only locally
71       * available DTDs can be referenced within the XML document.
72       * @param validating Whether the returned DocumentBuilder should validate input.
73       * @return A secure DocumentBuilder instance.
74       * @throws javax.xml.parsers.ParserConfigurationException In case xerces does not support one
75       * of the required features.
76       */
77      public static DocumentBuilder getSafeDocumentBuilder(boolean validating) throws ParserConfigurationException {
78          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
79          dbf.setValidating(validating);
80          dbf.setNamespaceAware(true);
81          dbf.setXIncludeAware(false);
82          dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
83          dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
84          dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
85          dbf.setExpandEntityReferences(false);
86          if (SECURITY_MANAGER != null) {
87              dbf.setAttribute("http://apache.org/xml/properties/security-manager", SECURITY_MANAGER);
88          }
89          try {
90              dbf.setAttribute("http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit", ENTITY_EXP_LIMIT);
91          } catch (IllegalArgumentException ie) { }
92          DocumentBuilder db = dbf.newDocumentBuilder();
93          db.setEntityResolver(new XMLHandler());
94          return db;
95      }
96  
97      /**
98       * Provides a secure SAXParser instance, which is protected against different
99       * types of entity expension, DoS attacks and makes sure that only locally
100      * available DTDs can be referenced within the XML document.
101      * @param validating Whether the returned DocumentBuilder should validate input.
102      * @return A secure SAXParser instance.
103      * @throws ParserConfigurationException In case Xerces does not support one of
104      * the required features.
105      * @throws SAXException In case Xerces does not support one of the required
106      * features.
107      */
108     public static SAXParser getSafeSAXParser(boolean validating) throws ParserConfigurationException, SAXException {
109         SAXParserFactory saxFactory = SAXParserFactory.newInstance();
110         saxFactory.setValidating(validating);
111         saxFactory.setNamespaceAware(true);
112         saxFactory.setXIncludeAware(false);
113         saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
114         saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
115         saxFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
116         SAXParser sp = saxFactory.newSAXParser();
117         if (SECURITY_MANAGER != null) {
118             sp.setProperty("http://apache.org/xml/properties/security-manager", SECURITY_MANAGER);
119         }
120         try {
121             sp.setProperty("http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit", ENTITY_EXP_LIMIT);
122         } catch (Exception ex) { }
123         sp.getXMLReader().setEntityResolver(new XMLHandler());
124         return sp;
125     }
126 }