【Web安全与防御】简析Sql注入与防御措施

本文介绍了SQL注入的基本原理及几种常见的防御方法,包括使用PreparedStatement、过滤SQL保留字和正则表达式检查等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

最近由于在CSDN上看到许多有关SQL注入问题的文章,以前因为是小白,重在学编程技术上,并没有在程序安全防护上大费周章。但是由于学习的路途越走越远,包括前段时间理工大教务处主页被黑(我同学也在网络工作室负责维护),让我深深的感受到,在Web应用上线之后,除了应付“高并发”之类的效率性问题之外,安全性问题也是非常重要的。俗话说的好,“不防君子防小人”,有些了解技术的“小人”们,可能处于娱乐或者是其它目的来hack网站,所以,一般的前台信息过滤只能防“君子”(为了提交的时候减轻后台的负担),对小人们来说,前台的信息过滤形同虚设,他们会利用各种手段(JavaScript、Ajax等)来更改前台与后台页面信息,甚至偷Cookie来达到掌控全局的目的。


我也是最近去了解Sql注入的,总结了部分经验供大家分享。

首先,什么是Sql注入呢?英文名称为SqlInject把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令(度娘说的)。

概念说的再好不如去实践一下,接下来咱们就看看什么是Sql注入,Sql注入的方式有哪些?

(下面使用的是JavaWeb技术,不过不影响大家看,因为和平台关系不大)


一.sql注入方式

我们日常访问的路径,如:http://www.example.com/,这个是一个正常的页面请求语句,没有涉及到任何动态请求数据库的动作,所以这种情况下是不会有SQL注入的。像http://www.example.com?userid=1这个URL请求,其中包含了一个Key-Value对(学过Web的这里就不多费口舌了),那么我们通过这个URL语句可以向数据库发送userid的数据,从而进行“增删改查”。也就是说"?"后面的数据是和数据库打交道的,那么在"?"后面跟的信息就有可能会被加入恶意的SQL语句。

我们做测试的数据库为Mysql,数据库名为user,表名users,其中的字段如下:

我储存了一些测试数据,如下:

我们有一个根据ID来查询用户信息的Dao层方法(这里id用String是为了模拟涵盖类似UUID那种类型的id),

    @Override
	public User getUserById(String userid) {
		User u=new User();
		
		Connection conn = null;
		Statement st = null;
		ResultSet rs = null;
		
		try
		{
			conn = DBOperator.getConnection();
			String sql = "select * from users where userid="+userid;
			st = conn.createStatement();
			rs = st.executeQuery(sql);
			if(rs.next())
			{
				u.setUserid(rs.getInt("userid"));
				u.setName(rs.getString("name"));
				u.setSex(rs.getString("sex"));
				u.setAge(rs.getString("age"));
				
			}
			
		}catch(Exception ex)
		{
			ex.printStackTrace();
			
		}finally
		{
			DBOperator.close(rs, st, conn);
			
		}
		
		return u;
	}

然后是执行这个Dao层方法并返回信息给网页的Servlet:

 package cn.edu.hpu.sqlinject.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.edu.hpu.sqlinject.dao.UserManager;
import cn.edu.hpu.sqlinject.dao.UserManagerImpl;
import cn.edu.hpu.sqlinject.domain.User;

public class GetUser extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request,response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		request.setCharacterEncoding("utf-8");
		response.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=UTF-8");
		
		String userid=(String)request.getParameter("userid");
		
		UserManager um=new UserManagerImpl();
		User u=um.getUserById(userid);
		
		if(u!=null){
			request.setAttribute("u", u);
			request.getRequestDispatcher("/index.jsp").forward(request, response);
		}else{
			response.sendRedirect("error.jsp");
		}
		
	}

}

之后我们访问这个路径,我们就得到了userid=2的用户的数据


很显然,我们的SQL语句是:

select * 
from users 
where userid=#{userid}

