简体   繁体   English

如何防止用php和mysql进行sql注入

[英]How do I prevent sql injection with php and mysql

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. 我有一个访客可以输入数据的表格,我想通过$ _POST变量将此数据存储在mysql数据库中。 What do I need to prevent sql injection? 我需要什么来防止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. 我在2010年5月的PHP TEK-X会议上作了有关此主题的演讲,并且尝试涵盖多种防止SQL注入的方法。 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. 在将用户输入或来自外部源的任何其他内容(甚至来自您自己数据库中的数据)进行验证之前,请先将其插入SQL查询中。 You can use PHP's filter extension or regular expressions, for instance. 例如,您可以使用PHP的过滤器扩展或正则表达式。

  • 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. 例如, (int) $_POST["userid"]将内容强制转换为纯整数,因此可以安全使用。

  • When including dynamic content in place of literal values in SQL expressions, use prepared queries with parameters. 当在SQL表达式中包括动态内容而不是文字值时,请使用带参数的预准备查询。 Note that the plain mysql extension in PHP does not support query parameters -- use PDO . 请注意,PHP中的普通mysql扩展不支持查询参数-使用PDO I don't use mysqli because its API is inconsistent and hard to use. 我不使用mysqli因为它的API不一致且mysqli

  • When using the IN() predicate, you can't use one parameter for a list of values. 使用IN()谓词时,不能将一个参数用于值列表。 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. 使用动态表名,列名或SQL关键字时,不能使用查询参数。 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. 有关更多信息和示例,请参见我的演讲“ SQL注入神话和谬论 ”。

Also you might like my new book SQL Antipatterns: Avoiding the Pitfalls of Database Programming . 您也可能喜欢我的新书《 SQL Antipatterns:避免数据库编程的陷阱》 My book has a chapter about SQL Injection. 我的书有一章关于SQL注入。

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: 要将查询发送到数据库,您有2个选项:

  1. Build a query usual way, to make it look exactly as SQL query you can run in sql console. 以通常的方式构建查询,以使其看起来完全像可以在sql控制台中运行的SQL查询一样。
    To do it, one should understand a whole set of rules , not just "use mysql_real_escape_string". 为此,应该理解一整套规则 ,而不仅仅是“使用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! 这是转义的唯一含义:这只是easacpe分隔符! (and some other characters - string termination char and escape character itself). (以及其他一些字符-字符串终止字符和转义字符本身)。 Without surrounding quotes mysql_real_escape_string is just useless. 如果没有引号,mysql_real_escape_string就是没用的。
    • 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. 尽管虽然数据数字可能像字符串一样受到威胁,但仍有一些数字(例如LIMIT子句参数)无法转义,只能进行强制转换。
  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. 可以绑定所有字符串,数字和LIMIT参数-完全不用担心。
    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. 等等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM