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

引言

最近由于在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

时间: 2024-11-02 06:08:10

【Web安全与防御】简析Sql注入与防御措施的相关文章

SQL注入攻击:防御和检查SQL注入的手段

虽然前面有许多文章讨论了SQL注入,但今天所讨论的内容也许可帮助你检查自己的服务器,并采取相应防范措施.知彼知己,方可取胜.首先要清楚SQL注入攻击有哪些种类. 观察近来的一些安全事件及其后果,安全专家们已经得到一个结论,这些威胁主要是通过SQL注入造成的.虽然前面有许多文章讨论了SQL注入,但今天所讨论的内容也许可帮助你检查自己的服务器,并采取相应防范措施. SQL注入攻击的种类 知彼知己,方可取胜.首先要清楚SQL注入攻击有哪些种类. 1.没有正确过滤转义字符 在用户的输入没有为转义字符过滤

实例简析SQL嵌套子查询

  实例简析SQL嵌套子查询: 一些初级程序员常常对SQL语法中的子查询,由其对嵌套子查询(子查询中包含一个子查询)的使用比较生疏,本文就此做一个基本讲解,相信新手会有一定收获. 使用子查询的原则 1.一个子查询必须放在圆括号中. 2.将子查询放在比较条件的右边以增加可读性. 子查询不包含 ORDER BY 子句.对一个 SELECT 语句只能用一个 ORDER BY 子句, 并且如果指定了它就必须放在主 SELECT 语句的最后. ORDER BY 子句可以使用,并且在进行 Top-N 分析时

SQL注入漏洞防范措施与攻击原理

ASP编程门槛很低,新手很容易上路.在一段不长的时间里,新手往往就已经能够编出看来比较完美的动态网站,在功能上,老手能做到的,新手也能够做到.那么新手与老手就没区别了吗?这里面区别可就大了,只不过外行人很难一眼就看出来罢了.在界面的友好性.运行性能以及网站的安全性方面是新手与老手之间区别的三个集中点.而在安全性方面,新手最容易忽略的问题就是SQL注入漏洞的问题.用NBSI 2.0对网上的一些ASP网站稍加扫描,就能发现许多ASP网站存在SQL注入漏洞,教育网里高校内部机构的一些网站这种漏洞就更普

来自微软漏洞研究与防御BLOG的SQL注入防范

自去年下半年开始,很多网站被恶意代码说困扰,攻击者在动态网页的SQL数据库中注入恶意的HTML <script>标签.这种脚本攻击行为在2008年第一季度开始加速传播,并继续影响有漏洞的Web应用 . 这些Web应用存在以下几点共性: 使用ASP作为编程代码; 使用SQL Server数据库; 应用程序代码根据URI请求字符串生成动态SQL查询(http://consoto.com/widgets.asp? widget=sprocket). 这代表了一种新的SQL注入(SQL injecti

简析SQL Server数据库用视图来处理复杂的数据查询关系_MsSql

SQL Server数据库用视图来处理复杂的数据查询关系是本文我们主要要介绍的内容,该内容是这样想到的:在辅助教务系统那块的时候,我做的一个页面是对单个老师和整个学院老师的工作量查询,这个操作设计到了三个本数据库中的表和一个不同数据库中的一个教师信息表,如果用普通的SQL语句是非常难实现的,由于我刚开始做的视频播放系统,数据库的表相对比较少,没有涉及到这么复杂的处理关系,刚开始感觉很难. 后来想到用视图可以解决多个表的复杂关系,但是另外一张表是不同数据库的,是否依然能进行操作,经过测试之后,居然

简析SQL Server数据库用视图来处理复杂的数据查询关系

SQL Server数据库用视图来处理复杂的数据查询关系是本文我们主要要介绍的内容,该内容是这样想到的:在辅助教务系统那块的时候,我做的一个页面是对单个老师和整个学院老师的工作量查询,这个操作设计到了三个本数据库中的表和一个不同数据库中的一个教师信息表,如果用普通的SQL语句是非常难实现的,由于我刚开始做的视频播放系统,数据库的表相对比较少,没有涉及到这么复杂的处理关系,刚开始感觉很难. 后来想到用视图可以解决多个表的复杂关系,但是另外一张表是不同数据库的,是否依然能进行操作,经过测试之后,居然

“SQL注入”的前世今生和防御思路

"SQL1注入"流行之前,缓冲区溢出2是最有效的黑客渗透方法,但经历了一些严重事件后(如:Code Red.Nimda.SQL Slammer),现在很多网络管理员的安全意识增强了,一般都能及时安装系统补丁,而且软.硬件厂商都针对溢出问题做了很多解决方案,可以说:缓冲区溢出在黑客攻击中的路越来越窄.这时候,针对CGI3程序的渗透被黑客发现是更有效的办法,因为CGI程序作为Web应用程序的一部分,通常开发周期很短,相应的测试环节很少,普遍存在缺陷, 那么这些CGI程序就有可能成为突破点.

关于SQL注入防御函数

函数 刚刚在最爱白菜那里看到了一个SQL注入防御的函数,突然想起以前看到这些文章时的一直有个问题想不通的,我对于SQL注入的防御很简单,就以下两个函数: '####'##'## SQL注入攻击预防装置[字符型]'##'## @ data ->处理的数据'## @ length ->长度限制'##'## 例: strSql("SQL字符型数据",50)'##function strSql(data,length)'###############################

防御SQL注入的方法总结

SQL 注入是一类危害极大的攻击形式.虽然危害很大,但是防御却远远没有XSS那么困难. SQL 注入可以参见:https://en.wikipedia.org/wiki/SQL_injection SQL 注入漏洞存在的原因,就是拼接 SQL 参数.也就是将用于输入的查询参数,直接拼接在 SQL 语句中,导致了SQL 注入漏洞. 1. 演示下经典的SQL注入 我们看到:select id,no from user where id=2; 如果该语句是通过sql字符串拼接得到的,比如: Strin