【问题标题】:How to use MySQLi Prepared Statements with Stored Procedures如何将 MySQLi 准备好的语句与存储过程一起使用
【发布时间】:2011-07-29 01:51:00
【问题描述】:

我正在尝试了解有关 MySQL 以及如何防止 SQL 注入的更多信息,因此我的研究将我带到了准备好的语句,这似乎是要走的路。

我也在努力学习如何编写存储过程,现在正在尝试将两者结合起来。不过这方面的信息不多。

目前,在我的 PHP 测试应用程序中,我有一个函数可以使用普通的 MySQL 命令调用 SP,如下所示:

mysql_query("CALL usp_inserturl('$longurl', '$short_url', '$source')");

我怎样才能对 MySQLi 和 Prepared Statement 执行相同的操作,以使其对注入尽可能安全?

谢谢!

【问题讨论】:

  • 为什么似乎没有人直接回答这个问题(带有代码示例)?

标签: php mysql stored-procedures mysqli prepared-statement


【解决方案1】:

尝试以下方法:

$mysqli= new mysqli(... info ...); 
$query= "call YourSPWithParams(?,?,?)"; 
$stmt = $mysqli->prepare($query); 
$x = 1; $y = 10; $z = 14;
$stmt->bind_param("iii", $x, $y, $z); 
$stmt->execute(); 

【讨论】:

  • 酷!几乎与任何其他 SELECT 声明相似;唯一的区别是您必须使用CALL 启动 SQL。
  • 您能否添加代码来说明如何从$stmt 检索结果?
  • @Gruber 您可以在此处找到示例 #5 中如何检索数据的示例:php.net/manual/en/mysqli.quickstart.stored-procedures.php
  • @Gruber,这是准备好的语句的要点,为进一步的多次重用做准备。您只使参数 bing 一次(它们与您的语句有 ref 关系,然后只需通过例如 loopp 更改参数(变量),并在此循环内多次执行已经准备好的语句。您不必准备 od 绑定参数再次在这个循环中。
【解决方案2】:

您可以同时使用它们:只需使用存储过程进行准备:

//prepare and bind SP's parameters with your variables only once
$stmt=$db->prepare("CALL MyStoredProc(?,?)");
$stmt->bind_param('is',$i,$name);

//then change binded variables and execute statement
for($i=1;$i<9;$i++)
{
  $name="Name".$i;
  $stmt->execute();
}

请记住,您应该只进行一次准备工作(而不是每次执行都重复一次),然后再执行多次(只需更改之前的参数值)。

【讨论】:

    【解决方案3】:

    您可能会找到以下使用答案:

    MySql: Will using Prepared statements to call a stored procedure be any faster with .NET/Connector?

    另外:

    仅授予执行权限,以便您的应用程序级用户只能调用存储过程。这样,您的应用程序用户只能通过您的存储过程 API 与数据库交互,他们不能直接:

    select, insert, delete, update, truncate, drop, describe, show etc. 
    

    没有比这更安全的了。唯一的例外是,如果您在存储过程中使用了动态 sql,我会不惜一切代价避免 - 或者至少要意识到这样做的危险。

    在构建数据库时,例如foo_db,我通常创建两个用户。第一个 foo_dbo(数据库所有者)是拥有数据库并被授予完全权限 (ALL) 的用户,因此他们可以根据需要创建模式对象和操作数据。第二个用户 foo_usr(应用程序用户)仅被授予执行权限,并在我的应用程序代码中用于通过我创建的存储过程 API 访问数据库。

    grant all on foo_db.* to foo_dbo@localhost identified by 'pass';
    
    grant execute on foo_db.* to foo_usr@localhost identified by 'pass';
    

    最后,您可以使用 mysql_real_escape_string 改进上面的代码示例:

    【讨论】:

      【解决方案4】:

      这个有点棘手,但我最终想出了如何使用使用准备好的语句的存储过程(使用 IN 参数)并通过 PHP 检索数据。本示例使用 PHP 7.4.6 和 MySQL 8.0.21 社区版。

      这是存储过程:

      CREATE DEFINER=`root`@`loalhost` PROCEDURE `SP_ADMIN_SEARCH_PLEDGORS`(
          IN P_email VARCHAR(60),
          IN P_password_hash VARCHAR(255),
          IN P_filter_field VARCHAR(80),
          IN P_filter_value VARCHAR(255)
      )
      BEGIN
          #Takes admin credentials (first tow paramaters and searches the pledgors_table where field name (P_filter_field) is LIKE value (%P_filter_value%))
          DECLARE V_admin_id INT(11);
          BEGIN
              GET DIAGNOSTICS CONDITION 1 @ERRNO = MYSQL_ERRNO, @MESSAGE_TEXT = MESSAGE_TEXT;
              SELECT 'ERROR' AS STATUS, CONCAT('MySQL ERROR: ', @ERRNO, ': ', @MESSAGE_TEXT) AS MESSAGE;
          END;
          SELECT admin_id INTO V_admin_id FROM admin_table WHERE password_hash = P_password_hash AND email = P_email;
          IF ISNULL(V_admin_id) = 0 THEN    
              SET @statement = CONCAT('SELECT pledgor_id, email, address, post_code, phone, alt_phone, contact_name
              FROM pledgors_table
              WHERE ',P_filter_field, ' LIKE \'%', P_filter_value, '%\';');
              PREPARE stmnt FROM @statement;
              EXECUTE stmnt;
          ELSE
              SELECT 'ERROR' AS STATUS, 'Bad admin credentials' AS MESSAGE;
          END IF;
      END
      

      这是 PHP 脚本

      query = 'CALL SP_ADMIN_SEARCH_PLEDGORS(\''.
      strtolower($email).'\', \''.
      $password_hash.'\', \''.
      $filter_field.'\', \''.
      $filter_value.'\');';
      
      $errNo = 0;
      //$myLink is a mysqli connection
      if(mysqli_query($myLink, $query)) {
          do {
              if($result = mysqli_store_result($myLink)) {
                  while($row = mysqli_fetch_assoc($result)) {
                      $data[] = $row;
                  }
                  mysqli_free_result($result);
              }
          } while(mysqli_next_result($myLink));
      }
      else {
          $errNo = mysqli_errno($myLink);
      }
      mysqli_close($myLink);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-26
        相关资源
        最近更新 更多