代码之家  ›  专栏  ›  技术社区  ›  rcs

防止IN子句内串联字符串的SQL注入

  •  2
  • rcs  · 技术社区  · 6 年前

    我有一个变量,它是一个字符串数组。我想传递变量的所有值,把它的所有元素连接成一个字符串。

    但我不确定这是否会带来SQL注入的风险。 我的代码:

    private string concatenateStrings(string[] sa)
    {
        StringBuilder sb = new StringBuilder();
    
        foreach (string s in sa)
        {
            if (sb.Length > 0)
            {
                sb.Append(",");
            }
            sb.Append("'");
            sb.Append(s);
            sb.Append("'");
        }
        return sb.ToString();
    }
    
    public void UpdateClaimSts(string[] ids)
    {
        string query = @"UPDATE MYTABLE
                        SET STATUS = 'X'
                        WHERE TABLEID in (" + concatenateStrings(ids) + ")";
    
        OracleCommand dbCommand = (OracleCommand)this.Database.GetSqlStringCommand(query) as OracleCommand;
        this.Database.ExecuteNonQuery(dbCommand, this.Transaction);
    }
    

    string query = @"UPDATE MYTABLE
                    SET STATUS = 'X'
                    WHERE TABLEID in (:ids)";
    
    OracleCommand dbCommand = (OracleCommand)this.Database.GetSqlStringCommand(query) as OracleCommand;
    
    dbCommand.Parameters.Add(":ids", OracleType.VarChar).Value = concatenateStrings(ids);
    this.Database.ExecuteNonQuery(dbCommand, this.Transaction);
    

    但它不起作用。有什么想法吗?

    3 回复  |  直到 6 年前
        1
  •  1
  •   Dmitrii Bychenko    6 年前

    部分 TABLEID 字段类型为 NUMBER 每个项目 在里面 sa 是一个 有效整数 :

    private string concatenateStrings(string[] sa) {
       return string.Join(", ", sa
         .Where(item => Regex.IsMatch(item, @"^\-?[0-9]+$"))); 
    } 
    
    public void UpdateClaimSts(string[] ids) {
      string query = string.Format(
        @"UPDATE MYTABLE
             SET STATUS = 'X'
           WHERE TABLEID IN ({0})", concatenateStrings(ids));
          ...
    

    一般情况下,您可以尝试使用 绑定变量 复数的 许多的 其中:

    public void UpdateClaimSts(string[] ids) {  
      // :id_0, :id_1, ..., :id_N   
      string bindVariables = string.Join(", ", ids
        .Select((id, index) => ":id_" + index.ToString()));
    
      string query = string.Format(
        @"UPDATE MYTABLE
             SET STATUS = 'X'
           WHERE TABLEID IN ({0})", bindVariables);
    
      // Do not forget to wrap IDisposable into "using"
      using (OracleCommand dbCommand = ...) {
        ...
        // Each item of the ids should be assigned to its bind variable
        for (int i = 0; i < ids.Length; ++i)
          dbCommand.Parameters.Add(":id_" + i.ToString(), OracleType.VarChar).Value = ids[i];
    
       ...
    
        2
  •  3
  •   Dmitrii Bychenko    6 年前

    创建一个PL/SQL过程(在PL/SQL包中),如下所示:

    TYPE TArrayOfVarchar2 IS TABLE OF MYTABLE.TABLEID%TYPE INDEX BY PLS_INTEGER;
    
    PROCEDURE UPDATE_MYTABLE(TABLEIDs IN TArrayOfVarchar2) IS
    BEGIN
    
        FORALL i IN INDICES OF TABLEIDs
        UPDATE MYTABLE SET STATUS = 'X' WHERE TABLEID = TABLEIDs(i);
    
    END;
    

    打个这样的电话:

    using (OracleCommand cmd = new OracleCommand("BEGIN UPDATE_MYTABLE(:tableId); END;"), con))
    {
      cmd.CommandType = CommandType.Text;
      // or
      // OracleCommand cmd = new OracleCommand("UPDATE_MYTABLE"), con);
      // cmd.CommandType = CommandType.StoredProcedure;
      var par = cmd.Parameters.Add("tableId", OracleDbType.Varchar2, ParameterDirection.Input);
      par.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
      par.Value = sa;
      par.Size = sa.Length;
    
      cmd.ExecuteNonQuery();
    }
    
        3
  •  0
  •   MT0    6 年前

    C#有一个 OracleCollectionType.PLSQLAssociativeArray

    不幸的是,它不支持将数组传递给SQL集合数据类型(可以在SQL查询中使用)。

    解决方法之一是要求DBA创建一个简单函数,将PL/SQL关联数组转换为SQL集合,然后将此函数用作查询中的中间步骤:

    CREATE TYPE varchar2s_array_type IS TABLE OF VARCHAR2(100)
    /
    
    CREATE PACKAGE utils IS
      TYPE varchar2s_assoc_array_type IS TABLE OF VARCHAR2(100) INDEX BY PLS_INTEGER;
    
      FUNCTION assoc_array_to_collection(
        p_assoc_array IN varchar2s_assoc_array_type
      ) RETURN varchar2s_array_type DETERMINISTIC;
    END;
    /
    
    CREATE PACKAGE BODY utils IS
      FUNCTION assoc_array_to_collection(
        p_assoc_array IN varchar2s_assoc_array_type
      ) RETURN varchar2s_array_type DETERMINISTIC
      IS
        p_array varchar2s_array_type := varchar2s_array_type();
        i PLS_INTEGER;
      BEGIN
        IF p_assoc_array IS NOT NULL THEN
          i := p_assoc_array.FIRST;
          LOOP
            EXIT WHEN i IS NULL;
            p_array.EXTEND();
            p_array(p_array.COUNT) := p_assoc_array(i);
            i := p_assoc_array.NEXT(i);
          END LOOP;
        END IF;
        RETURN p_array;
      END;
    END;
    /
    

    MEMBER OF 而不是 IN 在SQL语句中:

    UPDATE MYTABLE
    SET    STATUS = 'X'
    WHERE  TABLEID MEMBER OF utils.assoc_array_to_collection(:ids)
    

    我不是C用户,所以这只是给你一个方法的大致概念,即使语法不完全正确

    var par = cmd.Parameters.Add(":ids", OracleDbType.Varchar2, ParameterDirection.Input);
    par.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
    par.Value = ids;
    par.Size = ids.Length;
    cmd.ExecuteQuery();
    

    然后可以跨多个查询重用泛型函数。