3Secure by Design

输入验证

防御注入

输入验证白名单SQL注入XSS防护

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第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 注入?

设计一个全面的输入验证方案来保护用户注册表单。


3/7输入验证
0%