Skip to content

Commit 0310e5e

Browse files
committed
Improve the security of XML parsers
Improve the security of XML parsers to prevent XML attacks like XML bombs and XXE attacks. Set default configuration of xml parsers to throw an exception when it find DTD in the XML and implement factory logic to give an opportunity for customer to override the way of creation xml parsers. DEVSIX-3270
1 parent f4a1829 commit 0310e5e

File tree

27 files changed

+1398
-379
lines changed

27 files changed

+1398
-379
lines changed

forms/src/main/java/com/itextpdf/forms/xfa/XfaForm.java

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -54,28 +54,24 @@ This file is part of the iText (R) project.
5454
import com.itextpdf.kernel.pdf.PdfString;
5555
import com.itextpdf.kernel.pdf.PdfVersion;
5656
import com.itextpdf.kernel.pdf.VersionConforming;
57+
import com.itextpdf.kernel.utils.XmlProcessorCreator;
5758
import com.itextpdf.kernel.xmp.XmlDomWriter;
5859

59-
import javax.xml.parsers.DocumentBuilder;
60-
import javax.xml.parsers.DocumentBuilderFactory;
61-
import javax.xml.parsers.ParserConfigurationException;
62-
6360
import java.io.ByteArrayInputStream;
6461
import java.io.ByteArrayOutputStream;
6562
import java.io.File;
6663
import java.io.FileInputStream;
6764
import java.io.IOException;
6865
import java.io.InputStream;
69-
import java.io.StringReader;
7066
import java.nio.charset.StandardCharsets;
7167
import java.util.HashMap;
7268
import java.util.Map;
73-
69+
import javax.xml.parsers.DocumentBuilder;
70+
import javax.xml.parsers.ParserConfigurationException;
7471
import org.w3c.dom.Document;
7572
import org.w3c.dom.Element;
7673
import org.w3c.dom.Node;
7774
import org.w3c.dom.NodeList;
78-
import org.xml.sax.EntityResolver;
7975
import org.xml.sax.InputSource;
8076
import org.xml.sax.SAXException;
8177

@@ -107,13 +103,14 @@ public XfaForm() {
107103

108104
/**
109105
* Creates an XFA form by the stream containing all xml information
106+
*
110107
* @param inputStream the InputStream
111108
*/
112109
public XfaForm(InputStream inputStream) {
113110
try {
114111
initXfaForm(inputStream);
115112
} catch (Exception e) {
116-
throw new PdfException(e);
113+
throw new PdfException(e.getMessage(), e);
117114
}
118115
}
119116

@@ -140,7 +137,7 @@ public XfaForm(PdfDictionary acroFormDictionary) {
140137
try {
141138
initXfaForm(xfa);
142139
} catch (Exception e) {
143-
throw new PdfException(e);
140+
throw new PdfException(e.getMessage(), e);
144141
}
145142
}
146143
}
@@ -157,7 +154,7 @@ public XfaForm(PdfDocument pdfDocument) {
157154
try {
158155
initXfaForm(xfa);
159156
} catch (Exception e) {
160-
throw new PdfException(e);
157+
throw new PdfException(e.getMessage(), e);
161158
}
162159
}
163160
}
@@ -497,17 +494,12 @@ public void fillXfaForm(InputSource is) throws IOException {
497494
* @throws java.io.IOException if any I/O issue occurs on the {@link InputSource}
498495
*/
499496
public void fillXfaForm(InputSource is, boolean readOnly) throws IOException {
500-
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
501-
DocumentBuilder db;
502497
try {
503-
db = dbf.newDocumentBuilder();
504-
db.setEntityResolver(new SafeEmptyEntityResolver());
498+
DocumentBuilder db = XmlProcessorCreator.createSafeDocumentBuilder(false, false);
505499
Document newdoc = db.parse(is);
506500
fillXfaForm(newdoc.getDocumentElement(), readOnly);
507-
} catch (ParserConfigurationException e) {
508-
throw new PdfException(e);
509501
} catch (SAXException e) {
510-
throw new PdfException(e);
502+
throw new PdfException(e.getMessage(), e);
511503
}
512504
}
513505

@@ -631,11 +623,8 @@ private void initXfaForm(PdfObject xfa) throws IOException, ParserConfigurationE
631623
initXfaForm(new ByteArrayInputStream(bout.toByteArray()));
632624
}
633625

