调用存储过程从PHP代码中能够防止SQL注入吗?

huangapple go评论71阅读模式
英文:

Does calling a stored procedure from PHP code prevent SQL injection?

问题

I am very confused about stored procedures, and how to use them to prevent SQL injection. Everything I am reading online seems contradictory and each place gives one example which doesn't seem to work.

Is this, or is this not, secure from SQL injection?

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

Some tests I have run...
It works with ids that are ints...
It works with ids that are ints as strings...
It says something about fields if I use a string - but I just want it to recognize it is not an int? I tried using a string that is a field, and got the same error.
I used a string with a simple insertion injection (I will do work to test others)
E.G.

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

I will keep testing from my end, but maybe somebody on here knows if I am 99% there or not. thank you for your help!

I should mention that there is a PHP script which will call the procedure and will use user input.

It will be something similar to...

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

Reason this question is different than the proposed similar question:

  1. the offered response is different ways of crafting PHP code that is SQL injection safe, but this question is about another method using stored procedures and that is NOT provided in that question. Additionally, the answers in that question were not to my liking. Otherwise I would have used that thread when I came upon it the first, second, or third time.
  2. this question is not a general how-to question - this question is targeted and specifically asks for advice. I think it is unreasonable to close a specific question by marking it the same as a general question (E.G. If someone is confused about why their math equation isn't working, it makes sense for the respondent to point out why, not mark the question as closed and direct them to a free online math course).
英文:

I am very confused about stored procedures, and how to use them to prevent SQL injection. Everything I am reading online seems contradictory and each place gives one example which doesn't seem to work.

Is this, or is this not, secure from SQL injection?

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

Some tests I have run...
It works with ids that are ints...
It works with ids that are ints as strings...
It says something about fields if I use a string - but I just want it to recognize it is not an int? I tried using a string that is a field, and got the same error.
I used a string with a simple insertion injection (I will do work to test others)
E.G.

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)

I will keep testing from my end, but maybe somebody on here knows if I am 99% there or not. thank you for your help!

I should mention that there is a PHP script which will call the procedure and will use user input.

It will be something similar to...

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

Reason this question is different than the proposed similar question:

  1. the offered response is different ways of crafting PHP code that is SQL injection safe, but this question is about another method using stored procedures and that is NOT provided in that question. Additionally, the answers in that question were not to my liking. Otherwise I would have used that thread when i came upon it the first, second, or third time.
  2. this question is not a general how to question - this question is targetted, and specifically asks for advice. I think it is unreasonable to close a specific question by marking it the same as a general question (E.G. If someone is confused about why their math equation isn't working, it makes sense for the respondent to point out why, not mark the question as closed and direct them to a free online math course)

答案1

得分: 1

以下是翻译好的部分:

这实际上是安全的,不受SQL注入攻击的影响:

通过将输入参数声明为INT类型,您尝试传递的任何字符串参数都将被强制转换为数字值。

在MySQL中,类似于'123abc'的字符串的数值为123,它会忽略任何后续的非数字字符。例如,没有前导数字的字符串,比如'abc123',其数值为0。

因此,即使您执行以下操作,也是安全的:

调用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'] ] );

查询参数对于数字输入和字符串输入同样有效。

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

英文:

This is in fact secure from SQL injection:

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

By declaring the input argument as type INT, any string you try to pass as the argument will be coerced to a numeric value.

In MySQL, the numeric value of a string like '123abc' is 123, and it ignores any following non-numeric characters. A string that has no leading digits, for example 'abc123', has a numeric value of 0.

So it's safe even if you do this:

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

The value input to the procedure is simply the INT value 4. All the other characters in the argument are eliminated as the argument is cast as an INT.

So using the procedure is at least effective at protecting against SQL injection.

However, it isn't the recommended solution, because there are easier methods that don't require a stored procedure.

In PHP, you could cast the POST parameter to an integer value very easily, and either pass this as the value to a procedure:

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

Or even use it directly in a query string:

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

This is an effective protection from SQL injection for integers, but it does NOT work for string or date inputs.

The following is unsafe, because there is no type casting, so there's still a risk that special characters could be copied into the SQL query and cause unintended logic:

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

You could try to escape the special characters, but that gets confusing.

The solution that is recommended because it is always reliable, simpler, and safer is to use query parameters. Query parameters are not simply interpolation into a query string. They keep the dynamic value separate from the query until after it has been parsed, so there is no way the input value can corrupt the intended logic of the query.

$query = "SELECT id, email, name, password_hash FROM users WHERE id=?";
$stmt = $pdo->prepare($query);
$stmt->execute( [ $_POST['id'] ] );

Query parameters work equally well for both numeric inputs and string inputs.

It's even easier to write the code, read the code, and debug the code when you use query parameters!

答案2

得分: 1

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

但是,如果您从PHP调用此存储过程,就有可能发生SQL注入。像这样的代码存在漏洞:

$pdo->query('CALL sp('.$_POST['data'].')');

在PHP中执行任何SQL时,必须使用预处理语句。如果记住永远不要将任何PHP变量放入SQL中,那么您就可以免受SQL注入的威胁。

在PHP中,mysqli和PDO两个扩展都提供了预处理语句。确保按照以下方式调用它:

$stmt = $pdo->prepare('CALL sp(?)');
$stmt->execute([$_POST['data']]);
// 或者
$mysqli->execute_query('CALL sp(?)', [$_POST['data']]);

这样做是安全的,因为SQL字符串是恒定的。

与此无关,但我强烈不建议在PHP代码中使用存储过程。它们非常难以使用并且令人讨厌。尤其对于像您展示的简单SQL,存储过程并不合适。除非绝对必要,否则不要使用存储过程。

英文:

To have an SQL injection you need 2 programming languages (or a single one that evaluates itself, e.g. with eval). The stored procedure you showed is pure SQL, so there is no way to inject anything.

However, if you call this procedure from PHP then it is possible to have SQL injection. Code like this is vulnerable:

$pdo->query('CALL sp('.$_POST['data'].')');

When you execute any SQL from PHP, you must use prepared statements. If you remember to never put any PHP variable within SQL then you should be safe from SQL injection.

In PHP, both extensions, mysqli and PDO, offer prepared statements. Make sure you call it like this:

$stmt = $pdo->prepare('CALL sp(?)');
$stmt->execute([$_POST['data']]);
// or
$mysqli->execute_query('CALL sp(?)', [$_POST['data']]);

This is now safe from SQL injection because the SQL string is constant.

Unrelated, but I highly discourage using stored procedures in PHP code. They are very difficult to use and obnoxious. Especially for a simple SQL as you have shown, stored procedure is unsuitable. Don't use stored procedures unless you absolutely must.

huangapple
  • 本文由 发表于 2023年6月8日 19:17:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76431284.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定