如何防止php和mysql的sql注入

发布于 2024-09-05 14:44:06 字数 71 浏览 6 评论 0原文

我有一个访问者可以输入数据的表单,我想通过 $_POST 变量将此数据存储在 mysql 数据库中。我需要什么来防止sql注入?

I have a form into which the visitor can enter data, and I want to store this data in a mysql database via the $_POST variable. What do I need to prevent sql injection?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

东北女汉子 2024-09-12 14:44:06

我在 2010 年 5 月的 PHP TEK-X 会议上就这个主题做了一次演讲,并尝试介绍多种防御 SQL 注入的方法。没有一种方法在所有情况下都是最佳的,因此您应该学习多种方法并使用所有这些方法:

  • 在将其插入之前验证用户输入或来自外部源的任何其他内容(甚至来自您自己的数据库中的数据) SQL 查询。例如,您可以使用 PHP 的 过滤器 扩展或正则表达式。

  • 强制外部内容采用正确的格式。例如,(int) $_POST["userid"] 将该内容类型转换为普通整数,因此可以安全使用。

  • 当在 SQL 表达式中包含动态内容来代替文字值时,请使用带参数的准备好的查询。请注意,PHP 中的普通 mysql 扩展不支持查询参数 - 使用 PDO 。我不使用 mysqli 因为它的 API 不一致且难以使用。

  • 使用 IN() 谓词时,不能将一个参数用于一组值。连接多个参数占位符,数量与列表中的值相同。这并不难,只需要一两行代码:

     $sql = "SELECT ... FROM ... WHERE user_id IN ("
         。 join(",", array_fill(0,count($userid_list),"?")) 。 “)”;
     $pdoStmt = $pdo->准备($sql);
     $pdoStmt->execute($userid_list);
    
  • 当使用动态表名、列名或 SQL 关键字时,不能使用查询参数。您必须插入动态内容。但您可以使用白名单技术将不受信任的内容映射到合法、安全的标识符和关键字。

请参阅我的演示文稿SQL 注入神话和谬误了解更多信息和例子。

您也可能喜欢我的书 SQL 反模式卷 1:避免数据库编程的陷阱。我的书中有一章是关于 SQL 注入的。

I gave a presentation at the PHP TEK-X conference in May 2010 about this topic, and I tried to cover multiple methods for defending against SQL Injection. There is no single method that is best in all cases, so you should learn multiple methods and use all of them:

  • Validate user input or any other content from external sources (even data from within your own database) before interpolating it into an SQL query. You can use PHP's filter extension or regular expressions, for instance.

  • Force external content to be in correct format. For example, (int) $_POST["userid"] typecasts that content to be a plain integer, so it's safe to use.

  • When including dynamic content in place of literal values in SQL expressions, use prepared queries with parameters. Note that the plain mysql extension in PHP does not support query parameters -- use PDO. I don't use mysqli because its API is inconsistent and hard to use.

  • When using the IN() predicate, you can't use one parameter for a list of values. Concatenate multiple parameter placeholders, as many as you have values in your list. This is not hard, it only take a line or two of code:

     $sql = "SELECT ... FROM ... WHERE user_id IN ("
         . join(",", array_fill(0,count($userid_list),"?")) . ")";
     $pdoStmt = $pdo->prepare($sql);
     $pdoStmt->execute($userid_list);
    
  • When using dynamic table names, column names, or SQL keywords, you can't use query parameters. You have to interpolate dynamic content. But you can use whitelisting techniques to map the untrusted content to legal, safe identifiers and keywords.

See my presentation SQL Injection Myths and Fallacies for more information and examples.

Also you might like my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming. My book has a chapter about SQL Injection.

兲鉂ぱ嘚淚 2024-09-12 14:44:06

在向查询添加任何数据时,您必须遵循一些规则,无论数据来自何处 - 来自用户或表单或其他任何内容。规则始终保持不变。