634-
private void initXfaForm(InputStream inputStream) throws ParserConfigurationException, IOException, SAXException {
635-
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
636-
fact.setNamespaceAware(true);
637-
DocumentBuilder db = fact.newDocumentBuilder();
638-
db.setEntityResolver(new SafeEmptyEntityResolver());
626+
private void initXfaForm(InputStream inputStream) throws IOException, SAXException {
627+
DocumentBuilder db = XmlProcessorCreator.createSafeDocumentBuilder(true, false);
639628
setDomDocument(db.parse(inputStream));
640629
xfaPresent = true;
641630
}
@@ -696,12 +685,4 @@ private Node findDataNode(Node datasetsNode) {
696685
}
697686
return null;
698687
}
699-
700-
// Prevents XXE attacks
701-
private static class SafeEmptyEntityResolver implements EntityResolver {
702-
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
703-
return new InputSource(new StringReader(""));
704-
}
705-
}
706-
707688
}

forms/src/main/java/com/itextpdf/forms/xfdf/XfdfFileUtils.java

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,20 @@ This file is part of the iText (R) project.
4242
*/
4343
package com.itextpdf.forms.xfdf;
4444

45-
import org.w3c.dom.Document;
46-
import org.xml.sax.EntityResolver;
47-
import org.xml.sax.InputSource;
48-
import org.xml.sax.SAXException;
45+
import com.itextpdf.kernel.PdfException;
46+
import com.itextpdf.kernel.utils.XmlProcessorCreator;
4947

48+
import java.io.InputStream;
49+
import java.io.OutputStream;
5050
import javax.xml.XMLConstants;
5151
import javax.xml.parsers.DocumentBuilder;
52-
import javax.xml.parsers.DocumentBuilderFactory;
5352
import javax.xml.parsers.ParserConfigurationException;
5453
import javax.xml.transform.Transformer;
5554
import javax.xml.transform.TransformerException;
5655
import javax.xml.transform.TransformerFactory;
5756
import javax.xml.transform.dom.DOMSource;
5857
import javax.xml.transform.stream.StreamResult;
59-
import java.io.IOException;
60-
import java.io.InputStream;
61-
import java.io.OutputStream;
62-
import java.io.StringReader;
58+
import org.w3c.dom.Document;
6359

