1. 关于 SQL 注入
什么是 SQL 注入:
由于 jdbc 程序在执行的过程中 sql 语句在拼装时使用了由页面传入参数,如果用户恶意传入一些 sql 中的特殊关键字,会导致 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();