要将查询发送到数据库,您有 2 个选项:

  1. 以常规方式构建查询,使其看起来与可以在 sql 控制台中运行的 SQL 查询完全相同。
    为此,人们应该了解一整套规则,而不仅仅是“使用 mysql_real_escape_string”。
    规则例如:

    • 字符串应该用引号括起来并转义。这是转义的唯一含义:它只是 easacpe 分隔符! (以及一些其他字符 - 字符串终止字符和转义字符本身)。如果没有引号,mysql_real_escape_string 就毫无用处。
    • 数字应显式转换为其类型。尽管数据数字可以像字符串一样受到威胁,但也有一些数字(例如 LIMIT 子句参数)无法转义,只能进行强制转换。
  2. 单独发送查询和数据。
    这是最优选的方式,因为它可以缩短为“使用绑定”。所有字符串、数字和 LIMIT 参数都可以绑定 - 完全不用担心。
    使用此方法,带有占位符的查询将按原样发送到数据库,并且绑定数据将在单独的数据包中发送,因此它不会干扰。
    这就像代码数据分离一样。您发送与数据分开的程序(查询本身)。

上面所说的一切仅涉及数据插入。
但有时我们必须使查询更加动态,添加运算符或标识符。
在这种情况下,每个动态参数都应该在我们的脚本中进行硬编码,并从该集中进行选择。
例如,要进行动态排序:

$orders  = array("name","price","qty");
$key     = array_search($_GET['sort'],$orders));
$orderby = $orders[$key];
$query   = "SELECT * FROM `table` ORDER BY $orderby";

或动态搜索:

$w     = array();
$where = '';

if (!empty($_GET['rooms']))     $w[]="rooms='".mesc($_GET['rooms'])."'";
if (!empty($_GET['space']))     $w[]="space='".mesc($_GET['space'])."'";
if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'";

if (count($w)) $where="WHERE ".implode(' AND ',$w);
$query="select * from table $where";

在本示例中,我们仅向查询添加用户输入的数据,而不是字段名称,这些数据都硬编码在脚本中。
对于绑定,算法非常相似

等等。

You have to follow some rules while adding any data to the query, no matter from where it come - from user or form or anything. The rules always remain the same.

To send a query to the database, you have 2 options:

  1. Build a query usual way, to make it look exactly as SQL query you can run in sql console.
    To do it, one should understand a whole set of rules, not just "use mysql_real_escape_string".
    Rules such as:

    • strings should be both enclosed in quotes and escaped. that's the only meaning of escaping: it's just easacpe delimiters! (and some other characters - string termination char and escape character itself). Without surrounding quotes mysql_real_escape_string is just useless.
    • numbers should be cast to it's type explicitly. Though while data numbers can be threaten just like strings, there are some numbers, like LIMIT clause parameters, which cannot be escaped and can be only cast.
  2. To send query and data separately.
    This is most preferred way as it can be shortened to just "use binding". All strings, numbers and LIMIT parameters can be bound - no worry at all.
    Using this method, your query with placeholders being sent to database as is, and bound data being sent in separate packets, so, it cannot interfere.
    It is just like code and data separation. You send your program (query itself) separated from the data.

Everything said above covers only data insertion.
But sometimes we have to make our query even more dynamic, adding operators or identifiers.
In this case every dynamic parameter should be hardcoded in our script and chosen from that set.
For example, to do dynamic ordering:

$orders  = array("name","price","qty");
$key     = array_search($_GET['sort'],$orders));
$orderby = $orders[$key];
$query   = "SELECT * FROM `table` ORDER BY $orderby";

or dynamic search:

$w     = array();
$where = '';

if (!empty($_GET['rooms']))     $w[]="rooms='".mesc($_GET['rooms'])."'";
if (!empty($_GET['space']))     $w[]="space='".mesc($_GET['space'])."'";
if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'";

if (count($w)) $where="WHERE ".implode(' AND ',$w);
$query="select * from table $where";

in this example we're adding to the query only data entered by user, not field names, which are all hardcoded in the script.
For the binding the algorithm is very similar

And so on.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文