Предотвращение SQL-инъекций для объединенной строки внутри предложения IN - PullRequest
0 голосов
/ 12 сентября 2018

У меня есть переменная, которая является массивом строк.Я хочу передать все значения переменной, объединяя все ее элементы в одну строку.

Но я не уверен, представляет ли это риск внедрения 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 ]

0 голосов
/ 12 сентября 2018

В качестве быстрого и частичного (мы предполагаем, что 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];

   ...
0 голосов
/ 12 сентября 2018

C # имеет тип OracleCollectionType.PLSQLAssociativeArray для передачи массивов в тип данных PL / SQL Associative Array, но его нельзя использовать в запросах SQL, поскольку это только структура данных PL / SQL.

Это, к сожалению,, не поддерживает передачу массива в тип данных SQL Collection (который может использоваться в запросе SQL).

Обходной путь для этого - попросить вашего администратора баз данных создать простую функцию для преобразования 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();

Затем вы можете повторно использовать универсальную функцию во многих запросах.

0 голосов
/ 12 сентября 2018

Создайте процедуру 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...