数据库注入如何防御

wen 开源项目 26

本文目录导读:

数据库注入如何防御

  1. 核心防御:使用参数化查询(Prepared Statements)
  2. 重要辅助手段:输入验证与清理
  3. 关键实践:最小权限原则
  4. 限制数据库暴露面
  5. 高级与补充防御
  6. 防御优先级总结

数据库注入(SQL注入)是目前最常见也最危险的Web安全漏洞之一,防御的核心原则是:永远不要信任用户的任何输入

以下是经过行业验证的、从高到低优先级的防御措施:

核心防御:使用参数化查询(Prepared Statements)

这是防御SQL注入最有效、最根本的方法,它通过将SQL语句的结构与数据分离,确保用户输入永远不被解释为SQL代码。

  • 原理:数据库驱动程序会将SQL语句模板(包含占位符)和用户数据分别发送给数据库服务器,数据库先编译SQL模板,再将数据作为纯值绑定,从而杜绝了数据被当作代码执行的可能。

  • 代码示例(不同语言)

    • Java (JDBC)

      String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
      PreparedStatement pstmt = connection.prepareStatement(sql);
      pstmt.setString(1, userInputUsername);
      pstmt.setString(2, userInputPassword);
      ResultSet rs = pstmt.executeQuery();
    • Python (PyMySQL/MySQLdb)

      cursor = connection.cursor()
      sql = "SELECT * FROM users WHERE username = %s AND password = %s"
      cursor.execute(sql, (user_input_username, user_input_password))
    • Node.js (mysql2)

      const mysql = require('mysql2/promise');
      // 使用 ? 作为占位符
      const [rows] = await connection.execute(
        'SELECT * FROM users WHERE username = ? AND password = ?',
        [userInputUsername, userInputPassword]
      );
    • PHP (PDO)

      $stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username AND password = :password');
      $stmt->execute(['username' => $userInputUsername, 'password' => $userInputPassword]);
  • 注意:很多ORM(如Hibernate、MyBatis、Entity Framework、Django ORM)在底层实现了参数化查询,但必须确保你没有使用拼接字符串的方式(如 select * from table where name='${name}'),或者使用ORM提供的原生SQL接口时未错误地使用拼接

重要辅助手段:输入验证与清理

虽然参数化查询是主防御,但输入验证可以作为深度防御的一环,防止非SQL注入的其它攻击(如XSS、命令注入)。

  • 白名单验证:只允许已知的、安全的字符或格式。
    • 示例:如果字段预期是“数字ID”,使用 is_numeric()intval() 强制转换。
    • 示例:如果字段预期是“电子邮件”,使用正则表达式验证格式。
  • 黑名单过滤不推荐作为主要手段,攻击者总能绕过简单的黑名单(如过滤 等),黑名单容易漏掉编码变种(如 %270x27UNION/**/SELECT)。

关键实践:最小权限原则

  • 数据库账号权限
    • 应用程序连接数据库的账号,绝不能使用 rootDBA 账号。
    • 只给予该账户所需的最小权限
      • 常规查询:只给 SELECTINSERTUPDATE权限(对于用户表,通常不需要 DELETE)。
      • 禁止对不需要的表执行访问(如系统表 information_schema)。
    • 严格禁止使用动态执行存储过程的权限(如 EXECUTE 在某些数据库中被滥用)。

限制数据库暴露面

  • 禁用危险函数:在数据库中禁用或移除不需要的危险功能,如:
    • xp_cmdshell (SQL Server)
    • LOAD_FILEINTO OUTFILE (MySQL)
    • UTL_FILEDBMS_SCHEDULER (Oracle)
  • 错误信息处理
    • 绝对不要将数据库的原始错误信息(如 “You have an error in your SQL syntax...near '...' at line 1”)直接返回给用户。
    • 自定义错误页面:在生产环境中,向用户展示通用的“服务器内部错误”或“请求无效”页面,并将详细错误记录到服务器日志中。

高级与补充防御

  • Web应用防火墙:作为网络层面的防御,可以拦截明显的攻击载荷(如 OR 1=1UNION SELECT),但不能替代代码层面的防御。
  • 存储过程的安全使用
    • 如果使用存储过程,也必须使用参数化方式调用,而不能在存储过程内部进行字符串拼接。
  • 定期安全扫描:使用自动化工具(如SQLMap、商业DAST/SAST工具)或人工代码审计,检测代码中是否存在未被参数化的SQL查询。

防御优先级总结

优先级 防御措施 说明
第一(必须) 参数化查询/预编译语句 根除注入的根基,不可绕过。
第二(必须) 最小权限原则 限制攻击成功后的损害范围。
第三(重要) 输入白名单验证 提前拦截异常格式数据,减少攻击面。
第四(重要) 禁用危险函数/特性 关闭数据库的“后门”。
第五(推荐) 安全错误处理 避免泄露数据库结构信息。
第六(辅助) WAF、定期扫描 增加额外的安全层。

只要你使用参数化查询(Prepared Statements),并将数据库账户权限限制到最小,那么99%的SQL注入攻击都无法生效。 不要试图通过写复杂的正则或黑名单过滤器来替代参数化查询。

抱歉,评论功能暂时关闭!