java代码审计手书(二)
字数 2261 2025-08-27 12:33:43

Java代码审计手书(二) - XML与注入漏洞详解

1. XML解析导致的XXE漏洞

1.1 漏洞特征与危害

XXE (XML External Entity)漏洞发生在XML解析程序接收不受信任的输入且支持外部实体解析时。

主要危害

  1. 探测本地文件内容

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]>
    <foo>&xxe;</foo>
    
  2. 拒绝服务攻击(XEE)

    <?xml version="1.0"?>
    <!DOCTYPE lolz [
      <!ENTITY lol "lol">
      <!ELEMENT lolz (#PCDATA)>
      <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
      <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
      <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
      ...
    ]>
    <lolz>&lol9;</lolz>
    

1.2 不同XML解析器的防护方案

1.2.1 XMLStreamReader

有漏洞代码

public void parseXML(InputStream input) throws XMLStreamException {
    XMLInputFactory factory = XMLInputFactory.newFactory();
    XMLStreamReader reader = factory.createXMLStreamReader(input);
}

解决方案

  1. 禁用外部实体:
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
  1. 禁用DTD:
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);

1.2.2 XPathExpression

有漏洞代码

DocumentBuilder builder = df.newDocumentBuilder();
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xpath = xPathFactory.newXPath();
XPathExpression xPathExpr = xpath.compile("/somepath/text()");
xPathExpr.evaluate(new InputSource(inputStream));

解决方案

  1. 使用安全处理模式:
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
  1. 禁用DTD:
df.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

1.2.3 SAXParser

有漏洞代码

SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(inputStream, customHandler);

解决方案

  1. 安全处理模式:
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
  1. 禁用DTD:
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

1.2.4 XMLReader

有漏洞代码

XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setContentHandler(customHandler);
reader.parse(new InputSource(inputStream));

解决方案

  1. 安全处理模式:
reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
  1. 禁用DTD:
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

1.2.5 DocumentBuilder

有漏洞代码

DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(input);

解决方案

  1. 安全处理模式:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
  1. 禁用DTD:
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

1.2.6 TransformerFactory

有漏洞代码

Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(input, result);

解决方案

  1. 限制外部资源访问:
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "all");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "all");
  1. 安全处理模式:
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

1.3 XSLT解析导致的XXE漏洞

漏洞特征:XXE_XSLT_TRANSFORM_FACTORY

攻击示例

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:value-of select="document('/etc/passwd')">
  </xsl:value-of></xsl:template>
</xsl:stylesheet>

解决方案

  1. 限制外部资源访问:
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "all");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "all");
  1. 安全处理模式:
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

2. 注入漏洞

2.1 XPath注入

漏洞特征:XPATH_INJECTION

风险:与SQL注入类似,可能导致未授权数据访问或恶意数据插入。

引用

  • WASC-39: XPath Injection
  • OWASP: Top 10 2013-A1-Injection
  • CWE-643: Improper Neutralization of Data within XPath Expressions

2.2 框架端点识别

2.2.1 Struts 1

特征:STRUTS1_ENDPOINT

  • Action类自动实例化HTTP参数为对象
  • 所有参数需严格检查

2.2.2 Struts 2

特征:STRUTS2_ENDPOINT

  • POJO类自动映射HTTP参数到setter方法
  • 所有setter方法都应视为不受信任输入

2.2.3 Spring

特征:SPRING_ENDPOINT

  • @RequestMapping注解的方法可远程访问
  • 需严格分析暴露的方法安全性

2.3 Spring CSRF防护

2.3.1 禁用CSRF保护

特征:SPRING_CSRF_PROTECTION_DISABLED

不安全配置

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
    }
}

2.3.2 不受限的RequestMapping

特征:SPRING_CSRF_UNRESTRICTED_REQUEST_MAPPING

有漏洞代码

@Controller
public class UnsafeController {
    @RequestMapping("/path")
    public void writeData() {
        // 状态变更操作
    }
}

解决方案(Spring 4.3+)

@Controller
public class SafeController {
    @GetMapping("/path") // 无副作用操作
    public String readData() { ... }
    
    @PostMapping("/path") // 状态变更操作
    public void writeData() { ... }
}

2.4 SQL注入

2.4.1 通用SQL注入

特征:SQL_INJECTION

有漏洞代码

createQuery("select * from User where id = '"+inputId+"'");

解决方案

import org.owasp.esapi.Encoder;
createQuery("select * from User where id = '"+Encoder.encodeForSQL(inputId)+"'");

2.4.2 Turbine SQL注入

特征:SQL_INJECTION_TURBINE

有漏洞代码

List<Record> BasePeer.executeQuery("select * from Customer where id=" + inputId);

解决方案