如果我们在where这里加上一句“ or 1=1”,这样的话,where条件就变为永真(where (userid=#{id} or 1=1)),这个时候我们的sql语句就会变为:

select * 
from users
那么我们就会把所有的users表中的数据都取出来。

所以,当我们输入这样的URL:http://localhost/SqlInjectTest/GetUser?userid=2 or 1=1

这个时候我们的SQL语句就变成了这样:

select * 
from users 
where userid=#{userid} or 1=1
然后很明显,我们的where已经失效了,这个时候我们取出的结果是什么样子呢?

结果是:


得到的不是id为2的用户,而是id为1的用户!!!

为什么?因为我们是取出所有的用户,但是我们在Dao方法中只接受了一个结果集,并把这个结果集封装给一个User对象带到最终的网页上去,所以我们这里只得到所有数据的第一条。


虽然刚刚不是什么惊天大Bug,但是足以让网站乱了套。如果你使用了SpringMVC框架,接收的参数是一个字符串类型(Serializable[] ids),反馈的结果的是List<User>集合,那么恭喜你,黑客会黑出你所有的用户信息:

通过一个简单的sql注入获取一些简单的信息,获取user用户信息并没有太大的价值,那么如果我们知道user信息的表名,是不是就可以利用它进行“增删查修”了?没有错,确实可以做到,但是前提是我们需要知道这个网站的数据库表名是什么。


我们可以对它来进行推测,使用试错法,试验几次。 我们先来试验它是什么类型的数据库,我们先推测一个数据库的表名,然后写一个sql语句,看看它会会不会有错,然后通过不断尝试来获取数据库真实的表名。

这里面我们假设表的名字就叫user,我们访问这么一个URL:

http://localhost/SqlInjectTest/GetUser?userid=2 or 1=(select count(*) from user)

访问后的界面如下:


我们前面的数据没有查出来,说明数据库在解析sql语句的时候出了错,最终没有输出数据(有些后台还会报错,通过错误前缀代码还可以推测是什么数据库)。说明我们推测的表名是错误的。

假设这个网站的设计者很随意,他设计的表名很简单,就是users。那么,当我们利用暴力测试之后知道它的表名是users的时候,我在URL路径后面注入各种“增删改查”的sql语句,那就会十分的危险。


二.防止sql注入的几种方法

(1)PreparedStatement
始终以PreparedStatement代替Statement

采用JDBC的预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。
使用好处:
(1).代码的可读性和可维护性.
(2).PreparedStatement尽最大可能提高性能.
(3).最重要的一点是极大地提高了安全性.
原理:
sql注入只对sql语句的准备(编译)过程有破坏作用
而PreparedStatement已经准备好了,执行阶段只是把输入串作为数据处理,
而不再对sql语句进行解析,准备,因此也就避免了sql注入问题.

PreparedStatement需要服务器端的支持来提高效率。比如在Oracle上就会有显著效果,而MySQL上select时不支持PreparedStatement(亲测)。


(2)过滤sql语句保留字

使用正则表达式来过滤一些sql关键字,如or、where等

package cn.edu.hpu.sqlinject.test;

public class MatchTest {
	private static String textsql= "'|and|exec|insert|select|delete|update|count|*|%" +
			"|chr|mid|master|truncate|char|declare|; |or|-|+|,";
	
	public static void main(String[] args) {
		String content="userid=2 or 1=1";
		boolean flag=false;
		
		String[] filter= textsql.split("\\|");
		
		for(int i=0; i<filter.length;i++) {
			System.out.println(filter[i]);
			content=content.replace(filter[i], "");
		}
		 
		System.out.println("过滤之后:"+content);
	}
}
同时也可以通过这种方式来使用关键字检查sql是否有注入:

<span style="font-size:12px;">package cn.edu.hpu.sqlinject.test;

public class MatchTest {
	private static String textsql= "'|and|exec|insert|select|delete|update|count|*|%" +
	"|chr|mid|master|truncate|char|declare|; |or|-|+|,";

	public static void main(String[] args) {
		String content="userid=2 or 1=1";
		boolean flag=false;
		
		String[] filter= textsql.split("\\|");
		
		for(int i=0; i<filter.length;i++) {
			flag=content.contains(filter[i]);
			if(!flag){
				break;
			}
		}
		
		if(flag){
			System.out.println("正常");
		}else{
			System.out.println("包含敏感字符");
		}
		
	}
}</span><span style="font-size:18px;">
</span>


(3)正则表达式检查sql

大致方法:

String textsql="^(.+)\\sand\\s(.+)|(.+)\\sor(.+)\\s$";
		
boolean flag=content.matches(textsql);

具体的正则表达式:
检测SQL meta-characters的正则表达式 :/(\%27)|(\’)|(\-\-)|(\%23)|(#)/ix
修正检测SQL meta-characters的正则表达式 :/((\%3D)|(=))[^\n]*((\%27)|(\’)|(\-\-)|(\%3B)|(:))/i
典型的SQL 注入攻击的正则表达式 :/\w*((\%27)|(\’))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix
检测SQL注入,UNION查询关键字的正则表达式 :/((\%27)|(\’))union/ix(\%27)|(\’)
检测MS SQL Server SQL注入攻击的正则表达式:/exec(\s|\+)+(s|x)p\w+/ix
等等…..


(4)其它

还有其它的方式,只要保证传入的参数仅仅是数值而包含sql关键字等信息,或者在执行sql前将参数格式化等等措施,都可以防止sql注入。

转载请注明出处:http://blog.csdn.net/acmman/article/details/48862841

SQL注入是Internet上最危险、最有名的安全漏洞之一,本书是目前唯一一本专门致力于讲解SQL威胁的图书。本书作者均是专门研究SQL注入安全专家,他们集众家之长,对应用程序的基本编码和升级维护进行全面跟踪,详细讲解可能引发SQL注入的行为以及攻击者的利用要素,并结合长期实践经验提出了相应的解决方案。针对SQL注入隐蔽性极强的特点,本书重点讲解了SQL注入的排查方法和可以借助的工具,总结了常见的利用SQL漏洞的方法。另外,本书还专门从代码层和系统层的角度介绍了避免SQL注入的各种策略和需要考虑的问题。   本书主要内容   SQL注入一直长期存在,但最近有所增强。本书包含所有SQL注入攻击相关的、当前已知的信息,凝聚了由本书作者组成的、无私奉献的SQL注入专家团队的所有深刻见解。   什么是SQL注入?理解它是什么以及它的基本原理   查找、确认和自动发现SQL注入   查找代码中SQL注入时的提示和技巧   使用SQL注入创建利用   通过设计来避免由SQL攻击所带来的危险 第1章 什么是SQL注入 1 1.1 概述 2 1.2 理解Web应用的工作原理 2 1.2.1 一种简单的应用架构 3 1.2.2 一种较复杂的架构 4 1.3 理解SQL注入 5 1.4 理解SQL注入的产生过程 10 1.4.1 构造动态字符串 10 1.4.2 不安全的数据库配置 16 1.5 本章小结 18 1.6 快速解决方案 18 1.7 常见问题解答 19 第2章 SQL注入测试 21 2.1 概述 22 2.2 寻找SQL注入 22 2.2.1 借助推理进行测试 22 2.2.2 数据库错误 29 2.2.3 应用响应 38 2.2.4 SQL盲注 42 2.3 确认SQL注入 45 2.3.1 区分数字和字符串 46 2.3.2 内联SQL注入 46 2.3.3 终止式SQL注入 51 2.3.4 时间延迟 59 2.4 自动寻找SQL注入 60 2.5 本章小结 68 2.6 快速解决方案 68 2.7 常见问题解答 69 第3章 复查代码中的SQL注入 71 3.1 概述 72 3.2 复查源代码中的SQL注入 72 3.2.1 危险的编码行为 74 3.2.2 危险的函数 79 3.2.3 跟踪数据 82 3.2.4 复查PL/SQL和T-SQL代码 88 3.3 自动复查源代码第1章 什么是SQL注入 94 3.3.1 YASCA 96 3.3.2 Pixy 96 3.3.3 AppCodeScan 97 3.3.4 LAPSE 97 3.3.5 SWAAT 97 3.3.6 Microsoft SQL注入源代码分析器 98 3.3.7 CAT.NET 98 3.3.8 商业源代码复查工具 98 3.3.9 Ounce 99 3.3.10 Fortify源代码分析器 100 3.3.11 CodeSecure 100 3.4 本章小结 100 3.5 快速解决方案 101 3.6 常见问题解答 102 第4章 利用SQL注入 105 4.1 概述 106 4.2 理解常见的利用技术 107 4.3 识别数据库 108 4.3.1 非盲跟踪 109 4.3.2 盲跟踪 112 4.4 使用UINON语句提取数据 113 4.4.1 匹配列 114 4.4.2 匹配数据类型 115 4.5 使用条件语句 119 4.5.1 方法1:基于时间 120 4.5.2 方法2:基于错误 122 4.5.3 方法3:基于内容 123 4.5.4 处理字符串 123 4.5.5 扩展攻击 125 4.5.6 利用SQL注入错误 126 4.5.7 Oracle中的错误消息 128 4.6 枚举数据库模式 131 4.6.1 SQL Server 131 4.6.2 MySQL 136 4.6.3 Oracle 139 4.7 提升权限 142 4.7.1 SQL Server 142 4.7.2 Oracle 147 4.8 窃取哈希口令 148 4.8.1 SQL Server 149 4.8.2 MySQL 150 4.8.3 Oracle 151 4.9 带外通信 154 4.9.1 E-mail 154 4.9.2 HTTP/DNS 157 4.9.3 文件系统 158 4.10 自动利用SQL注入 161 4.10.1 Sqlmap 161 4.10.2 Bobcat 164 4.10.3 BSQL 164 4.10.4 其他工具 166 4.11 本章小结 166 4.12 快速解决方案 167 4.13 常见问题解答 168 第5章 SQL盲注利用 171 5.1 概述 172 5.2 寻找并确认SQL盲注 173
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光仔December

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值