文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

SQL 注入(SQLi)深度解析【原理、检测、风险、防护措施】

一、什么是 SQL 注入(概念)

SQL 注入是指攻击者把恶意 SQL 代码注入到应用的输入里,应用在拼接或执行这些输入构造的 SQL 时,导致数据库执行非预期命令(数据泄露、篡改、权限提升、远程命令执行等)。根源是不安全的用户输入被直接拼接到 SQL 语句,未使用参数化语句或未正确转义。


二、注入类型与原理

  1. 基于错误(Error‑based)
    利用数据库错误消息泄露结构信息或敏感数据。例如通过 UNION 或构造语法错误使数据库返回可被解析的内容。

  2. 基于联合(Union‑based)
    使用 UNION SELECT 合并攻击者控制的查询结果到原查询的结果集中,从而读取表数据。

  3. 布尔盲注(Boolean‑based Blind)
    利用不同布尔条件改变页面返回(或返回状态)来逐位推断数据,例如 AND (SUBSTR((SELECT password FROM users WHERE id=1),1,1)='a')

  4. 时间盲注(Time‑based Blind)
    利用延迟函数(如 SLEEP()pg_sleep())判断条件是否成立(用于不能从错误/返回区别的场景)。

  5. 基于堆叠查询(Stacked Queries)(部分 DB 如 MSSQL/MySQL 的某些配置)
    在单个请求中执行多条独立 SQL(例如 '; DROP TABLE users; --)。

  6. Out‑of‑Band (OOB)
    利用数据库或环境与外部服务器通信(DNS/HTTP)来回传数据(当直接通道受限时非常有用)。

  7. 二次注入(Second‑order)
    恶意数据先存入数据库,随后在不同的上下文或查询中被回显并触发注入(常被静态检测漏掉)。


三、关键危险点(为什么会产生)

  • 直接把用户输入拼接到 SQL 字符串中。
  • 在不可信上下文中动态构建表名/列名/排序字段而不做校验。
  • 数据库错误/调试信息在生产中泄露(信息泄露助攻)。
  • 过高的数据库账户权限(例如具有 DROP/ADMIN 权限)。
  • 缺乏最小权限、审计和监控。

四、易受攻击的源码示例(含修复)

说明:示例同时给出“易受攻击代码”与“推荐修复”,方便审计和直接替换。

1) Java(JDBC) — 易受攻击

// vulnerable
String q = "SELECT id, name FROM users WHERE email = '" + email + "' AND status = '" + status + "'";
Statement s = conn.createStatement();
ResultSet rs = s.executeQuery(q);

问题:emailstatus 直接拼接。攻击者可注入 ' OR '1'='1 等。

修复(PreparedStatement)

String q = "SELECT id, name FROM users WHERE email = ? AND status = ?";
PreparedStatement ps = conn.prepareStatement(q);
ps.setString(1, email);
ps.setString(2, status);
ResultSet rs = ps.executeQuery();

要点:参数化查询(PreparedStatement)把输入作为值传递,避免语法注入。

2) PHP(mysqli) — 易受攻击

// vulnerable
$q = "SELECT * FROM products WHERE name LIKE '%" . $_GET['q'] . "%'";
$res = $mysqli->query($q);

修复(绑定参数):

$stmt = $mysqli->prepare("SELECT * FROM products WHERE name LIKE CONCAT('%', ?, '%')");
$stmt->bind_param("s", $_GET['q']);
$stmt->execute();

3) Python(psycopg2 / PostgreSQL) — 易受攻击

# vulnerable
cur.execute("SELECT * FROM orders WHERE id = %s" % order_id)

修复:

cur.execute("SELECT * FROM orders WHERE id = %s", (order_id,))

要点:使用 DB API 的参数化接口,不要手动 format 字符串。不同驱动使用不同占位符(%s, ?, :1 等)——要用驱动的参数方式。

4) ORM(如 Hibernate / JPA / ActiveRecord)常见陷阱

易受攻击:

// JPA with string concatenation
String q = "FROM User u WHERE u.name = '" + name + "'";
entityManager.createQuery(q).getResultList();

修复(查询绑定):

Query q = entityManager.createQuery("FROM User u WHERE u.name = :name");
q.setParameter("name", name);

注意:即使使用 ORM,也可能因为使用 nativeQuery、动态拼表名、ORDER BY 字段拼接等导致注入。对列名、表名、排序字段必须做白名单校验,不能直接把用户输入当成结构的一部分。

5) Stored Procedures(存储过程)

存储过程本身不是万能防护:若在存储过程内部仍通过字符串拼接并 EXEC(),仍会注入。正确做法是使用参数化调用与内部参数化 SQL。


五、常见攻击 Payload(用于检测/测试,防御目的)

(用于安全测试,勿用于非法入侵)

  • 基本条件绕过: ' OR '1'='1
  • 终止并注入: '; DROP TABLE users; -- (注意很多 DB+驱动会禁用多语句)
  • UNION 读取列: '+ UNION SELECT username, password FROM users --
  • 布尔盲注示例: 1' AND SUBSTRING((SELECT password FROM users WHERE id=1),1,1)='a' --
  • 时间盲注(MySQL): ' OR SLEEP(5) --
    这些 payload 在现代防护环境下可能被 WAF 拦截或触发 IDS。用于渗透测试时要在授权环境进行。

六、检测与测试方法(审计/渗透/自动化)

在这里插入图片描述

  1. 静态代码审计(SAST)

    • 搜索易危险 API:createStatement, executeQuery(String), rawQuery, execSQL, mysqli->query, PDO->query, odbc_exec, execute 带字符串的情况。
    • 搜索字符串拼接、String.format、模板化 SQL、+ 拼接中的用户输入。
    • 检查 ORM 的 nativeQueryfromSqlRaw 等危险方法。
  2. 动态测试(DAST / 手工)

    • 在输入点注入布尔/语法错误/时间延迟 payload,观察响应差异/延迟/错误信息。
    • 使用 sqlmap(在授权测试)自动化识别盲注、UNION、时间盲注等。
    • 模拟高权限用户/不同角色测试二次注入。
  3. 日志/监控检测

    • 识别异常 SQL 错误、语法异常、频繁的延迟(time‑based),或来自同一 IP 的大量带有 SQL 特征的请求。
    • 配置 DB 审计(如 MySQL 连接日志、Oracle audit)来追踪可疑查询。
  4. 模糊测试 & 代码覆盖率

    • 针对拼接发生路径做模糊输入测试,尤其是动态 SQL、搜索、过滤、排序、分页等入口。

七、防护策略(按优先级与细节实现)

1. 最重要:参数化查询 / Prepared Statements / ORM 参数绑定

  • 所有 SQL 语句的值部分必须使用参数绑定,不要做字符串拼接。
  • 在支持的语言与驱动中使用预编译语句(JDBC PreparedStatement、PDO prepared statements、psycopg2 参数化等)。

2. 对结构性输入使用白名单验证

  • 用户不能控制 SQL 的结构(列名、表名、ORDER BY 字段、LIMIT 等)。若必须支持,须对允许的值做严格白名单(枚举)校验。
  • 示例:if (!allowedSorts.contains(userSort)) throw ...;

3. 最小权限数据库账户

  • 每个应用只使用权限最小的 DB 用户(只 GRANT 必要的 SELECT/INSERT/UPDATE/DELETE)。
  • 拒绝 DROP/ALTER 权限给普通应用账户,防止被注入后破坏。

4. 错误信息与异常处理

  • 生产环境关闭详细 SQL 错误输出,避免泄露表结构/列名。
  • 记录详细日志并发送到安全审计系统,但不要将详细错误暴露给用户。

5. 输入校验(白名单优先)与类型检查

  • 对数字、UUID、日期等强制进行类型验证(避免将用户输入当作字符串直接拼接)。
  • 长度限制、字符集限制可以作为额外防线,但不能替代参数化

6. WAF / 入侵检测(作为补充,而非主防线)

  • 部署 WAF 能拦截常见攻击模式(UNION、注入关键词、延迟请求),但不要把 WAF 当作唯一防护。

