漏洞产生的根本原因
核心问题:用户输入与数据库交互
php
// 安全代码(数据写死)
$sql = "SELECT * FROM table1 WHERE id < 4";
上述代码没有漏洞,因为查询条件是固定的。
php
// 存在漏洞的代码(用户可控)
$id = $_GET['id'];
$sql = "SELECT * FROM table1 WHERE id < $id";
当用户可以控制查询参数时,就可能产生SQL注入漏洞。
漏洞触发条件
用户输入场景:
- GET参数:
?id=4 - POST表单提交
- Cookie值
- HTTP头部信息
危险操作:
php
// 带引号的参数
$sql = "SELECT * FROM table1 WHERE username='$username'";
如果用户输入包含单引号:
?username=admin' OR '1'='1
实际执行的SQL变成:
sql
SELECT * FROM table1 WHERE username='admin' OR '1'='1'
破坏引号闭合:
?id=4'
?id=4\
这些输入会破坏SQL语句结构,导致:
- 数据库报错
- 泄露数据库结构信息
- 执行恶意SQL语句
生产环境与实验环境差异
PHPStudy实验环境:
- 默认关闭错误显示
- 便于学习和测试
- 不会暴露敏感信息
靶场环境:
- 故意开启错误显示
- 方便学习漏洞利用
- 模拟不安全的配置
生产环境:
- 应该关闭错误显示
- 记录日志但不向用户展示
- 使用通用错误页面
SQL注入实战案例
靶场地址:sqliabs.njhack.xyz
测试目标: 获取数据库中的所有用户信息
基础测试:
http://sqliabs.njhack.xyz/?id=2
http://sqliabs.njhack.xyz/index.php?id=2
攻击思路:
- 判断注入点存在性
- 确定字段数量
- 获取数据库信息
- 提取表名和字段名
- 导出敏感数据
代码层面的漏洞分析
后端处理逻辑:
php
<?php
$id = $_GET['id'];
$sql = "SELECT username, password FROM table1 WHERE id = $id";
$result = $conn->query($sql);
?>
正常请求:
?id=1
执行:SELECT username, password FROM table1 WHERE id = 1
恶意请求:
?id=1 OR 1=1
执行:SELECT username, password FROM table1 WHERE id = 1 OR 1=1
结果:返回所有记录
估算目标数据库语法
数据库语法差异: 不同数据库有不同的SQL方言:
- MySQL:使用反引号、LIMIT语法
- SQL Server:使用方括号、TOP语法
- Oracle:使用ROWNUM
- PostgreSQL:使用双引号
判断数据库类型: 通过错误信息、特定函数、语法差异来判断。