Criteria c = new Criteria();
c.add(CustomerPeer.ID, inputId);
List<Customer> customers = CustomerPeer.doSelect(c);

2.4.3 Hibernate注入

特征:SQL_INJECTION_HIBERNATE

有漏洞代码

Session session = sessionFactory.openSession();
Query q = session.createQuery("select t from UserEntity t where id = " + input);
q.execute();

解决方案

Query q = session.createQuery("select t from UserEntity t where id = :userId");
q.setString("userId",input);

2.4.4 JDO注入

特征:SQL_INJECTION_JDO

有漏洞代码

PersistenceManager pm = getPM();
Query q = pm.newQuery("select * from Users where name = " + input);
q.execute();

解决方案

Query q = pm.newQuery("select * from Users where name = nameParam");
q.declareParameters("String nameParam");
q.execute(input);

2.4.5 JPA注入

特征:SQL_INJECTION_JPA

有漏洞代码

EntityManager pm = getEM();
TypedQuery<UserEntity> q = em.createQuery(
    String.format("select * from Users where name = %s", username), 
    UserEntity.class);
UserEntity res = q.getSingleResult();

解决方案

TypedQuery<UserEntity> q = em.createQuery(
    "select * from Users where name = usernameParam",
    UserEntity.class)
    .setParameter("usernameParam", username);

2.4.6 Spring JDBC注入

特征:SQL_INJECTION_SPRING_JDBC

有漏洞代码

JdbcTemplate jdbc = new JdbcTemplate();
int count = jdbc.queryForObject(
    "select count(*) from Users where name = '"+paramName+"'", 
    Integer.class);

解决方案

int count = jdbc.queryForObject(
    "select count(*) from Users where name = ?", 
    Integer.class, paramName);

2.4.7 JDBC注入

特征:SQL_INJECTION_JDBC

有漏洞代码

Connection conn = [...];
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
    "update COFFEES set SALES = "+nbSales+" where COF_NAME = '"+coffeeName+"'");

解决方案

Connection conn = [...];
conn.prepareStatement("update COFFEES set SALES = ? where COF_NAME = ?");
updateSales.setInt(1, nbSales);
updateSales.setString(2, coffeeName);

2.4.8 Scala Slick注入

特征:SCALA_SQL_INJECTION_SLICK

有漏洞代码

db.run { sql"select * from people where name = '#$value'".as[Person]}

解决方案

db.run { sql"select * from people where name = $value".as[Person]}

2.4.9 Scala Anorm注入

特征:SCALA_SQL_INJECTION_ANORM

有漏洞代码

val peopleParser = Macro.parser[Person]("id", "name", "age")
DB.withConnection { implicit c =>
    val people: List[Person] = SQL("select * from people where name = '" + value + "'").as(peopleParser.*)
}

解决方案

val people: List[Person] = SQL"select * from people where name = $value".as(peopleParser.*)

2.4.10 Android SQL注入

特征:SQL_INJECTION_ANDROID

有漏洞代码

String query = "SELECT * FROM messages WHERE uid= '"+userInput+"'" ;
Cursor cursor = this.getReadableDatabase().rawQuery(query,null);

解决方案

String query = "SELECT * FROM messages WHERE uid= ?" ;
Cursor cursor = this.getReadableDatabase().rawQuery(query,new String[] {userInput});

2.5 LDAP注入

特征:LDAP_INJECTION

有漏洞代码

NamingEnumeration<SearchResult> answers = context.search(
    "dc=People,dc=example,dc=com", 
    "(uid=" + username + ")", 
    ctrls);

防护建议

  • 对输入参数进行严格验证和过滤
  • 使用LDAP查询参数化工具(如Spring LDAP的Filter类)

3. 总结

3.1 XXE防护要点

  1. 禁用外部实体(IS_SUPPORTING_EXTERNAL_ENTITIES=false)
  2. 禁用DTD(SUPPORT_DTD=false或disallow-doctype-decl=true)
  3. 启用安全处理模式(FEATURE_SECURE_PROCESSING=true)
  4. 限制外部资源访问(ACCESS_EXTERNAL_DTD/STYLESHEET)

3.2 注入防护要点

  1. 使用参数化查询(prepared statements)
  2. 使用ORM框架的安全查询方法
  3. 对输入进行严格验证和过滤
  4. 使用框架提供的安全API(如Criteria查询)

3.3 框架安全要点

  1. 不要禁用CSRF保护
  2. 为状态变更操作使用正确的HTTP方法(POST/PUT/DELETE)
  3. 验证所有用户输入
  4. 使用框架提供的安全特性
