首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 防 SQL 注入三重防線:PreparedStatement、全局 Filter、字段級轉義

     編輯:Java 相關知識

SQL 注入是 Web 安全頭號殺手,核心成因是「數據與代碼未分離」。本文構建三層防禦體系:參數化查詢(根治) + 全局請求過濾(攔截) + 字段級轉義(補丁),並給出生產級代碼示例。

第一防線:PreparedStatement —— 根本解決

原理:SQL 模板預編譯,參數僅作為數據傳入,永不改變查詢邏輯結構。

// ✅ 安全:參數化查詢
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setString(1, username);
    ps.setString(2, password);
    ResultSet rs = ps.executeQuery();
}

// ❌ 危險:字符串拼接
String sql = "SELECT * FROM users WHERE username = '" + username + "'";

強制規範:項目中禁用 Statement 拼接 SQL,所有動態查詢統一用 PreparedStatement 或 ORM 框架(MyBatis/JPA/Hibernate)的參數綁定。

第二防線:全局 Servlet Filter —— 統一攔截惡意請求

@WebFilter("/*")
public class SqlInjectionFilter implements Filter {
    // 常見 SQL 關鍵字與特殊符號(根據業務擴展)
    private static final String INJECTION_PATTERN = 
        "(?i)(\\b(union|select|insert|update|delete|drop|alter|create|exec|declare|truncate)\\b|[';\\-+*/%])";

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        Map params = request.getParameterMap();
        for (String[] values : params.values()) {
            for (String value : values) {
                if (value != null && value.matches(".*" + INJECTION_PATTERN + ".*")) {
                    log.warn("SQL注入嘗試攔截: IP={}, 參數值={}", request.getRemoteAddr(), value);
                    // 可選:直接阻斷、重定向錯誤頁、記錄審計日志
                    ((HttpServletResponse) resp).sendError(HttpServletResponse.SC_BAD_REQUEST, "非法請求參數");
                    return;
                }
            }
        }
        chain.doFilter(req, resp);
    }
}

優化建議:

  • 正則預編譯為 Pattern,避免重複編譯開銷。
  • 白名單機制:對已知安全參數(如富文本內容)跳過檢查。
  • 結合 WAF(Web Application Firewall)在網關層攔截,應用層作為二線防禦。

第三防線:字段級轉義工具 —— 兼容歷史代碼/特殊場景


public class SqlUtils {
    // 單引號轉義 + 註釋符過濾,僅作補丁,不可替代 PreparedStatement
    public static String escapeForLegacy(String input) {
        if (input == null) return null;
        return input.replace("'", "''")           // 標準 SQL 轉義
                    .replaceAll("(?i)(--|/\\*|\\*/)", ""); // 移除註釋符
    }
}

適用場景:

  • 老系統無法全面重構為 PreparedStatement 時的緊急修補。
  • 動態構建 IN 子句、ORDER BY 等無法參數化的片段(需極度謹慎)。

深度防禦最佳實踐清單

  1. 全項目強制 PreparedStatement,Code Review / Sonar 規則攔截拼接 SQL。
  2. 最小權限原則:應用數據庫賬號僅擁有增刪改查權限,無 DDL/DCL 權限。
  3. 錯誤信息不回顯:生產環境隱藏 SQL 異常堆棧,避免泄露表結構。
  4. 輸入驗證前置:Controller 層用 Bean Validation (@Pattern, @Size) 校驗格式、長度。
  5. ORM 框架原生防注入:MyBatis #{param}、JPA ?1、Hibernate 命名參數,均等價於 PreparedStatement。
  6. 定期掃描:集成 SQLMap、OWASP ZAP 進行自動化注入掃描。

防 SQL 注入的核心只有一條:數據永遠不參與 SQL 解析。PreparedStatement 是銀彈,Filter 是盾牌,轉義只是創可貼。架構層面統一參數化,才是治本之道。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved