SQL注入简介
SQL注入就是指Web应用程序对用户输入数据的合理性没有进行判断,前端传入后端的参数是攻击者可控制的,并且根据参数带入数据库查询,攻击者可以通过构造不同的SQL语句来对数据库进行任意查询。下面以PHP语句为例作为展示:
q u e r y = " S E L E C T ∗ F R O M u s e r s W H E R E i d = query=" SELECT*FROM users WHERE id=query="SELECT∗FROMusersWHEREid=_GET [‘id’] ";
由于这里的参数ID可控,且带入数据库查询,所以非法用户可以任意拼接SQL语句进行攻击。
当然,SQL注入主要原因是程序员在开发用户和数据库的系统时没有对用户输入的字符串进行过滤、转义、限制或处理不严谨,导致攻击者可以通过精心构造的字符串去非法获取到数据库中的数据。
二、SQL注入的危害与防范
1、SQL注入原理
知已知彼才能百战不殆,要想防护住SQL注入,首先我们需要了解其攻击原理。
SQL注入攻击的原理是利用web应用程序中对用户输入数据的合法性判断不足或过滤不严的情况。
攻击者常常会在web应用程序中,预先设定的查询语句的末尾巧妙地附加额外的SQL语句,借此实现对数据库的非法操控。这些非法操控可能包括窃取数据库中的敏感信息、篡改数据,甚至执行其他具有破坏性的恶意指令。具体来说,攻击者会伪装成正常用户,将精心构造的恶意SQL代码作为输入数据提交给应用程序。若应用程序对用户输入的校验和过滤工作不到位,这些恶意的SQL代码便会被当作正常的SQL语句执行,进而引发未授权的数据库操作。
以登录表单为例,攻击者可能会在其中输入如“(’ or 1=1)”这样特定的SQL代码片段。如果应用程序未对这种异常输入采取恰当的防范措施,原本用于验证用户身份的SQL查询语句可能会被篡改为“select * from users where user_name=‘’ or 1=1”,导致所有用户都能绕过验证,直接登录系统。
SQL注入攻击的手法多样,包括基于布尔值的盲注、基于时间差的盲注、基于错误报告的注入以及联合查询注入等。这些技术均依赖于对SQL语句结构的深刻理解和对数据库特定行为的精准利用。为了有效防范SQL注入攻击,我们必须对用户输入实施严格的验证和过滤,并尽可能采用参数化查询或预编译语句等安全实践,从源头上减少SQL注入的风险。
2、SQL注入危害
包括但不局限于:
-
数据库信息泄漏:攻击者通过SQL注入攻击,能够窥探并窃取数据库中存储的用户隐私信息,如个人身份信息、账户密码等,严重侵犯了用户的隐私权。
-
网页篡改:利用SQL注入技术,攻击者可以轻易操作数据库,进而对特定网页的内容进行篡改,误导用户或传播虚假信息,破坏网站的声誉和信任度。
-
网站被挂马,传播恶意软件:攻击者通过修改数据库中的字段值,可以嵌入恶意链接或代码,从而实施挂马攻击,使用户在访问网站时感染恶意软件,危害用户设备的安全。
-
数据库被恶意操作:一旦数据库服务器遭受攻击,攻击者可能获得数据库系统管理员的权限,进而对数据库进行恶意操作,如删除数据、篡改信息或执行其他破坏性行为。
-
服务器被远程控制,被安装后门:通过利用数据库服务器提供的操作系统支持,攻击者可以进一步修改或控制操作系统,从而在服务器上安装后门程序,实现对服务器的远程控制,为后续的攻击和窃取行为提供便利。
-
破坏硬盘数据,瘫痪全系统:更为严重的是,一些数据库系统允许SQL指令直接操作文件系统,这使得SQL注入的危害被进一步放大。攻击者可以利用这一漏洞,直接对服务器硬盘上的数据进行破坏,甚至导致整个系统瘫痪,造成巨大的经济损失和安全威胁。
三、SQL注入的攻击分类
注入点类型
1.数字型注入
主要涉及到应用程序对用户输入的数字型参数的处理不当。当应用程序在构造SQL查询语句时,未能对用户输入的数字型参数进行充分的验证和过滤,攻击者就可以通过输入恶意的SQL代码,修改原始的查询语句,从而达到非法操作数据库的目的。
具体来说,即类似结构 http://xxx.com/users.php?id=1 基于此种形式的注入,一般被叫做数字型注入点,缘由是其注入点 id 类型为数字,在大多数的网页中,诸如查看用户个人信息,查看文章等,大都会使用这种形式的结构传递id等信息,交给后端,查询出数据库中对应的信息,返回给前台。
当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入。
测试步骤:
(1) 加单引号,URL:xxx.xxx.xxx/xxx.php?id=3’;
对应的sql:select * from table where id=3’ 这时sql语句出错,程序无法正常从数据库中查询出数据,就会抛出异常;
(2) 加and 1=1 ,URL:xxx.xxx.xxx/xxx.php?id=3 and 1=1;
对应的sql:select * from table where id=3’ and 1=1 语句执行正常,与原始页面没有差异;
(3) 加and 1=2,URL:xxx.xxx.xxx/xxx.php?id=3 and 1=2;
对应的sql:select * from table where id=3 and 1=2 语句可以正常执行,但是无法查询出结果,所以返回数据与原始网页存在差异;
如果满足以上三点,则可以判断该URL存在数字型注入。
2.字符型注入
主要源于应用程序对用户输入的字符型数据没有进行严格的过滤和验证。当应用程序构造SQL查询语句时,如果它直接使用了用户输入的字符型数据,而没有进行必要的转义和过滤,那么攻击者就有可能在这些输入中插入恶意的SQL代码。
具体来说,即类似结构http://xxx.com/users.php?name=admin 这种形式,其注入点 name 类型为字符类型,所以叫字符型注入点。这一类的 SQL 语句原型大概为 “select * from 表名 where name=‘admin’” 值得注意的是这里相比于数字型注入类型的sql语句原型多了引号,可以是单引号或者是双引号。
当输入的参数为字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过单引号来闭合的。
例如数字型语句:select * from table where id =3;
则字符型如下:select * from table where name=’admin’;
因此,在构造payload时通过闭合单引号可以成功执行语句。
测试步骤:
(1) 加单引号:select * from table where name=’admin’’;
由于加单引号后变成三个单引号,则无法执行,程序会报错;
(2) 加 ’and 1=1 此时sql 语句为:select * from table where name=’admin’ and 1=1’ ,也无法进行注入,还需要通过注释符号将其绕过;
因此,构造语句为:select * from table where name =’admin’ and 1=–’ 可成功执行返回结果正确;
(3) 加and 1=2— 此时sql语句为:select * from table where name=’admin’ and 1=2–’则会报错;
如果满足以上三点,可以判断该url为字符型注入。
3.搜索性注入
主要涉及到应用程序对用户输入的搜索关键词处理不当。当应用程序允许用户通过搜索功能查询数据库中的信息时,如果它没有对用户输入的搜索关键词进行严格的验证和过滤,攻击者就可以利用这个漏洞来构造恶意的搜索请求,从而实现对数据库的非法操作。
这是一类特殊的注入类型。具体来说,是这类注入在进行数据搜索时没过滤搜索参数,一般在链接地址中有 keyword=关键字 ,有的不显示在的链接地址里面,而是直接通过搜索框表单提交。此类注入点提交的 SQL 语句,其原形大致为:select * from 表名 where 字段 like ‘%关键字%’
注入点提交方式
1.Get注入
SQL Get注入原理主要涉及到Web应用程序对用户通过GET方法提交的参数处理不当。当用户在浏览器的地址栏中直接输入URL,并通过GET方法向服务器发送请求时,应用程序会解析URL中的参数并用于构造SQL查询语句。如果应用程序没有对用户提交的GET参数进行严格的验证和过滤,攻击者就可以构造恶意的URL,通过GET参数插入恶意的SQL代码,从而实现对数据库的非法操作。
2.Post注入
SQL Post注入原理主要涉及到Web应用程序对用户通过POST方法提交的参数处理不当。当用户通过HTML表单等方式向服务器提交数据时,通常使用POST方法。在这个过程中,用户输入的数据被包含在HTTP请求的正文中,并通过POST参数传递给服务器。如果应用程序没有对用户通过POST方法提交的参数进行充分的验证和过滤,攻击者就可以构造恶意的POST请求,通过插入恶意的SQL代码来实现对数据库的非法操作。
3.Cookie注入
SQL Cookie注入原理主要涉及到攻击者利用Web应用程序对Cookie处理不当的漏洞,通过修改或伪造Cookie中的值,将恶意的SQL代码注入到应用程序的数据库查询中,从而执行非授权的操作或获取未经授权的数据。
在Web应用程序中,Cookie通常用于在服务器和客户端之间传递数据,如用户身份验证信息、会话标识符等。当用户登录网站或执行某些操作时,服务器会生成一个包含相关信息的Cookie,并将其发送给客户端(浏览器)。随后,浏览器在每次请求时都会自动携带这个Cookie发送给服务器,以便服务器识别用户身份或维护会话状态。
然而,如果应用程序对Cookie中的数据进行不充分的验证和过滤,攻击者就可以构造恶意的Cookie值,并在其中插入恶意的SQL代码。当应用程序解析这个恶意的Cookie并构造数据库查询语句时,恶意SQL代码就有可能被嵌入到查询中并被执行。
4.HTTP头部注入
HTTP头部注入原理主要基于攻击者利用Web应用程序对HTTP请求头部字段处理不当的漏洞,通过修改或伪造请求头中的信息,将恶意的SQL代码注入到应用程序的数据库查询中,从而执行非授权的操作或获取未经授权的数据。
HTTP请求头部包含了丰富的信息,如User-Agent、Cookie、X-Forwarded-For等,这些字段在Web应用程序中扮演着重要的角色。例如,User-Agent字段用于标识客户端的类型和版本信息,Cookie字段用于传递会话状态信息,X-Forwarded-For字段用于记录客户端的IP地址等。
然而,如果Web应用程序对这些请求头字段中的数据没有进行充分的验证和过滤,攻击者就有可能通过修改这些字段的值,插入恶意的SQL代码。当应用程序解析这些恶意的请求头字段并构造数据库查询语句时,恶意SQL代码就有可能被嵌入到查询中并被执行。
四、实例演示
首先创建一个测试表,并插入简单的数据(MySQL数据库):
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) DEFAULT NULL,
`password` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `t_user` VALUES ('1', 'John', '111111');
INSERT INTO `t_user` VALUES ('2', 'Tom', '222222');
接下来是Java程序部分,采用Spring Boot加上MyBatis的方式,如果对于搭建Spring Cloud工程还不太熟悉,可以参考之前的文章:手把手:Spring Cloud Alibaba项目搭建
在pom.xml文件中添加依赖:
<!-- 连接Spring Boot和MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- mysql 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
然后是Java各层的代码,从controller一直到dao,为了完整起见,我把代码都贴出来。controller层有一个方法,参数为用户名,返回该用户的详细信息,为了演示方便,此处返回一个列表:
package com.fullstack.commerce.user.controller;
import com.fullstack.commerce.user.entity.User;
import com.fullstack.commerce.user.service.UserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("user")
public class UserController {
@Resource
private UserService userService;
@RequestMapping("getUserInfo")
@ResponseBody
// 根据用户姓名查询出用户列表信息
public List<User> getUserInfo(@RequestParam("username")String username){
List<User> result = userService.getUserInfo(username);
return result;
}
}
service层比较简单,有一个接口和对应的实现类,而实现类就是调用dao的方法,把用户列表查询出来:
package com.fullstack.commerce.user.service;
import com.fullstack.commerce.user.entity.User;
import java.util.List;
public interface UserService {
List<User> getUserInfo(String username);
}
package com.fullstack.commerce.user.service.impl;
import com.fullstack.commerce.user.dao.UserDao;
import com.fullstack.commerce.user.entity.User;
import com.fullstack.commerce.user.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
@Override
public List<User> getUserInfo(String username) {
return userDao.getUserInfo(username);
}
}
dao层就是一个接口,里面只有一个方法:
package com.fullstack.commerce.user.dao;
import com.fullstack.commerce.user.entity.User;
import java.util.List;
public interface UserDao {
List<User> getUserInfo(String username);
}
除了上面的业务代码,还需要有一个启动类:
package com.fullstack.commerce.user;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages = "com.fullstack.commerce.user.dao")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
以下是对应的mapper文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fullstack.commerce.user.dao.UserDao">
<resultMap id="UserMap" type="com.fullstack.commerce.user.entity.User">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
</resultMap>
<select id="getUserInfo" resultMap="UserMap">
SELECT * FROM t_user WHERE username = ${username}
</select>
</mapper>
注意,在上面这个mapper文件中,只有一个select语句,而这个语句传参使用了符号$,它会把参数值直接进行替换,而不会进行预编译(如果使用占位符#,就会进行预编译,从而可以防止SQL注入)。
当然,application.yml文件也需要配置一下,数据库用户名和密码替换成实际的:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: myuser
password: myuser
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: mapper/*.xml
代码就算写完了,接下来我们测试一下看看。
五、测试验证:
启动程序,我们来进行测试,在浏览器里面输入以下地址,然后回车:
http://localhost:8080/user/getUserInfo?username='John'
没问题,我们是要查找用户名为John的数据,它返回了如下的结果,正是我们所期望的:
但如果我们把参数重新设置一下,变成如下这样:
http://localhost:8080/user/getUserInfo?username='John' OR 1=1
那就相当于在数据库中执行下面的SQL了,也就是返回所有的用户数据:
SELECT * FROM t_user WHERE username = 'John' OR 1=1
看结果,确实是这样,把表中的两条记录都返回到了客户端(测试数据只插了两条记录,如果有多条,同样会把所有的数据都返回过来):
这显然是不对的,接口把查询范围之外的数据都搜索出来了,如果还有一些修改数据或者操纵数据库的一些命令,那就更危险了。
当然,我们把mapper文件里面的参数部分改成占位符#,这样就会先进行预编译,就不会发生刚才的情况了。修改成#以后运行程序,再执行上面的url,就会返回空了,因为这个时候是去数据库中查询用户名为【‘John’ OR 1=1】的记录,显然是不存在的。
网络安全学习资源分享:
给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
因篇幅有限,仅展示部分资料,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,需要点击下方链接即可前往获取
读者福利 |
CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享 (安全链接,放心点击)
同时每个成长路线对应的板块都有配套的视频提供:
学习资料工具包
压箱底的好资料,全面地介绍网络安全的基础理论,包括逆向、八层网络防御、汇编语言、白帽子web安全、密码学、网络安全协议等,将基础理论和主流工具的应用实践紧密结合,有利于读者理解各种主流工具背后的实现机制。
面试刷题
视频配套资料&国内外网安书籍、文档
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料
所有资料共282G,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,可以扫描下方二维码或链接免费领取~
读者福利 |
CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享 (安全链接,放心点击)