代码质量检查规则中的 is provided externally to the method and not sanitized b.

今天在解决小组内代码质量检查严重级别问题时遇到一条规则错误'"count"is provided externally to the method and not sanitized before use'(“count”在方法外部提供,在使用前未经消毒--谷歌翻译)本来感觉是挺容易解决的一个问题,却让我很是郁闷了一下,在这里跟大家共享一下!

接下来我们可以看一下SQ给出来的示例

Noncompliant Code Example

public User getUser(Connection con, String user) throws SQLException {

  Statement stmt1 = null;
  Statement stmt2 = null;
  PreparedStatement pstmt;
  try {
    stmt1 = con.createStatement();
    ResultSet rs1 = stmt1.executeQuery("GETDATE()"); // Compliant; parameters not used here

    stmt2 = con.createStatement();
    ResultSet rs2 = stmt2.executeQuery("select FNAME, LNAME, SSN " +
                 "from USERS where UNAME=" + user);  // Noncompliant; parameter concatenated directly into query

    pstmt = con.prepareStatement("select FNAME, LNAME, SSN " +
                 "from USERS where UNAME=" + user);  // Noncompliant; parameter concatenated directly into query
    ResultSet rs3 = pstmt.executeQuery();

    //...
}

public User getUserHibernate(org.hibernate.Session session, String userInput) {

  org.hibernate.Query query = session.createQuery(  // Compliant
            "FROM students where fname = " + userInput);  // Noncompliant; parameter binding should be used instead
  // ...
}
Compliant Solution

public User getUser(Connection con, String user) throws SQLException {

  Statement stmt1 = null;
  PreparedStatement pstmt = null;
  String query = "select FNAME, LNAME, SSN " +
                 "from USERS where UNAME=?"
  try {
    stmt1 = con.createStatement();
    ResultSet rs1 = stmt1.executeQuery("GETDATE()");

    pstmt = con.prepareStatement(query);
    pstmt.setString(1, user);  // Compliant; PreparedStatements escape their inputs.
    ResultSet rs2 = pstmt.executeQuery();

    //...
  }
}

public User getUserHibernate(org.hibernate.Session session, String userInput) {

  org.hibernate.Query query =  session.createQuery("FROM students where fname = ?");
  query = query.setParameter(0,userInput);  // Parameter binding escapes all input
  // ...

原始版本代码

String sqlSear = "";
            if(status == 1){
                sqlSear = "select C_ID, C_ZIP from DB_SJB.T_SJB where N_ZT = ? and N_RECEIVEID = " + this.receiveId + " order by D_DCSJ";
            }else{
                sqlSear = "select C_ID, C_ZIP from DB_SJB.T_SJB where N_ZT = ? and N_RECEIVEID = " + this.receiveId + " order by D_DCSJ limit "+ count;
            }
            queryPs = conn.prepareStatement(sqlSear);
            queryPs.setInt(1, status);
            rs = queryPs.executeQuery();

结合SQ给出的示例代码我们猜测:因为count是外部传递的,他可能认为这个值不可控,有可能造成注入漏洞,所以提示错误。

修正版本代码 1

String sqlSear = "";
            if(status == 1){
                sqlSear = "select C_ID, C_ZIP from DB_SJB.T_SJB where N_ZT = ? and N_RECEIVEID = " + this.receiveId + " order by D_DCSJ";
            }else{
                sqlSear = "select C_ID, C_ZIP from DB_SJB.T_SJB where N_ZT = ? and N_RECEIVEID = " + this.receiveId + " order by D_DCSJ limit ?";
            }
            queryPs = conn.prepareStatement(sqlSear);
            queryPs.setInt(1, status);
            // 代码质量检查规则不允许外部参数直接与执行语句相加,使用占位符
            if(status != 1){
                    queryPs.setInt(2, count);
            }
            rs = queryPs.executeQuery();

重新校验后发现错误依旧,只是去掉了count变量名 '""is provided externally to the method and not sanitized before use'

那么继续分析,思路依旧是注入漏洞。只剩下 this.receiveId 没有使用占位符了,有可能他导致的?

修正版本代码 2

String sqlSear = null;
            if(status == 1){
                sqlSear = "select C_ID, C_ZIP from DB_SJB.T_SJB where N_ZT = ? and N_RECEIVEID = ? order by D_DCSJ";
            }else{
                sqlSear = "select C_ID, C_ZIP from DB_SJB.T_SJB where N_ZT = ? and N_RECEIVEID = ? order by D_DCSJ limit ?";
            }
            queryPs = conn.prepareStatement(sqlSear);
            queryPs.setInt(1, status);
            queryPs.setInt(2, this.receiveId);
            // 代码质量检查规则不允许外部参数直接与执行语句相加,使用占位符
            if(status != 1){
                    queryPs.setInt(3, count);
            }
            rs = queryPs.executeQuery();

再次校验,错误提示没有任何改变,始终指向 queryPs = conn.prepareStatement(sqlSear) 这一行
这就不科学了呀,示例里面也是使用占位符规避这个问题的呀!代码中现在已经全部使用占位符了,你还要怎样?
但问题依然需要解决呀,那就求助谷歌大神吧!
网上针对这个问题的方案跟SQ自己的示例都差不多,都是改用占位符解决问题,但我的还是没解决啊(摔)!!!


在下面这个网址的代码示例中定义了四个字符串变量query,query2,query3,s(着重关注这四个,其他的略过)
对比后发现query定义时直接使用+字符串类型的参数,这样有注入漏洞的危险我们都知道,果然下面使用该变量时,后面都使用了Noncompliant关键字标注,代表不合格
但是使用query2和query3的地方都没有提示不合格。为什么呢?query2虽然有拼接,但是都是固定的值,不会存在注入漏洞;query3直接是没有任何变动的字符串,所以也不会出现注入漏洞。但是我们的修正代码版本2也不会有注入漏洞呀!!!(╯‵□′)╯︵┻━┻
修正代码版本2中的代码与query2和query3的区别是什么呢?最后发现示例中的query2和query3都是在定义时给了一个不为空的值,而我们的代码中给了一个null,所以猜测可能是初始值不能为空(空值或null),而他不知道你在初始化到执行conn.prepareStatement(sqlSear)之间对这个变量做了什么操作(或者他判断逻辑有误),执行时他的结果是什么(本质是因为他得到的变量结果和实际得到的结果不一致),导致后面的规则校验没有通过导致。
参考解决方案地址 https://github.com/SonarSource/sonar-java/blob/master/java-checks/src/test/files/checks/SQLInjection.java
PS:可以看到后面的变量s是直接定义的变量,没有给任何默认值,这样他也是认为合法。

修正版本代码 3

private static final String sqlSear = "select C_ID, C_ZIP from DB_SJB.T_SJB where N_ZT = ? and N_RECEIVEID = ? order by D_DCSJ ?";  //定义为类常量

问题解决!!!
可能本人level不够,感觉这是规则校验不完全合理,修正代码2从逻辑上来说我认为是合法的。请大神们指点!!!
PS:脚本中两个参数都是int类型的,所以不存在注入漏洞,如果这个规则是为了防止注入漏洞的话,不修改任何代码都算逻辑上的合法!如果从代码规范化上来说,修正版本2也应该是最终版本,不应该继续提示错误才对!!!


规则源码地址 https://github.com/SonarSource/sonar-java/blob/master/java-checks/src/main/java/org/sonar/java/checks/SQLInjectionCheck.javahttp://www.atetric.com/atetric/javadoc/org.codehaus.sonar-plugins.java/java-checks/2.7/src-html/org/sonar/java/checks/SQLInjectionCheck.html

猜你喜欢

转载自blog.csdn.net/leandzgc/article/details/103055207
今日推荐