一、SQL注入介绍
?
SQL注入就是将原本的SQL语句的逻辑结构改变,使得SQL语句的执行结果和原本开发者的意图不一样;
方法:在表单中将命令当作用户输入提交给程序;
?
二、SQL注入范例
?
这里我们根据用户登录页面
?
- <form?action=""?>??
- 用户名:<input?type="text"?name="username"><br/>??
- 密??码:<input?type="password"?name="password"><br/>??
- ?</form>??
?
?
预先创建一个表:
?
- create?table?user_table(??
- ????id??????int?Primary?key,??
- ????username????varchar(30),??
- ????password????varchar(30)??
- );??
- insert?into?user_table?values(1,'xiazdong-1','12345');??
- insert?into?user_table?values(2,'xiazdong-2','12345');??
?
一般查询数据库的代码如下:
- public?class?Demo01?{??
- ????public?static?void?main(String[]?args)?throws?Exception?{??
- ????????String?username?=?"xiazdong";??
- ????????String?password?=?"12345";????
- ????????String?sql?=?"SELECT?id?FROM?user_table?WHERE?"?+?"username='"?+?username??
- ????????????????+?"'AND?"?+?"password='"?+?password?+?"'";??
- ????????Class.forName("com.mysql.jdbc.Driver");??
- ????????Connection?con?=?DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","12345");??
- ????????PreparedStatement?stat?=?con.prepareStatement(sql);??
- ????????System.out.println(stat.toString());??
- ????????ResultSet?rs?=?stat.executeQuery();??
- ????????while(rs.next()){??
- ????????????System.out.println(rs.getString(1));??
- ????????}??
- ????}??
- }??
?
但是这里username=xiazdong,password=12345,
因此此处的SQL语句为:
?
- SELECT?id?FROM?user_table?WHERE?username='xiazdong'?AND?password='12345';??
?
如果我们把username和password的值变为:
username='? OR 1=1 --?
password=x
会变成一个很可怕的情况:将把数据库中所有用户都列出来,为什么呢?
因为SQL语句现在为:
?
- SELECT?id?FROM?user_table?WHERE?username=''?OR?1=1?--?'?AND?password='12345';??
?
因为--表示SQL注释,因此后面语句忽略;
因为1=1恒成立,因此 username='' OR 1=1? 恒成立,因此SQL语句等同于:
?
- SELECT?id?FROM?user_table;??
?
很奇妙吧....
?
三、解决方法
?
其实解决方法很简单,就是使用PreparedStatement即可;
?
?
1.限制错误信息的输出
这个方法不能阻止SQL注入,但是会加大SQL注入的难度,不会让注入者轻易得到一些信息,让他们任意破坏数据库。SQL Server有一些系统变量,如果我们没有限制错误信息的输出,那么注入着可以直接从出错信息获取,例如(假定这里用的string即字符类型并可以发生SQL注入):http://www.xxx.com/showdetail.aspx?id=49 and user>0 这句语句很简单,但却包含了SQL Server特有注入方法的精髓,。首先看看它的含义:首先,前面的语句是正常的,重点在and user>0,我们知道,user是SQL Server的一个内置变量,它的值是当前连接的用户名,类型为nvarchar。拿一个nvarchar的值跟int的数0比较,系统会先试图将nvarchar的值转成int型,当然,转的过程中肯定会出错,SQL Server的出错提示是:将nvarchar值 ”abc” 转换数据类型为 int 的列时发生语法错误,呵呵,abc正是变量user的值,这样,注入着就拿到了数据库的用户名。
众所周知,SQL Server的用户sa是个等同Adminstrators权限的角色,拿到了sa权限,几乎肯定可以拿到主机的Administrator了。上面的方法可以很方便的测试出是否是用sa登录,要注意的是:如果是sa登录,提示是将”dbo”转换成int的列发生错误,而不是”sa”。
当然注入者还可以输入不同的信息来得到他们想要的信息 ,上面只是其中一个例子,所以我们要限制错误信息的输出,从而保护我们的数据库数据,这里我给出.NET限制错误信息的方法:
在Web.Config文件中设置
<customErrors mode="On" defaultRedirect="error.aspx">
</customErrors>
这样当发生错误时候就不会讲信息泄露给外人。
2.限制访问数据库帐号的权限
对于数据库的任何操作都是以某种特定身份和相应权限来完成的,SQL语句执行前,在数据库服务器端都有一个用户权限验证的过程,只有具备相应权限的帐号才可能执行相应权限内的SQL语句。因此,限制数据库帐号权限,实际上就阻断了某些SQL语句执行的可能。不过,这种方法并不能根本解决SQL注入问题,因为连接数据库的帐号几乎总是比其他单个用户帐号拥有更多的权限。通过限制贴帐号权限,可以防止删除表的攻击,但不能阻止攻击者偷看别人的信息。
3.参数化使用命令
参数化命令是在SQL文本中使用占位符的命令。占位符表示需要动态替换的数据,它们通过Command对象Parameters集合来传送。能导致攻击的SQL代码可以写成:
Select * from employee where [email protected];
如果用户输入:?09105022’OR ‘1’=’1,将得不到何记录,因为没有一个用户ID与文本框中输入的’?09105022’OR ‘1’=’1’相等。参数化命令的语法随提供程序的不同略有差异。对于SQL SERVER提供程序,参数化命令使用命名的占位符(具有唯一的名字),而对于OLE DB提供程序,每个硬编码的值被问号代替。使用OLE DB提供程序时,需要保证参数的顺序和它们出现在SQL字符串中的位置一致。SQL SERVER提供程序没有这样的需求,因为它们用名字和占位符匹配。
4.调用存储过程
存储过程是存储在数据库服务器上的一系列SQL代码,存储过程与函数相似,有良好的逻辑封装结构,可以接收和返回数据。使用存储过程可以使代码更易于维护,因为对存储过程的更改不会导致应用程序的重新编译,使用存储过程还可以节省带宽,提高应用程序性能。因为存储过程是存储在数据库服务端的独立的封装体,调用存储过程可以保证应用程序只执行存储过程中的固定代码,从而杜绝SQL语句注入的可能。结合上述所讲的参数化命令,可以实现SQL代码可以实现的任何功能,并进一步提高应用程序的安全等级。
?
5.如果加密了,一旦被MD5加密或者其他加密方式加密的,那么密码就不会是完整的,那么就不存在这样的错误了。