准备好的语句执行,带有可变数量的要绑定的参数

Eri*_*ver 6 mysql stored-procedures dynamic-sql prepared-statement

我使用的一些存储过程需要根据是否提供了过程输入参数来插入 WHERE 条件。为了避免潜在的注入点,我想对将成为内插标准的一部分的值使用参数绑定。

由于添加到准备好的语句中的条件以及要绑定的参数数量可能因用户输入而异,因此我设计了以下方法来确定将哪些变量传递给 EXECUTE 语句。这有效,但似乎不优雅。

CREATE PROCEDURE foo (IN mandatory INT, IN optional INT, IN optional2 VARCHAR(20))
  BEGIN

    SELECT
      0, '', '', mandatory, optional, optional2
    INTO 
      @params, @sql, @where, @m, @o1, @o2;

    IF (@o1 > '' AND @o1 IS NOT NULL) THEN
      SET @where = CONCAT(@where, ' AND field = ?');
      SET @params = @params + 1;
    END IF;
    IF (@o2 > '' AND @o2 IS NOT NULL) THEN
      SET @where = CONCAT(@where, ' AND field2 = ?');
      SET @params = @params + 3;
    END IF;

    SET @sql = CONCAT('
      SELECT id, bar FROM table
      WHERE
        baz = ?
        ', @where
    );
    PREPARE STMT FROM @sql;
    CASE @params
      WHEN 0 THEN EXECUTE STMT USING @m;
      WHEN 1 THEN EXECUTE STMT USING @m, @o1;
      WHEN 3 THEN EXECUTE STMT USING @m, @o2;
      WHEN 4 THEN EXECUTE STMT USING @m, @o1, @o2;
    END CASE;
    DEALLOCATE PREPARE STMT;

  END$$
Run Code Online (Sandbox Code Playgroud)

我知道替代方案:

  • 将调用这些存储过程的二进制文件具有一个函数,该函数尝试通过将用户提供的字符串传递给正则表达式来识别潜在的 SQL 注入。
  • 可以使用用户定义的函数动态构造给定输入数量的 EXECUTE 语句。

但是,我想知道是否还有其他人遇到过这种纯粹使用 SQL 处理 EXECUTE 语句的动态构造的愿望。

Mic*_*bot 6

至少在这个例子中,有一种比这更简单的方法。请记住,优化器总是试图以这样一种方式来计划查询执行,即涉及检索有效结果集所需工作量最少的路径将是所选择的路径。

SELECT * FROM t1 WHERE (col1 = 'foo') OR (1 = 1);
Run Code Online (Sandbox Code Playgroud)

服务器将始终返回所有行,因为 1 = 1 包含得出结论“是的,该行与WHERE子句匹配”所需的所有真实性......并且它不会打扰扫描 col1 中的值以查看它们是否包含 'foo ' 因为优化器已经找到了一种更明显、更简单、“成本更低”的方法......(any expression) OR (TRUE)总是正确的......而通往真相的最短路径是优化器应该找到并采用的路径。它不需要1 = 1逐行评估,因为这是一个常量表达式。

您甚至不需要准备好的语句,只需在过程中进行查询:

SELECT id, bar 
  FROM table
 WHERE baz = mandatory
   AND (optional IS NULL OR field = optional)
   AND (optional2 IS NULL OR field2 = optional2);
Run Code Online (Sandbox Code Playgroud)

如果程序变量“optional”为空,优化器意识到整个表达式(在该行的括号中)对于每一行都为真,无论如何,因为在运行查询的上下文中,“optional”是一个常量价值,并且optional is NULL永远是真的,就像1 = 1永远是真的一样。优化器意识到这也是一个常量表达式。“字段”的内容永远不需要评估,因为OR表达式已预先确定为真。可选 2 也是如此。

另一方面,如果“可选”不为空,则被optional IS NULL优化掉,因为该表达式不可能为真,而你只剩下AND (field = optional). 同样,对于“optional2”。

在每种情况下,优化器都会留下仍然可以适当使用索引的查询。

由于查询在过程体中,而不是准备好的语句,SQL 注入是不可能的,因为服务器没有机会模糊变量中的数据和文字查询之间的区别。