Java最新SQL注入原因以及预防方案

发表于:2021-1-12 09:34

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:敲敲敲代码    来源:今日头条

  前沿
  在现有的框架中SQL防注入已经做得很好了,我们需要做的就是尽量不要使用SQL拼接调用。
  1. SQL注入
  1.1 原理
  SQL注入是通过客户端的输入把SQL命令注入到一个应用的数据库中,从而执行恶意的SQL语句。
  1.2 演示
  1.2.1 案例1
  有一个登录框,需要 输入用户名和密码 ,然后我们的密码输入 'or '123' = '123 这样的。我们在查询用户名和密码是否正确的时候,本来执行的sql语句是:select * from user where username = '' and password = ''. 这样的sql语句,现在我们输入密码是如上这样的,然后我们会通过参数进行拼接,拼接后的sql语句就是:
  select * from user where username = '' and password = ' ' or '123' = '123 ';这样的了,那么会有一个or语句,只要这两个有一个是正确的话,就条件成立,因此 123 = 123 是成立的。因此验证就会被跳过。这只是一个简单的例子,
  1.2.2 案例2
  密码比如是这样的:'; drop table user;, 这样的话,那么sql命令就变成了:
  select * from user where username = '' and password = ''; drop table user;', 那么这个时候我们会把user表直接删除了。
  1.3 防范
  1.3.1 前端
  前端表单进行参数格式控制;
  1.3.2 后端
  我们可以使用预编译语句(PreparedStatement,这 样的话即使我们使用sql语句伪造成参数,到了服务端的时候,这个伪造sql语句的参数也只是简单的字符,并不能起到攻击的作用。
  使用正则表达式过滤传入的参数。
  注意: 永远也不要把未经检查的用户输入的值直接传给数据库。
  java中的验证字符串是否包含sql的判断
  package cn.javanode.thread; 
   
  import java.util.regex.Pattern; 
   
  /** 
   * @author xgt(小光头) 
   * @version 1.0 
   * @date 2021-1-8 11:48 
   */ 
  public class CheckSqlDemo { 
   
      /**正则表达式**/ 
      private static String reg = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|" 
              + "(\\b(select|update|union|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)"; 
   
   
      private static Pattern sqlPattern = Pattern.compile(reg, Pattern.CASE_INSENSITIVE); 
   
      private static boolean isValid(String str) { 
          if (sqlPattern.matcher(str).find()) 
          { 
              System.out.println("未能通过过滤器:str=" + str); 
              return false; 
          } 
          return true; 
      } 
   
      public static void main(String[] args) { 
          System.out.println(isValid("tongji_user_add")); 
      } 
   
  } 
  补充
  PreparedStatement是如何防止SQL注入的?
  1. 拼接参数(sql注入)
  Connection connection = DriverManager.getConnection(DB_URL, USER, PASS); 
          PreparedStatement preparedStatement = connection.prepareStatement(sql);         
          String param = "'test' or 1=1"; 
          String sql = "select file from file where name = " + param; // 拼接SQL参数 
          ResultSet resultSet = preparedStatement.executeQuery(); 
          System.out.println(resultSet.next());
  输出结果为 true ,DB中执行的SQL为
  -- 永真条件1=1成为了查询条件的一部分,可以返回所有数据,造成了SQL注入问题 
  select file from file where name = 'test' or 1=1 
  2. setString (防注入)
  Connection connection = DriverManager.getConnection(DB_URL, USER, PASS); 
          PreparedStatement preparedStatement = connection.prepareStatement(sql);     
          preparedStatement.setString(1,account);//设置参数 
          preparedStatement.setString(2,password); 
          ResultSet resultSet = preparedStatement.executeQuery();//执行查询sql,获取结果集 
  输出结果为 false ,DB中执行的SQL为
  select file from file where name = '\'test\' or 1=1' 
  我们可以看到输出的 SQL是把整个参数用引号包起来,并把参数中的引号作为转义字符,从而避免了参数也作为条件的一部分
  3. 源码分析
  结论
  ·preparedStatement.setString 会判断当前参数的符号是否需要转义,是的话加的转义符。
  ·如果不需要,则直接加上引号。

  //完整代码 
  public void setString(int parameterIndex, String x) throws SQLException { 
         synchronized (checkClosed().getConnectionMutex()) { 
             // if the passed string is null, then set this column to null 
             if (x == null) { 
                 setNull(parameterIndex, Types.CHAR); 
             } else { 
                 checkClosed(); 
   
                 int stringLength = x.length(); 
   
                 if (this.connection.isNoBackslashEscapesSet()) { 
                     // Scan for any nasty chars 
                     // 判断是否需要转义 
                     boolean needsHexEscape = isEscapeNeededForString(x, stringLength); 
   
                     if (!needsHexEscape) { 
                         byte[] parameterAsBytes = null; 
   
                         StringBuilder quotedString = new StringBuilder(x.length() + 2); 
                         quotedString.append('\''); 
                         quotedString.append(x); 
                         quotedString.append('\''); 
   
                         if (!this.isLoadDataQuery) { 
                             parameterAsBytes = StringUtils.getBytes(quotedString.toString(), this.charConverter, this.charEncoding, 
                                     this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor()); 
                         } else { 
                             // Send with platform character encoding 
                             parameterAsBytes = StringUtils.getBytes(quotedString.toString()); 
                         } 
   
                         setInternal(parameterIndex, parameterAsBytes); 
                     } else { 
                         byte[] parameterAsBytes = null; 
   
                         if (!this.isLoadDataQuery) { 
                             parameterAsBytes = StringUtils.getBytes(x, this.charConverter, this.charEncoding, this.connection.getServerCharset(), 
                                     this.connection.parserKnowsUnicode(), getExceptionInterceptor()); 
                         } else { 
                             // Send with platform character encoding 
                             parameterAsBytes = StringUtils.getBytes(x); 
                         } 
   
                         setBytes(parameterIndex, parameterAsBytes); 
                     } 
   
                     return; 
                 } 

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号