作者:肖文伟
今天在IBM的站点上看到一篇关于系统安全的文章,文章是由伍斯特工业学院(Worcester Polytechnic Institute,WPI)的计算机科学在读研究生Bob Breznak写的.
中间有很多关于系统攻击方法,不禁让人深思:我们的系统到底有多脆弱呢?
文章位于:
http://www.ibm.com/developerworks/cn/rational/edge/08/may08/breznak/
我尝试了文中所讲的利用SQL注入的这种简单的攻击方法,以及我所知道的解决方法,我将它分享出来,希望你看过之后会有所收获.当然,我指的有所收获不是让你尝试去攻击别人的系统,而是让你在设计系统的时候也将这类安全问题考虑进去.
首先让我们来看看我们在JAVA中常用到的验证用户登录的SQL语句:
String sql="SELECT * FROM users WHERE Login_name='"+username+"' and Password='"+password+"'";
假设我的数据表users中有这样一些数据:
Login_name
|
Password
|
admin
|
abc#@4531
|
xiaowenwei
|
#@#Measuer71
|
1. 当用户在登录时输入:Login_name= "admin"; Password= "abc#@4531"时我们的SQL语句会变成:
String sql="SELECT * FROM users WHERE Login_name='admin' and Password=' abc#@4531'";
这样可以查看出来一条结果,系统也不会有问题,一切正常.
Ok,让我们再来看看下面的这种方式.
2. 当黑客在登录时输入:Login_name= "admin"; Password= "def' OR 1=1--"时我们的SQL语句会变成:(注意,用户输入法的Password字符串是: "def' OR 1=1--")
String sql="SELECT * FROM users WHERE Login_name='admin' and Password='def' OR 1=1-—'";
看出来什么了吗?
这句SQL会这样执行:
SELECT * FROM users WHERE Login_name='admin' and Password='def' OR 1=1
后面的-—'被当作SQL注释了.
这样结果我们可想而知,黑客不用知道密码便轻易登录上你的系统了,这样他便可以利用SQL注入在你的系统上做更多让你意想不到和害怕的事情!!!
解决方法(一):使用预处理器PreparedStatement
我尝试了如果系统中使用处理器Statement来执行登录验证的SQL语句的话,那你的系统就等着被黑吧.结果就会像上面所说的那样.黑客可以轻易登录上你的系统.
而我尝试将处理器改为预处理器之后,就不会发生这种问题了,代码如下:(下面代码示例是等着被黑的那种,中间注释了使用预处理器的方法),至于是为什么,还是你自己来想吧.
/**
* 根据用户名和密码查询用户资料
* @param username 用户登录名
* @param password 用户密码
* @return 用户实体
*/
public UserBean getUserByPwd(String username,String password)
{
//声明用户实体
UserBean user=null;
//获得数据库连接对象
Connection conn=DBConnection.getConnection();
//使用 预处理器 的SQL语句
//String sql="select * from Sys_user where Login_name=? and Password=?";
//使用 处理器 的SQL语句
String sql="select * from Sys_user where Login_name='"+username+"' and Password='"+password+"'";
try
{
//使用 预处理器 的方式:
/*PreparedStatement pstmt=conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs=pstmt.executeQuery();*/
//使用 处理器 的方式:
Statement stmt=conn.createStatement();
ResultSet rs=stmt.executeQuery(sql);
//如果有结果集
if(rs.next())
{
//获得结果集中的结果
long userid=rs.getLong("User_id");
String loginname=rs.getString("Login_name");
String pwd=rs.getString("Password");
String uname=rs.getString("User_name");
short gender=rs.getShort("Gender");
String privatephone=rs.getString("Private_phone");
String companyphone=rs.getString("Company_phone");
String email=rs.getString("Email");
short isactive=rs.getShort("Is_active");
//将结果构造成用户实体
user=new UserBean(userid,loginname,pwd,uname,gender,privatephone,companyphone,email,isactive);
}
} catch (SQLException e)
{
e.printStackTrace();
}
//返回用户实体
return user;
}
解决方法(二):对用户密码加密处理
这种方法比较可靠,我一般使用的是MD5加密方式,将用户密码加密成32位长度的16进制字符串.使用这种方式时,在系统中使用的过程一般为:
1. 注册新用户时,将用户输入的密码进行MD5加密后再保存进数据库;
2. 用户修改密码时,将用户输入的密码进行MD5加密后再保存进数据库;
3. 用户登录时,将用户输入的登录密码进行MD5加密后,再做SQL查询;
我们可以写一个专门用来将字符串进行MD5(或其它)加密的方法,在对密码进行操作时先加密处理就可以了.(至于MD5加密的处理,我会在下一篇中贴出来,先申明这不是我写的.)
这样即使黑客在输入密码时是使用的"def' OR 1=1--"之类的字符串,我们也会先将它进行字符串加密处理成32位长度的字符串,然后再做SQL查询.所以,这样就能保证系统这部分始终是安全的.
我想可能有部分好钻研的人,看到这会想到另一个问题.可能你也没有想到.其实那个问题就是如果我输入用户名的时候使用"def' OR 1=1--"那会发生什么情况呢?
可想而知,那你也可以通过验证,进入系统!
My god! 原来我们的系统是这么的不安全!是的,他就是这么的脆弱.
我想说的是,你最好是将解决方法(一)和解决方法(二)都使用起来,至少我是这么做的.
以上是我个人的见解,可能对于这部分你还有更好的方法,我希望能有机会和你一起来探讨这方面的问题.或者其它关于开发的问题.如果我所说的有错误也希望你能批评指正出来,谢谢.