6460
final class XfdfFileUtils {
6561

@@ -68,32 +64,40 @@ private XfdfFileUtils() {
6864

6965
/**
7066
* Creates a new xml-styled document for writing xfdf info.
67+
*
7168
* @throws ParserConfigurationException in case of failure to create a new document.
7269
*/
73-
static Document createNewXfdfDocument() throws ParserConfigurationException {
74-
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
75-
DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();
76-
documentBuilder.setEntityResolver(new XfdfFileUtils.SafeEmptyEntityResolver());
77-
return documentBuilder.newDocument();
70+
static Document createNewXfdfDocument() {
71+
try {
72+
DocumentBuilder db = XmlProcessorCreator.createSafeDocumentBuilder(false, false);
73+
return db.newDocument();
74+
} catch (Exception e) {
75+
throw new PdfException(e.getMessage(), e);
76+
}
7877
}
7978

8079
/**
8180
* Creates a new xfdf document based on given input stream.
81+
*
8282
* @param inputStream containing xfdf info.
8383
*/
84-
static Document createXfdfDocumentFromStream(InputStream inputStream) throws ParserConfigurationException, IOException, SAXException {
85-
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
86-
DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();
87-
documentBuilder.setEntityResolver(new XfdfFileUtils.SafeEmptyEntityResolver());
88-
return documentBuilder.parse(inputStream);
84+
static Document createXfdfDocumentFromStream(InputStream inputStream) {
85+
try {
86+
DocumentBuilder db = XmlProcessorCreator.createSafeDocumentBuilder(false, false);
87+
return db.parse(inputStream);
88+
} catch (Exception e) {
89+
throw new PdfException(e.getMessage(), e);
90+
}
8991
}
9092

9193
/**
92-
* Saves the info from output stream to xml-styled document.
93-
* @param document to save info to.
94-
* @param outputStream the stream containing xfdf info.
94+
* Saves the info from XML-styled {@link Document} to {@link OutputStream}.
95+
*
96+
* @param document input {@link Document} that contains XFDF info
97+
* @param outputStream the output stream
9598
*/
96-
static void saveXfdfDocumentToFile(Document document, OutputStream outputStream) throws TransformerException {
99+
static void saveXfdfDocumentToFile(Document document, OutputStream outputStream)
100+
throws TransformerException {
97101
TransformerFactory transformerFactory = TransformerFactory.newInstance();
98102
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
99103
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
@@ -103,11 +107,4 @@ static void saveXfdfDocumentToFile(Document document, OutputStream outputStream)
103107
StreamResult streamResult = new StreamResult(outputStream);
104108
transformer.transform(domSource, streamResult);
105109
}
106-
107-
// Prevents XXE attacks
108-
private static class SafeEmptyEntityResolver implements EntityResolver {
109-
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
110-
return new InputSource(new StringReader(""));
111-
}
112-
}
113110
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2021 iText Group NV
4+
Authors: iText Software.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License version 3
8+
as published by the Free Software Foundation with the addition of the
9+
following permission added to Section 15 as permitted in Section 7(a):
10+
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
11+
ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
12+
OF THIRD PARTY RIGHTS
13+
14+
This program is distributed in the hope that it will be useful, but
15+
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
or FITNESS FOR A PARTICULAR PURPOSE.
17+
See the GNU Affero General Public License for more details.
18+
You should have received a copy of the GNU Affero General Public License
19+
along with this program; if not, see http://www.gnu.org/licenses or write to
20+
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21+
Boston, MA, 02110-1301 USA, or download the license from the following URL:
22+
http://itextpdf.com/terms-of-use/
23+
24+
The interactive user interfaces in modified source and object code versions
25+
of this program must display Appropriate Legal Notices, as required under
26+
Section 5 of the GNU Affero General Public License.
27+
28+
In accordance with Section 7(b) of the GNU Affero General Public License,
29+
a covered work must retain the producer line in every PDF that is created
30+
or manipulated using iText.
31+
32+
You can be released from the requirements of the license by purchasing
33+
a commercial license. Buying such a license is mandatory as soon as you
34+
develop commercial activities involving the iText software without
35+
disclosing the source code of your own applications.
36+
These activities include: offering paid services to customers as an ASP,
37+
serving PDFs on the fly in a web application, shipping iText with a closed
38+
source product.
39+
40+
For more information, please contact iText Software Corp. at this
41+
42+
*/
43+
package com.itextpdf.forms.xfa;
44+
45+
import com.itextpdf.kernel.PdfException;
46+
import com.itextpdf.kernel.utils.DefaultSafeXmlParserFactory;
47+
import com.itextpdf.test.ExceptionTestUtil;
48+
49+
import javax.xml.parsers.DocumentBuilder;
50+
import javax.xml.parsers.DocumentBuilderFactory;
51+
import javax.xml.parsers.ParserConfigurationException;
52+
import org.xml.sax.EntityResolver;
53+
import org.xml.sax.InputSource;
54+
55+
public class SecurityTestXmlParserFactory extends DefaultSafeXmlParserFactory {
56+
57+
@Override
58+
public DocumentBuilder createDocumentBuilderInstance(boolean namespaceAware, boolean ignoringComments) {
59+
DocumentBuilder db;
60+
try {
61+
db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
62+
} catch (ParserConfigurationException e) {
63+
throw new PdfException(e.getMessage(), e);
64+
}
65+
66+
db.setEntityResolver(new TestEntityResolver());
67+
return db;
68+
}
69+
70+
private static class TestEntityResolver implements EntityResolver {
71+
public InputSource resolveEntity(String publicId, String systemId) {
72+
throw new PdfException(ExceptionTestUtil.getXxeTestMessage());
73+
}
74+
}
75+
}

forms/src/test/java/com/itextpdf/forms/xfa/XXEVulnerabilityTest.java

Lines changed: 0 additions & 104 deletions
This file was deleted.

0 commit comments

Comments
 (0)