SQL注入
SQL注入是通过操作输入来修改事先定义好的SQL语句,对用户输入的字符串进行过滤,转义,限制或处理不严谨,导致用户可以通过输入精心构造的字符串去非法获取到数据库中的数据,以达到执行代码对服务器进行攻击的方法。
现有一个数据库test中的表user,可以通过账号name,密码pass登录,查看id
登录代码
package JDBCtest; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.Scanner; /* * 用户登录 */ public class Demo4 { public static void main(String[] args) throws Exception { // 1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.创建连接 String url = "jdbc:mysql:///test"; String username = "root"; String password = "1234"; Connection connection = DriverManager.getConnection(url, username, password); // 接收用户名密码 Scanner sc = new Scanner(System.in); String name = sc.next(); String pass = sc.next();// 3.sql语句 String sql = "Select * from user where name='" + name + "' and pass ='" + pass + "'"; // System.out.println(sql); // 4.获取sql对象statement Statement statement = connection.createStatement(); // 5.执行sql语句 ResultSet rs = statement.executeQuery(sql); // 6.登录 if (rs.next()) { System.out.println("登录成功"); } else { System.out.println("登录失败"); } // 7.释放资源 statement.close(); connection.close(); } }
通过表中账号密码登录成功
由于账号或密码错误登录失败
以上可以正确登录成功或失败
注意!
如果此时我这样输入 【lihua 'or'1'='1
】,也成功登录了,但是数据库没根本没有这条数据
这是为什么呢?让我们从代码里找问题!
String sql = "Select * from user where name='" + name + "' and pass ='" + pass + "'";
为了调用数据库,使用字符串拼接SQL语句
让我们打印一下输入 【lihua 'or'1'='1
】的sql语句一探究竟
select语句中的where条件可以看做两组并列(注意and在前先执行,or后执行)
由于'1'='1'为TRUE,所以以上语句等价于肯定会登录成功!
同理输入【'or'1'='1'# xxx
】也能登陆成功
这是由于#在SQL中是注释符号,以上语句等价于,于是就和上述情况一样了。
附防止sql注入的一些建议
1. 代码层防止sql注入攻击的最佳方案就是sql预编译
public List<Course> orderList(String studentId){ String sql = "select id,course_id,student_id,status from course where student_id = ?"; return jdbcTemplate.query(sql,new Object[]{studentId},new BeanPropertyRowMapper(Course.class)); }
这样我们传进来的参数 4 or 1 = 1
就会被当作是一个student_id
,所以就不会出现sql注入了。
2. 确认每种数据的类型,比如是数字,数据库则必须使用int类型来存储
3. 规定数据长度,能在一定程度上防止sql注入
4. 严格限制数据库权限,能最大程度减少sql注入的危害
5. 避免直接响应一些sql异常信息,sql发生异常后,自定义异常进行响应
6. 过滤参数中含有的一些数据库关键词
@Component public class SqlInjectionFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { HttpServletRequest req=(HttpServletRequest)servletRequest; HttpServletRequest res=(HttpServletRequest)servletResponse; //获得所有请求参数名 Enumeration params = req.getParameterNames(); String sql = ""; while (params.hasMoreElements()) { // 得到参数名 String name = params.nextElement().toString(); // 得到参数对应值 String[] value = req.getParameterValues(name); for (int i = 0; i < value.length; i++) { sql = sql + value[i]; } } if (sqlValidate(sql)) { throw new IOException("您发送请求中的参数中含有非法字符"); } else { chain.doFilter(servletRequest,servletResponse); } } /** * 关键词校验 * @param str * @return */ protected static boolean sqlValidate(String str) { // 统一转为小写 str = str.toLowerCase(); // 过滤掉的sql关键字,可以手动添加 String badStr = "'|and|exec|execute|insert|select|delete|update|count|drop|*|%|chr|mid|master|truncate|" + "char|declare|sitename|net user|xp_cmdshell|;|or|-|+|,|like'|and|exec|execute|insert|create|drop|" + "table|from|grant|use|group_concat|column_name|" + "information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|*|" + "chr|mid|master|truncate|char|declare|or|;|-|--|+|,|like|//|/|%|#"; String[] badStrs = badStr.split("\\|"); for (int i = 0; i < badStrs.length; i++) { if (str.indexOf(badStrs[i]) >= 0) { return true; } } return false; } }