第3章:输入验证(Input Validation)
一、导读
1.1 本章学习目标
第3章讲解输入验证——防御注入攻击的第一道防线。
本章的核心学习目标包括:
- 理解输入验证的基本原则
- 掌握白名单验证 vs 黑名单验证
- 学会防御 SQL 注入、XSS、命令注入
- 理解验证的层次:语法验证、语义验证、业务验证
1.2 为什么本章如此重要
几乎所有安全漏洞都源于未经验证的用户输入。SQL 注入、XSS、命令注入、路径遍历——这些攻击的本质都是程序信任了不可信的输入。
1.3 核心问题引导
为什么白名单验证比黑名单验证更安全?
SQL 注入的根本原因是什么?
XSS 攻击有哪些类型?如何防御?
验证应该在哪个层次进行?
二、核心概念详解
2.1 白名单验证
java
// 白名单:只允许已知安全的输入
public class UsernameValidator {
private static final Pattern VALID = Pattern.compile("^[a-zA-Z0-9_]{3,20}$");
public static boolean isValid(String username) {
return username != null && VALID.matcher(username).matches();
}
}
// 黑名单:试图排除已知危险的输入(不推荐)
// 问题:攻击者总能找到绕过的方法2.2 SQL 注入防御
java
// 危险!字符串拼接
String query = "SELECT * FROM users WHERE name = '" + userInput + "'";
// 输入: ' OR '1'='1
// 结果: SELECT * FROM users WHERE name = '' OR '1'='1'
// 安全:参数化查询
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE name = ?"
);
stmt.setString(1, userInput); // 自动转义
ResultSet rs = stmt.executeQuery();2.3 XSS 防御
java
// 危险:直接输出用户输入
response.getWriter().write("<p>" + userInput + "</p>");
// 输入: <script>alert('xss')</script>
// 安全:HTML 转义
String safe = HtmlUtils.htmlEscape(userInput);
response.getWriter().write("<p>" + safe + "</p>");
// 或者使用模板引擎的自动转义
// Thymeleaf: <p th:text="${userInput}"></p>2.4 验证层次
层1: 语法验证(格式正确吗?)
→ 邮箱格式、手机号格式
层2: 语义验证(值合理吗?)
→ 年龄 > 0 且 < 150,日期在过去
层3: 业务验证(符合业务规则吗?)
→ 转账金额不超过余额,用户名未被占用三、实战案例
3.1 案例一:全面的输入验证框架
java
public class Validator<T> {
private final List<ValidationRule<T>> rules = new ArrayList<>();
public Validator<T> addRule(ValidationRule<T> rule) {
rules.add(rule);
return this;
}
public ValidationResult validate(T value) {
List<String> errors = new ArrayList<>();
for (ValidationRule<T> rule : rules) {
String error = rule.check(value);
if (error != null) errors.add(error);
}
return new ValidationResult(errors.isEmpty(), errors);
}
}
// 使用
Validator<String> emailValidator = new Validator<String>()
.addRule(v -> v == null ? "Email is required" : null)
.addRule(v -> v != null && !v.contains("@") ? "Invalid email format" : null)
.addRule(v -> v != null && v.length() > 254 ? "Email too long" : null);
ValidationResult result = emailValidator.validate(userInput);
if (!result.isValid()) {
return ResponseEntity.badRequest().body(result.getErrors());
}四、本章小结
白名单验证:只允许已知安全的输入。
参数化查询:防御 SQL 注入的最佳方式。
HTML 转义:防御 XSS 的基本手段。
多层验证:语法、语义、业务三个层次。
五、思考与练习
为什么白名单验证比黑名单更安全?
除了参数化查询,还有什么方法可以防御 SQL 注入?
设计一个全面的输入验证方案来保护用户注册表单。