关于SQL注入的问题以及解决方法
1.关于SQL注入
什么是SQL注入:
首先看一下以下代码:
- String sql = "select* from users where username='" + userName
- + "' and password='" + passWord+"'";
- Connection conn = null;
- Statement state = null;
- ResultSet result;
- conn = JdbcUtil.getConnection();
- System.out.println(sql);
- try {
- state = conn.createStatement();
- result = state.executeQuery(sql);
这是一段根据传入用户名,密码查找用户表的代码。
在做用户登录的验证的时候,我们可能会根据用户所填写的用户名和密码在后台拼成一条SQL语句执行,去查用户表:
select* from users where username='张三' and password='小张',如果能查出结果则表示验证成功,允许登录,否则账号或密码错误不允许登录。那么在组成这条语句的过程中会存在一个叫做SQL注入的问题,就是用户在输入用户名或密码的时候填写某些内容使得后台所拼成的SQL语句语义有所变化。
举个例子,在没有防止SQL的情况下:假如我们知道一个用户叫做张三,但是不知道这个用户的密码是什么,我们依然可以在登录的时候在用户输入框写上:张三'# 然后密码框任意填:njksad。一点击登录,会发现居然能够登录上去。那是为什么呢?
这是因为#在SQL中的意思是注释,那么我们根据上面的情况来分析一下最终所拼成的SQL语句是怎样的,
select* from users where username='张三'#' and password='njksad'
为了让大家能够看清楚上面那条SQL语句,笔者特地加大显示,可以看到 username='张三' 之后是一个#
那就意味着之后的内容都是注释,也就是可以忽略掉那么这条语句真正发挥作用的部分就是:select* from users where username='张三'
直接变成了一条查找张三 的语句,完全不用经过密码验证。
2.防止SQL注入攻击
那么怎么才能做到防止SQL注入攻击呢?
在上面那段代码中,Statement的对象是用来执行SQL语句的,Statement有一个子类叫做PreparedStatement,可以做到防止SQL注入攻击,接下来我们来看看PreparedStatement有什么特点以及怎么使用:
PreparedStatement是Statement的孩子,不同的是,PreparedStatement使用预编译机制,在创建PreparedStatement对象时就需要将sql语句传入,传入的过程中参数要用?替代,这个过程回导致传入的sql被进行预编译,然后再调用PreparedStatement的setXXX将参数设置上去,由于sql语句已经经过了预编译,再传入特殊值也不会起作用了。
而且PreparedStatement使用了预编译机制,sql语句在执行的过程中效率比Statement要高。
- String sql = "select* from users where username=? and password=?";
- Connection conn = null;
- PreparedStatement state = null;
- ResultSet result;
- conn = JdbcUtil.getConnection();
- System.out.println(sql);
- try {
- state = conn.prepareStatement(sql);
- state.setString(1, userName);
- state.setString(2, passWord);
- result = state.executeQuery();