7. 安全配置

  • 禁用不必要的 DB 功能(如允许多语句的选项、允许外部程序执行等)如果不需要就关闭。
  • 更新 DB 与驱动,利用现代驱动的安全特性。

8. 审计与监控

  • DB 审计日志、慢查询日志、异常 SQL 报警。
  • 将异常模式(如包含 ' OR '1'='1')建立告警规则。

9. 定期安全测试

  • SAST 于 CI 中自动扫描,DAST 定期运行(并在变更后手动复测)。
  • 授权渗透测试(包含 sqlmap 扫描、手工盲注测试、二次注入场景)。

10. 防止二次注入

  • 当应用先存储数据再在另一处做 SQL 拼接时,必须对回显/二次使用的场景特别注意(例如,一个字段先保存,再在动态 SQL 中作为列名使用)。

八、风险评估(影响面与严重性)

  • 高风险:能够读取敏感表(用户、密码/哈希、支付信息)、能够写入/修改关键数据(更改权限、写入管理员账号)、能够执行 OS 命令或写入文件(如通过 xp_cmdshell 等在 MSSQL)——可能导致全面失陷。
  • 中风险:能枚举内部表结构或大量数据但无法直接变更关键业务数据。
  • 低风险:仅影响非敏感字段的查询或仅导致页面异常。
    严重性评估应结合:受影响的用户数、被窃取数据的敏感性、是否可导致持久后门或权限提升。

九、工程化 Checklist(代码审计/修复优先级)

在这里插入图片描述

  1. 优先替换所有拼接 SQL(尤其用户输入参与的)。
  2. 检查所有 exec/query/rawQuery/nativeQuery 的调用:是否包含用户输入?若包含,使用参数化或白名单。
  3. 对所有动态结构(表名、列、order by)做白名单验证。
  4. 限制 DB 账户权限,确保无法进行 DROP/ALTER/EXEC 等高危操作。
  5. 关闭生产详细错误输出,启用审计日志并集中告警。
  6. 将 SAST 规则加入 CI(关键关键字/模式自动告警)。
  7. 对曾暴露的表/列进行凭证重置(若怀疑泄露),并做应急响应(隔离、审计、回滚/修补)。
  8. 定期(至少季度)进行授权渗透测试与代码审计。

十、常见误区与陷阱

  • “只做输入过滤就够” —— 错误。输入过滤可以减少攻击面,但不能替代参数化。
  • “ORM 自动安全” —— ORM 不会自动避免所有注入(特别是使用原生 SQL、拼接查询、分页排序等场景)。
  • “WAF 就万无一失” —— WAF 是补充,不能替代安全编码与最小权限。
  • “只检查 Web 输入” —— 注入向量还包含批处理、内部系统任务、第三方集成数据、日志/上传文件元数据等。

十一、示例应急响应步骤(如果发现注入)

  1. 迅速下线或临时修复:对受影响端点临时做输出转义或限制访问(若可行)。
  2. 查明范围:哪些 API/页面受影响,是否有已被泄露的数据。
  3. 审计日志:导出数据库访问日志、错误日志,定位可疑查询与时间线。
  4. 更改凭证:若怀疑凭证被泄露(用户/管理员密码、API keys),按策略重置。
  5. 修复根本原因:用参数化查询替换拼接逻辑、修复二次注入点。
  6. 回溯调查:确认是否存在后门/持久化修改,清理并恢复。
  7. 通报并加固:内部复盘、合规通报(如需),持续监控。
    在这里插入图片描述

十二、快速参考(实用命令/关键字 - 审计时 grep)

常用待搜索关键字(代码中):

createStatement|executeQuery\(|query\(|execSQL\(|rawQuery|nativeQuery|format\(|String.format\(|\+ .*sql|\%s.*execute

前端/后端要着重找拼接字符串、使用 execRuntime.exec 相关的组合用法。

posted @ 2025-09-28 16:48  NeoLshu  阅读(34)  评论(0)    收藏  举报  来源