Java代码审计手书(二) - XML与注入漏洞详解 1. XML解析导致的XXE漏洞 1.1 漏洞特征与危害 XXE (XML External Entity)漏洞发生在XML解析程序接收不受信任的输入且支持外部实体解析时。 主要危害 : 探测本地文件内容 拒绝服务攻击(XEE) 1.2 不同XML解析器的防护方案 1.2.1 XMLStreamReader 有漏洞代码 : 解决方案 : 禁用外部实体: 禁用DTD: 1.2.2 XPathExpression 有漏洞代码 : 解决方案 : 使用安全处理模式: 禁用DTD: 1.2.3 SAXParser 有漏洞代码 : 解决方案 : 安全处理模式: 禁用DTD: 1.2.4 XMLReader 有漏洞代码 : 解决方案 : 安全处理模式: 禁用DTD: 1.2.5 DocumentBuilder 有漏洞代码 : 解决方案 : 安全处理模式: 禁用DTD: 1.2.6 TransformerFactory 有漏洞代码 : 解决方案 : 限制外部资源访问: 安全处理模式: 1.3 XSLT解析导致的XXE漏洞 漏洞特征 :XXE_ XSLT_ TRANSFORM_ FACTORY 攻击示例 : 解决方案 : 限制外部资源访问: 安全处理模式: 2. 注入漏洞 2.1 XPath注入 漏洞特征 :XPATH_ INJECTION 风险 :与SQL注入类似,可能导致未授权数据访问或恶意数据插入。 引用 : WASC-39: XPath Injection OWASP: Top 10 2013-A1-Injection CWE-643: Improper Neutralization of Data within XPath Expressions 2.2 框架端点识别 2.2.1 Struts 1 特征 :STRUTS1_ ENDPOINT Action类自动实例化HTTP参数为对象 所有参数需严格检查 2.2.2 Struts 2 特征 :STRUTS2_ ENDPOINT POJO类自动映射HTTP参数到setter方法 所有setter方法都应视为不受信任输入 2.2.3 Spring 特征 :SPRING_ ENDPOINT @RequestMapping注解的方法可远程访问 需严格分析暴露的方法安全性 2.3 Spring CSRF防护 2.3.1 禁用CSRF保护 特征 :SPRING_ CSRF_ PROTECTION_ DISABLED 不安全配置 : 2.3.2 不受限的RequestMapping 特征 :SPRING_ CSRF_ UNRESTRICTED_ REQUEST_ MAPPING 有漏洞代码 : 解决方案(Spring 4.3+) : 2.4 SQL注入 2.4.1 通用SQL注入 特征 :SQL_ INJECTION 有漏洞代码 : 解决方案 : 2.4.2 Turbine SQL注入 特征 :SQL_ INJECTION_ TURBINE 有漏洞代码 : 解决方案 : 2.4.3 Hibernate注入 特征 :SQL_ INJECTION_ HIBERNATE 有漏洞代码 : 解决方案 : 2.4.4 JDO注入 特征 :SQL_ INJECTION_ JDO 有漏洞代码 : 解决方案 : 2.4.5 JPA注入 特征 :SQL_ INJECTION_ JPA 有漏洞代码 : 解决方案 : 2.4.6 Spring JDBC注入 特征 :SQL_ INJECTION_ SPRING_ JDBC 有漏洞代码 : 解决方案 : 2.4.7 JDBC注入 特征 :SQL_ INJECTION_ JDBC 有漏洞代码 : 解决方案 : 2.4.8 Scala Slick注入 特征 :SCALA_ SQL_ INJECTION_ SLICK 有漏洞代码 : 解决方案 : 2.4.9 Scala Anorm注入 特征 :SCALA_ SQL_ INJECTION_ ANORM 有漏洞代码 : 解决方案 : 2.4.10 Android SQL注入 特征 :SQL_ INJECTION_ ANDROID 有漏洞代码 : 解决方案 : 2.5 LDAP注入 特征 :LDAP_ INJECTION 有漏洞代码 : 防护建议 : 对输入参数进行严格验证和过滤 使用LDAP查询参数化工具(如Spring LDAP的Filter类) 3. 总结 3.1 XXE防护要点 禁用外部实体(IS_ SUPPORTING_ EXTERNAL_ ENTITIES=false) 禁用DTD(SUPPORT_ DTD=false或disallow-doctype-decl=true) 启用安全处理模式(FEATURE_ SECURE_ PROCESSING=true) 限制外部资源访问(ACCESS_ EXTERNAL_ DTD/STYLESHEET) 3.2 注入防护要点 使用参数化查询(prepared statements) 使用ORM框架的安全查询方法 对输入进行严格验证和过滤 使用框架提供的安全API(如Criteria查询) 3.3 框架安全要点 不要禁用CSRF保护 为状态变更操作使用正确的HTTP方法(POST/PUT/DELETE) 验证所有用户输入 使用框架提供的安全特性