SQL注入
含义:是一种常见的网咯攻击手段,攻击者通过在输入字段或者请求中注入恶意的SQL语句,操控数据库执行目的以外的操作:
1.窃取敏感信息;
2.绕过身份验证;
3.修改/删除数据库内容;
4.执行系统命令等
工作原理:
1.输入验证不足:在web程序中没有对用户的输入进行正确的验证,用户可以在该输入字段中插入SQL代码;
2.SQL语句拼接:程序后端将用户的输入与SQL查询拼接在一起,形成完整的数据库查询语句;
3.执行恶意SQL:程序没有对输入进行过滤,清理或者转义,恶意SQL语句将被数据库服务器执行;
4.数据库泄露或者破坏:攻击者可以利用存在的SQL注入漏洞进行查询、修改或删除数据库中的数据,或是执行数据库管理的系统命令
常见SQL注入:
SQL语句:SELECT * FROM users WHERE username = 'user1' AND password = 'password1';
1.“--”:
注释符,将其后面的SQL语句注释掉
输入:
user:aaa'--
Passwd:任意
SELECT * FROM users WHERE username = 'aaa'--' AND password = 'password1';
2.字符型注入' or '1'='1,数字型注入or 1=1:
1=1恒为真,可以绕过验证
SELECT * FROM users WHERE username = 'aaa'or'1'='1' AND password = 'password1';
3.union联合查询:
'UNION SELECT null,username,password FROMusers --
SELECT * FROM users WHERE username = ''UNION SELECT null,username,password FROMusers --' AND password = 'password1';
将会以users表的username和password作为数据结果返回
4.盲注:
无法直接获取查询结果,攻击者通过判断返回页面的响应(如布尔值或时间延迟)来逐步推测数据。
布尔型盲注:
SELECT * FROM users WHERE username = '' AND (SELECT 1 WHERE SUBSTRING((SELECT database()), 1, 1)='t') --';
Select database():尝试获取当前数据库名称
Substring( ,1,1)=’t’:判断数据库名称第一个字符是否为’t’
根据返回判断数据库名称首字母是否为t
时间盲注:
SELECT * FROM users WHERE username = '' AND IF(1=1, SLEEP(5), 0) --';
Sleep()用于延迟语句的执行时间
1=1恒成立,执行sleep(5)延迟语句5秒执行
不成立则立即执行(0)
5.堆叠查询注入:
允许多条SQL语句同时执行
SELECT * FROM users WHERE username = ''; DROP TABLE users; --';
原查询(SELECT * FROM users WHERE username = '')执行后,会继续执行恶意语句,最终导致users表被删除。
6.存储过程注入:
利用存储过程的输入参数注入恶意 SQL
EXEC LoginProcedure 'username', ''; EXEC xp_cmdshell('dir'); --'
利用存储过程(Login Procedure)的输入参数,拼接恶意SQL语句,通过;分隔,执行EXEC xp_cmdshell('dir'),xp_cmdshell()可调用系统命令,dir列出目录
7.Cookie注入:
修改浏览器的cookie值进行注入
Cookie: session_id=' OR '1'='1;
若拼接到SQL语句中,恶意cookie被解析后,SQL条件恒成立,可能导致未被授权访问、数据泄露等风险
危害
1.数据泄露: 攻击者获取数据库中的用户名、密码、银行卡号等敏感信息。
2.权限提升: 攻击者可能通过注入命令获得更高的访问权限。
3.数据篡改: 数据库内容被修改或删除。
4.服务中断: 恶意 SQL 代码可能导致数据库崩溃,影响系统可用性。
5.执行系统命令: 通过数据库扩展功能,攻击者可能直接操作操作系统
防范措施
SQL语句:SELECT * FROM users WHERE username = 'user1' AND password = 'password1';
1.参数化查询和预编译
核心原理
问题根源:普通的字符串拼接 SQL 语句会将用户输入直接解析为 SQL 代码,极易引发 SQL 注入攻击。
解决方案:参数化查询将 SQL 语句的结构与用户输入的数据完全分离。数据库会先预编译 SQL 模板,再安全地代入参数,从而彻底避免注入风险。
代码示例解析
java代码:
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
? 是占位符,代表后续要传入的参数。
PreparedStatement 会预先编译 SQL 模板,确保参数仅作为数据处理,不会被解析为 SQL 命令。
这种方式不仅更安全,还能通过重复使用预编译的语句提升执行效率。
Node.js (MySQL 模块) 实现:
const query = "SELECT * FROM users WHERE username = ? AND password = ?";
connection.query(query, [username, password], (err, results) => {
if (err) throw err;
// Handle results
});
Node.js 的 MySQL 模块同样支持占位符语法。
参数通过数组形式传入,驱动会自动完成转义和安全注入,避免了手动拼接字符串的风险。
2.使用ORM框架
核心原理
ORM 框架会将编程语言中的对象与数据库表进行映射,自动生成和执行 SQL 语句。
它完全替代了手动拼接 SQL 的过程,从根源上减少了 SQL 注入的风险。
开发者只需操作对象,无需编写复杂的 SQL,大幅提升了开发效率。
javascript
// 使用 Sequelize
const user = await User.findOne({
where: { username: 'admin', password: 'password123' }
});
框架:代码中使用的是 Node.js 生态里的 Sequelize ORM。
逻辑:User.findOne() 方法会根据传入的条件对象,自动生成安全的SELECT查询语句。
安全性:框架内部会自动对where中的参数进行参数化处理,确保用户输入(即使是恶意输入)只会被当作数据,不会被解析为 SQL 命令。
3.输入验证
核心原理
提前拦截:在用户输入到达数据库层之前,就对其格式、长度、内容进行检查,拒绝不符合规则的输入。
白名单原则:只允许已知的、安全的字符或格式通过,而非试图去过滤所有可能的恶意字符(黑名单)。
转义特殊字符:对于必须保留的特殊字符,进行转义处理,使其失去在 SQL 中的特殊含义。
代码示例解析
javascript
const username = req.body.username.replace(/[^a-zA-Z0-9]/g, ''); // 清理特殊字符
正则表达式:/[^a-zA-Z0-9]/g 是一个 “否定” 字符类,匹配所有不是大小写字母或数字的字符。
作用:.replace() 方法会将所有匹配到的特殊字符替换为空字符串,从而彻底清除用户名中的潜在危险字符。
适用场景:适合对用户名、ID 等格式要求简单的字段进行清洗。
4.限制数据库权限
核心原理
最小权限原则:确保每个数据库用户只能执行其工作所需的最基本操作,绝不多赋予任何额外权限。
权限隔离:将不同操作(如读、写、管理)分配给不同的用户账号,避免一个账号泄露导致全库失控。
纵深防御:即使应用层被突破,攻击者也只能在被限制的权限内活动,无法执行DROP、ALTER等高风险操作。
代码示例解析
sql
-- 创建一个新的数据库用户
CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'secure_password';
-- 授予该用户对指定数据库所有表的只读权限
GRANT SELECT ON database_name.* TO 'readonly_user'@'%';
CREATE USER:创建一个名为readonly_user的新用户,@'%'表示该用户可以从任何 IP 地址连接。
GRANT SELECT:仅授予该用户执行SELECT查询的权限,确保它只能读取数据,无法修改或删除数据。
database_name.*:将权限作用于database_name数据库下的所有表。