当前位置: 首页 > article >正文

SQL 注入 (C++向)

SQL 注入(SQL Injection)原理

SQL 注入是一种常见的 Web 安全漏洞,攻击者通过在输入字段中注入恶意 SQL 代码,来操纵数据库查询,从而窃取数据、修改数据,甚至破坏数据库。

基本原理

  • 服务器接收用户输入,并将其作为 SQL 语句的一部分执行。
  • 如果输入未经过严格检查和过滤,攻击者可以构造特殊输入,使 SQL 语句的逻辑发生变化,从而执行额外的数据库操作。

例如,假设有以下 SQL 查询:

std::string sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";

如果 username 传入:

' OR '1'='1

那么最终 SQL 语句变成:

SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';

'1'='1' 始终为真,导致 查询返回所有用户,攻击者可以直接绕过登录验证。


SQL 注入攻击示例

示例 1:绕过登录认证

假设有一个 C++ 代码处理用户登录:

std::string sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
sql::ResultSet* res = mysqlUtil_.executeQuery(sql);
if (res->next()) {
    std::cout << "登录成功!" << std::endl;
} else {
    std::cout << "用户名或密码错误!" << std::endl;
}

攻击方式

  • 输入用户名admin' --
  • 输入密码随便填

最终生成的 SQL 语句:

SELECT * FROM users WHERE username = 'admin' --' AND password = '';
  • -- 使得 后面的 AND password = '' 被注释掉,攻击者成功绕过密码验证,直接以 admin 身份登录。

示例 2:获取数据库中的所有用户

假设攻击者想要获取 users 表中的所有数据,他们可以输入:

  • 用户名' OR 1=1 --
  • 密码随便填

导致 SQL 变成:

SELECT * FROM users WHERE username = '' OR 1=1 --' AND password = '';

由于 1=1 始终为真,这个查询会返回 所有用户的数据,攻击者就能获取整个 users 表的信息。


示例 3:修改数据库中的数据

如果数据库允许修改密码:

std::string sql = "UPDATE users SET password = '" + new_password + "' WHERE username = '" + username + "'";

攻击者输入:

  • 用户名admin' --
  • 新密码hacked

最终 SQL 语句:

UPDATE users SET password = 'hacked' WHERE username = 'admin' --';

攻击者成功修改了 admin 用户的密码。


示例 4:删除数据库表

如果服务器执行如下 SQL 语句:

std::string sql = "DELETE FROM users WHERE username = '" + username + "'";

攻击者输入:

  • 用户名' OR 1=1 --

导致 SQL 变成:

DELETE FROM users WHERE username = '' OR 1=1 --';

1=1 始终为真,结果是 删除 users 表中所有数据


示例 5:联合查询窃取数据库信息

如果前端显示用户信息:

std::string sql = "SELECT id, username FROM users WHERE username = '" + username + "'";

攻击者输入:

  • 用户名' UNION SELECT id, password FROM users --

最终 SQL 语句:

SELECT id, username FROM users WHERE username = '' UNION SELECT id, password FROM users --';

这样攻击者就能获取 users 表中的 idpassword 字段!


SQL 注入的防范方法(C++)

C++ 主要使用 MySQL Connector/C++SQLite 进行数据库操作,防止 SQL 注入的关键在于:

  1. 使用预处理语句(Prepared Statements)
  2. 限制数据库权限
  3. 使用输入过滤
  4. 隐藏错误信息
  5. 使用 Web 应用防火墙(WAF)

方法 1:使用预处理语句

预处理语句(Prepared Statement)可以自动处理用户输入,防止 SQL 注入

使用 MySQL Connector/C++ 预处理语句

sql::PreparedStatement* pstmt;
pstmt = conn->prepareStatement("SELECT id FROM users WHERE username = ? AND password = ?");
pstmt->setString(1, username);
pstmt->setString(2, password);
sql::ResultSet* res = pstmt->executeQuery();
if (res->next()) {
    std::cout << "登录成功!" << std::endl;
} else {
    std::cout << "用户名或密码错误!" << std::endl;
}

为什么安全?

  • ? 占位符不会被 SQL 解析为代码,不会拼接 SQL 语句,而是让数据库单独处理参数。
  • setString() 自动转义输入,无论攻击者输入什么,数据库都不会把它当成 SQL 代码执行,而是当成 普通字符串。

方法 2:限制数据库权限

即使攻击者获得了访问权限,也应该 最小化数据库的权限

GRANT SELECT ON mydb.users TO 'webuser'@'localhost';
REVOKE DELETE, INSERT, UPDATE ON mydb.users FROM 'webuser'@'localhost';
  • webuser 只能查询数据,不能修改、删除数据。

方法 3:输入过滤

对用户输入进行严格的 白名单过滤

bool isValidUsername(const std::string& username) {
    return username.find("'") == std::string::npos && username.find("--") == std::string::npos;
}

但这不能完全防止 SQL 注入,最好结合预处理语句!


方法 4:隐藏错误信息

在 SQL 语句执行失败时,不要返回数据库错误信息:

try {
    sql::ResultSet* res = stmt->executeQuery(query);
} catch (sql::SQLException &e) {
    std::cout << "系统错误,请稍后再试。" << std::endl;
}

否则攻击者可以通过错误信息 推测数据库结构


方法 5:使用 Web 应用防火墙(WAF)

ModSecurity 等 WAF 可以检测 SQL 注入攻击,并阻止可疑的 SQL 语句:

apt install libapache2-mod-security2

总结

SQL 注入是一种非常危险的漏洞,但可以通过 预处理语句、权限管理、输入过滤等方式防止
使用预处理语句(防止 SQL 拼接)
限制数据库权限(防止数据泄露)
隐藏错误信息(防止信息泄露)
使用 WAF 防火墙(阻止恶意 SQL 语句)

C++ 代码中,一定要使用预处理语句,这样才能从根本上防止 SQL 注入!


http://www.kler.cn/a/582074.html

相关文章:

  • 前端 - npm - - npm安装依赖时 -d -s -g的区别
  • ​【C++设计模式】第十七篇:中介者模式(Mediator)
  • Ubuntu下MySQL的安装与使用(一)
  • Tweak Power:全方位电脑系统优化的高效工具
  • 第85期 | GPTSecurity周报
  • 详细介绍 Jupyter nbconvert 工具及其用法:如何将 Notebook 转换为 Python 脚本
  • golang从入门到做牛马:第十二篇-Go语言数组:数据的“有序集合”
  • 数据类设计_图片类设计之3_半规则图类设计(前端架构基础)
  • 官宣 | Fluss 0.6 发布公告
  • 基于Python+Vue开发的电影订票管理系统源码+运行步骤
  • Android 蓝牙工具类封装:支持经典蓝牙与 BLE,兼容高版本权限
  • 【redis】数据类型之布隆过滤器
  • 存储过程和自定义函数在银行信贷业务中的应用(oracle)
  • Spark数据倾斜深度解析与实战解决方案
  • 力扣-数组-367 有效的完全平方数
  • 【机器学习的主要类型】
  • HTML5 + Bootstrap5 网站底部代码实现与解析
  • Django系列教程(3)——Django的MVT设计模式
  • 大语言模型(LLM)的微调与应用
  • Elasticsearch 2025/3/7