IBM Appscan检测到BufferedReader处理XML的DoS漏洞,求助解决办法
我之前也碰到过一模一样的情况——用BufferedReader处理大型XML时被Appscan标记为DoS高危,试过BoundedBufferedReader也没搞定。下面是几个经过验证的解决方案,你可以逐一尝试:
1. 利用XML解析器本身的安全限制(最推荐)
直接用BufferedReader读取XML内容,绕开了专业XML解析器的安全防护,这很可能是Appscan触发告警的核心原因。你应该改用SAX/StAX等流式解析器,并配置严格的安全参数,从根源上限制恶意输入:
// 初始化SAX解析器工厂,禁用危险特性 SAXParserFactory factory = SAXParserFactory.newInstance(); // 禁止DOCTYPE声明,防止实体注入类DoS攻击 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // 禁用外部实体,避免恶意实体扩展消耗资源 factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); // 设置实体大小上限(以Xerces为例,根据业务需求调整数值) factory.setAttribute("http://apache.org/xml/properties/security-manager/max-general-entity-size", 1024 * 1024); // 1MB factory.setAttribute("http://apache.org/xml/properties/security-manager/max-parameter-entity-size", 1024 * 1024); // 创建解析器并处理输入流 SAXParser parser = factory.newSAXParser(); parser.parse(new FileInputStream("your-large.xml"), yourCustomHandler);
这种方式直接在解析层面做安全限制,Appscan能识别到这些标准安全配置,更容易通过扫描。
2. 实现全局字节限制的输入流包装器
BoundedBufferedReader通常只限制单次读取的字符数,但Appscan可能关注的是整个输入流的总读取量是否无限制。你可以自定义一个BoundedInputStream,直接从流的源头限制总字节数:
public class BoundedInputStream extends InputStream { private final InputStream delegate; private long remainingBytes; private final long maxAllowedBytes; public BoundedInputStream(InputStream delegate, long maxAllowedBytes) { this.delegate = delegate; this.maxAllowedBytes = maxAllowedBytes; this.remainingBytes = maxAllowedBytes; } @Override public int read() throws IOException { if (remainingBytes <= 0) { throw new IOException("Input exceeds maximum allowed size: " + maxAllowedBytes + " bytes"); } int readByte = delegate.read(); if (readByte != -1) { remainingBytes--; } return readByte; } @Override public int read(byte[] b, int off, int len) throws IOException { if (remainingBytes <= 0) { throw new IOException("Input exceeds maximum allowed size: " + maxAllowedBytes + " bytes"); } int bytesToRead = (int) Math.min(len, remainingBytes); int actualRead = delegate.read(b, off, bytesToRead); if (actualRead != -1) { remainingBytes -= actualRead; } return actualRead; } // 重写skip、available等方法,确保限制逻辑一致 @Override public long skip(long n) throws IOException { long skipped = delegate.skip(Math.min(n, remainingBytes)); remainingBytes -= skipped; return skipped; } @Override public int available() throws IOException { return (int) Math.min(delegate.available(), remainingBytes); } }
使用时直接包装原始输入流:
InputStream safeInput = new BoundedInputStream(new FileInputStream("your-large.xml"), 10 * 1024 * 1024); // 限制10MB BufferedReader reader = new BufferedReader(new InputStreamReader(safeInput)); // 后续解析逻辑不变
这种方式从输入源头做全局大小限制,Appscan能检测到输入被做了边界控制。
3. 检查Appscan漏洞详情,针对性调整
有时候Appscan的告警会附带具体的检测点(比如“检测到未限制的实体扩展”或“无限循环风险”),你可以根据这些信息精准修复:
- 如果是实体扩展问题:必须严格禁用外部实体(参考方案1的配置);
- 如果是大节点处理:在自定义SAX Handler中,设置单个节点的字符数上限,超过就抛出异常;
- 如果是误报:可以提交误报申请,但前提是你能提供输入已被严格限制的证据。
4. 使用OWASP ESAPI的安全XML解析工具
OWASP ESAPI库内置了专门的XML安全解析逻辑,能自动处理各种DoS风险,你可以直接集成:
// 用ESAPI验证并解析XML,设置最大允许大小 InputStream inputStream = new FileInputStream("your-large.xml"); String safeXml = ESAPI.validator().getValidXML("xmlInput", inputStream, 10 * 1024 * 1024, false); // 后续处理safeXml即可
为什么BoundedBufferedReader没用?
大概率是因为它的限制逻辑没有覆盖Appscan的检测规则:比如它只限制了单次read()的字符数,但解析器可能会一次性读取大量内容到内部缓冲区,或者Appscan无法识别它的限制逻辑,仍然判定输入无边界。
建议先尝试方案1,因为这是XML解析安全的标准做法,最容易通过扫描。每个方案实施后记得重新用Appscan扫描验证,确保漏洞被清除。
内容的提问来源于stack exchange,提问作者Akki

