Java代码审计手书(二) - XML与注入漏洞详解
1. XML解析导致的XXE漏洞
1.1 漏洞特征与危害
XXE (XML External Entity)漏洞发生在XML解析程序接收不受信任的输入且支持外部实体解析时。
主要危害:
-
探测本地文件内容
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]> <foo>&xxe;</foo> -
拒绝服务攻击(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);
}
解决方案:
- 禁用外部实体:
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
- 禁用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));
解决方案:
- 使用安全处理模式:
DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
- 禁用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);
解决方案:
- 安全处理模式:
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
- 禁用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));
解决方案:
- 安全处理模式:
reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
- 禁用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);
解决方案:
- 安全处理模式:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
- 禁用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);
解决方案:
- 限制外部资源访问:
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "all");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "all");
- 安全处理模式:
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>
解决方案:
- 限制外部资源访问:
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "all");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "all");
- 安全处理模式:
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防护要点
- 禁用外部实体(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)
- 验证所有用户输入
- 使用框架提供的安全特性