1. SQL注入
观察如下SQL语句:
String sql = "select * from user where name='" + name + "' and password = '" + password + "'";
输入用户名和密码:
String name = "Harris";
String password = "123456";
拼接成了SQL查询语句:
select * from user where name='Harris' and password ='123456';
一切看似运转正常,数据库验证用户名和密码,验证成功时允许登录,否则不允许登录。
但是有经验的程序员会很快找到漏洞,输入这样的用户名和密码:
String name = "Harris';-- ";
String password = "";
拼接成了SQL查询语句:
select * from user where name='Harris';-- ' and password ='';
密码的验证被看成了注释,在只输入用户名的情况下,依然成功的登录了系统。这就是一个简单的SQL注入。
2. SQL注入实例
代码中使用SQL语句拼接很容易有SQL注入的漏洞,如下例:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class sql{
/**
* @param args
*/
//驱动程序就是之前在classpath中配置的JDBC的驱动程序的JAR 包中
public static final String DBDRIVER = "com.mysql.jdbc.Driver";
//连接地址是由各个数据库生产商单独提供的,所以需要单独记住
public static final String DBURL = "jdbc:mysql://192.168.0.31:3306/myDB";
//连接数据库的用户名
public static final String DBUSER = "root";
//连接数据库的密码
public static final String DBPASS = "";
public static boolean Login(String name, String password) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
boolean isLoginSuccess = false;
try {
Class.forName(DBDRIVER);
conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS);
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from user where name='" + name + "' and password = '" + password + "'");
while (rs.next()) {
isLoginSuccess = true;
}
} catch (SQLException e) {
e.printStackTrace();
}catch (Exception e1) {
}
finally {
try {
if (conn != null) conn.close();
if (stmt != null) stmt.close();
if (rs != null) rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return isLoginSuccess;
}
public static void main(String[] args) throws Exception {
boolean result = Login("Harris","123456"); //true
System.out.println(result);
}
}
用如下语句调用Login()函数时,只输入用户名,依然能够登录成功。
boolean result = Login("Harris';-- "," "); //true
3. SQL注入防范实例
为了避免SQL注入,采用PreparedStatement代替Statement。
public static boolean Login(String name, String password) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
boolean isLoginSuccess = false;
try {
Class.forName(DBDRIVER);
conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS);
stmt = conn.prepareStatement("select * from user where name =? and password =?");
stmt.setString(1, name);
stmt.setString(2, password);
rs = stmt.executeQuery();
while (rs.next()) {
isLoginSuccess = true;
}
} catch (SQLException e) {
e.printStackTrace();
}catch (Exception e1) {
}
finally {
try {
if (conn != null) conn.close();
if (stmt != null) stmt.close();
if (rs != null) rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return isLoginSuccess;
}
再次用如下语句调用Login()函数时,只输入用户名,登录失败。
boolean result = Login("Harris';-- "," "); //false
4.其他注意事项
- 严格管理数据库权限
仅给与应用程序访问数据库的最小权限,避免drop table等权限。
- 封装数据库错误
对后端异常信息进行必要的封装,避免直接将后端数据库异常信息暴露给用户。
- 机密信息禁止明文存储
机密信息需要加密处理,可以使用AES_ENCRYPT/AES_DECRYPT加密和解密。