从 PHP 代码中调用存储过程是否可以防止 SQL 注入?

问题描述 投票:0回答:2

我对存储过程以及如何使用它们来防止 SQL 注入感到非常困惑。我在网上读到的所有内容似乎都是矛盾的,每个地方都给出了一个似乎不起作用的例子。

这是否能够避免 SQL 注入?

PROCEDURE user_get_by_id (IN user_id INT)
BEGIN
    SELECT id, email, name, password_hash FROM users WHERE id=user_id;
END

我运行过的一些测试... 它适用于整数的 id... 它适用于作为字符串的整数的 id... 如果我使用字符串,它会说明有关字段的信息 - 但我只是希望它识别它不是 int?我尝试使用作为字段的字符串,但得到了相同的错误。 我使用了一个带有简单插入注入的字符串(我将做测试其他的工作) EG

CALL user_get_by_id(4);
CALL user_get_by_id('4');
CALL user_get_by_id('4; INSERT INTO users () VALUE ();'); (I tested the second statement, and ensured it worked as a regular query, but it did not insert when used this way)

我会继续测试,但也许这里有人知道我是否达到了 99%。谢谢你的帮助!

我应该提到,有一个 PHP 脚本将调用该过程并使用用户输入。

它将类似于...

$query = "CALL user_get_by_id(".mysql_escape_string($+POST[id]).")";

此问题与提议的类似问题不同的原因:

  1. 提供的响应是制作 SQL 注入安全的 PHP 代码的不同方法,但这个问题是关于使用存储过程的另一种方法,并且该问题中没有提供。此外,该问题的答案并不符合我的喜好。否则,当我第一次、第二次或第三次遇到它时,我就会使用该线程。
  2. 这个问题不是一般的如何提问——这个问题是有针对性的,专门寻求建议。我认为通过将特定问题标记为与一般问题相同来结束它是不合理的(例如,如果有人对为什么他们的数学方程不起作用感到困惑,那么受访者指出原因而不是标记问题是有意义的关闭并引导他们参加免费的在线数学课程)
php sql mysql sql-injection
2个回答
1
投票

这实际上可以防止 SQL 注入:

PROCEDURE user_get_by_id (IN user_id INT)
BEGIN
    SELECT id, email, name, password_hash FROM users WHERE id=user_id;
END

通过将输入参数声明为类型

INT
,您尝试作为参数传递的任何字符串都将被强制转换为数值。

在 MySQL 中,像“123abc”这样的字符串的数值是 123,它会忽略后面的所有非数字字符。没有前导数字的字符串(例如“abc123”)的数值为 0。

所以即使你这样做也是安全的:

CALL user_get_by_id('4; INSERT INTO users () VALUE ();'); 

过程的输入值只是

INT
值 4。当参数被转换为
INT
时,参数中的所有其他字符都将被消除。

因此使用该过程至少可以有效地防止 SQL 注入。

但是,这不是推荐的解决方案,因为有不需要存储过程的更简单的方法。

在 PHP 中,您可以非常轻松地将 POST 参数转换为整数值,并将其作为值传递给过程:

$query = "CALL user_get_by_id({intval($_POST['id'])})"; 

或者甚至直接在查询字符串中使用它:

$query = "SELECT id, email, name, password_hash FROM users WHERE id={intval($_POST['id'])}";

这是针对整数的 SQL 注入的有效保护,但它不适用于字符串或日期输入。

以下内容是不安全的,因为没有类型转换,因此仍然存在特殊字符可能被复制到 SQL 查询中并导致意外逻辑的风险:

$query = "SELECT id, email, name, password_hash FROM users WHERE name='{$_POST['id'])}'";
您可以尝试转义特殊字符,但这会让人感到困惑。

推荐的解决方案是使用

查询参数,因为它始终可靠、更简单且更安全。查询参数并不是简单地插值到查询字符串中。它们将动态值与查询分开,直到解析后为止,因此输入值不可能破坏查询的预期逻辑。

$query = "SELECT id, email, name, password_hash FROM users WHERE id=?"; $stmt = $pdo->prepare($query); $stmt->execute( [ $_POST['id'] ] );
查询参数对于数字输入和字符串输入同样有效。

使用查询参数,编写代码、阅读代码、调试代码更加容易!


1
投票
要进行 SQL 注入,您需要 2 种编程语言(或一种能够自我评估的语言,例如使用 eval)。您展示的存储过程是纯 SQL,因此无法注入任何内容。

但是,如果您从 PHP 调用此过程,则可能会出现 SQL 注入。这样的代码很容易受到攻击:

$pdo->query('CALL sp('.$_POST['data'].')');
当您从 PHP 执行任何 SQL 时,必须使用准备好的语句。如果您记得永远不要在 SQL 中放置任何 PHP 变量,那么您应该可以避免 SQL 注入。

在 PHP 中,mysqli 和 PDO 两个扩展都提供准备好的语句。确保你这样称呼它:

$stmt = $pdo->prepare('CALL sp(?)'); $stmt->execute([$_POST['data']]); // or $mysqli->execute_query('CALL sp(?)', [$_POST['data']]);
现在可以安全地避免 SQL 注入,因为 SQL 字符串是常量。

不相关,但我强烈反对在 PHP 代码中使用存储过程。它们非常难以使用并且令人讨厌。特别是对于您所展示的简单 SQL,存储过程是不合适的。除非绝对必要,否则不要使用存储过程。

© www.soinside.com 2019 - 2024. All rights reserved.