用PHP清理用户input的最好方法是什么?

是否有一个catchall函数的地方,很好的消毒用户input的SQL注入和XSS攻击,同时仍然允许某些types的html标签?

用户input可以被过滤是一个常见的误解。 PHP甚至有一个(现在被弃用的)“function”,称为魔术引号,build立在这个想法。 这是无稽之谈。 忘记过滤(或清洁,或任何人称之为)。

你应该做什么,以避免问题是非常简单的:每当你在外国代码中embedded一个string,你必须根据该语言的规则逃避它。 例如,如果在某些针对MySql的SQL中embeddedstring,则必须使用MySql的函数( mysqli_real_escape_string )将string转义。

另一个例子是HTML:如果你在HTML标记中embeddedstring,你必须用htmlspecialchars来转义它。 这意味着每个echoprint语句都应该使用htmlspecialchars

第三个例子可以是shell命令:如果要将string(如参数)embedded到外部命令中,并使用exec调用它们,则必须使用escapeshellcmdescapeshellarg

等等等等 …

唯一需要主动过滤数据的情况是,如果您接受预先格式化的input。 例如。 如果你让你的用户张贴HTML标记,你打算在网站上显示。 然而,你应该明智地避免这个代价,因为无论你如何过滤它,它总会是一个潜在的安全漏洞。

不要试图通过清理input数据来防止SQL注入。

相反, 不要让数据用于创build您的SQL代码 。 使用准备好的语句(即使用模板查询中的参数)使用绑定variables。 这是保证SQL注入的唯一方法。

有关防止SQL注入的更多信息,请参阅我的网站http://bobby-tables.com/

不可以,一般不过滤数据,没有任何背景。 有时候你会想把一个SQL查询作为input,有时候你会想把HTML作为input。

您需要过滤白名单上的input – 确保数据符合您所期望的一些规范。 那么在使用之前,您需要先将其转义出来,具体取决于您使用的上下文。

SQL转义数据的过程 – 防止SQL注入 – 与转换(X)HTML数据的过程非常不同,以防止XSS。

现在PHP有了新的很好的filter_input函数,例如解放你find'最终的电子邮件正则expression式'现在有一个内置的FILTER_VALIDATE_EMAILtypes

我自己的filter类(使用JavaScript突出错误的领域)可以由ajax请求或正常forms发布启动。 (见下面的例子)

 /** * Pork.FormValidator * Validates arrays or properties by setting up simple arrays. * Note that some of the regexes are for dutch input! * Example: * * $validations = array('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date'); * $required = array('name', 'email', 'alias', 'pwd'); * $sanatize = array('alias'); * * $validator = new FormValidator($validations, $required, $sanatize); * * if($validator->validate($_POST)) * { * $_POST = $validator->sanatize($_POST); * // now do your saving, $_POST has been sanatized. * die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>"); * } * else * { * die($validator->getScript()); * } * * To validate just one element: * $validated = new FormValidator()->validate('blah@bla.', 'email'); * * To sanatize just one element: * $sanatized = new FormValidator()->sanatize('<b>blah</b>', 'string'); * * @package pork * @author SchizoDuckie * @copyright SchizoDuckie 2008 * @version 1.0 * @access public */ class FormValidator { public static $regexes = Array( 'date' => "^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}\$", 'amount' => "^[-]?[0-9]+\$", 'number' => "^[-]?[0-9,]+\$", 'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$", 'not_empty' => "[a-z0-9A-Z]+", 'words' => "^[A-Za-z]+[A-Za-z \\s]*\$", 'phone' => "^[0-9]{10,11}\$", 'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$", 'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$", 'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$", '2digitopt' => "^\d+(\,\d{2})?\$", '2digitforce' => "^\d+\,\d\d\$", 'anything' => "^[\d\D]{1,}\$" ); private $validations, $sanatations, $mandatories, $errors, $corrects, $fields; public function __construct($validations=array(), $mandatories = array(), $sanatations = array()) { $this->validations = $validations; $this->sanatations = $sanatations; $this->mandatories = $mandatories; $this->errors = array(); $this->corrects = array(); } /** * Validates an array of items (if needed) and returns true or false * */ public function validate($items) { $this->fields = $items; $havefailures = false; foreach($items as $key=>$val) { if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false) { $this->corrects[] = $key; continue; } $result = self::validateItem($val, $this->validations[$key]); if($result === false) { $havefailures = true; $this->addError($key, $this->validations[$key]); } else { $this->corrects[] = $key; } } return(!$havefailures); } /** * * Adds unvalidated class to thos elements that are not validated. Removes them from classes that are. */ public function getScript() { if(!empty($this->errors)) { $errors = array(); foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; } $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; $output .= "new FormValidator().showMessage();"; } if(!empty($this->corrects)) { $corrects = array(); foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; } $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");'; } $output = "<script type='text/javascript'>{$output} </script>"; return($output); } /** * * Sanatizes an array of items according to the $this->sanatations * sanatations will be standard of type string, but can also be specified. * For ease of use, this syntax is accepted: * $sanatations = array('fieldname', 'otherfieldname'=>'float'); */ public function sanatize($items) { foreach($items as $key=>$val) { if(array_search($key, $this->sanatations) === false && !array_key_exists($key, $this->sanatations)) continue; $items[$key] = self::sanatizeItem($val, $this->validations[$key]); } return($items); } /** * * Adds an error to the errors array. */ private function addError($field, $type='string') { $this->errors[$field] = $type; } /** * * Sanatize a single var according to $type. * Allows for static calling to allow simple sanatization */ public static function sanatizeItem($var, $type) { $flags = NULL; switch($type) { case 'url': $filter = FILTER_SANITIZE_URL; break; case 'int': $filter = FILTER_SANITIZE_NUMBER_INT; break; case 'float': $filter = FILTER_SANITIZE_NUMBER_FLOAT; $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND; break; case 'email': $var = substr($var, 0, 254); $filter = FILTER_SANITIZE_EMAIL; break; case 'string': default: $filter = FILTER_SANITIZE_STRING; $flags = FILTER_FLAG_NO_ENCODE_QUOTES; break; } $output = filter_var($var, $filter, $flags); return($output); } /** * * Validates a single var according to $type. * Allows for static calling to allow simple validation. * */ public static function validateItem($var, $type) { if(array_key_exists($type, self::$regexes)) { $returnval = filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false; return($returnval); } $filter = false; switch($type) { case 'email': $var = substr($var, 0, 254); $filter = FILTER_VALIDATE_EMAIL; break; case 'int': $filter = FILTER_VALIDATE_INT; break; case 'boolean': $filter = FILTER_VALIDATE_BOOLEAN; break; case 'ip': $filter = FILTER_VALIDATE_IP; break; case 'url': $filter = FILTER_VALIDATE_URL; break; } return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false; } } 

当然,请记住,你需要做你的sql查询也取决于你正在使用什么types的数据库(mysql_real_escape_string()是无用的sql服务器)。 您可能想要像ORM一样在相应的应用程序层自动处理它。 另外,如上所述:输出到html使用其他PHP专用function,如htmlspecialchars;)

为了真正允许HTMLinput与类似的剥离类和/或标签依赖于专用的xssvalidation包之一。 不要写你自己的REGEXES PARSE HTML!

不,那里没有。

首先,SQL注入是一个input过滤问题,而XSS是一个输出转义 – 所以你甚至不会在代码生命周期中同时执行这两个操作。

基本的经验法则

  • 对于SQL查询,绑定参数(如使用PDO)或使用驱动程序本机转义函数查询variables(如mysql_real_escape_string()
  • 使用strip_tags()过滤掉不需要的HTML
  • 使用htmlspecialchars()转义所有其他输出,并在此注意第二个和第三个参数。

要解决XSS问题,请查看HTML Purifier 。 这是相当可configuration的,并有一个体面的logging。

至于SQL注入攻击,请确保检查用户input,然后通过mysql_real_escape_string()运行它。 但是这个函数并不能击败所有的注入攻击,所以在将数据转储到查询string之前检查数据是非常重要的。

更好的解决scheme是使用准备好的语句。 PDO库和mysqli扩展支持这些。

PHP 5.2引入了filter_var函数。

它支持大量的SANITIZE,VALIDATEfilter。

http://php.net/manual/en/function.filter-var.php

一个技巧,可以帮助在你有一个像/mypage?id=53的页面的特定情况下,你在WHERE子句中使用id是确保id肯定是一个整数,如下所示:

 if (isset($_GET['id'])) { $id = $_GET['id']; settype($id, 'integer'); $result = mysql_query("SELECT * FROM mytable WHERE id = '$id'"); # now use the result } 

但是,当然只是删除一个特定的攻击,所以阅读所有其他的答案。 (是的,我知道上面的代码不是很好,但它显示了具体的防守。)

你在这里描述的是两个不同的问题:

  1. 消毒/过滤用户input数据。
  2. 转义输出。

1)用户input应始终被认为是不好的。

使用准备好的语句,或/和用mysql_real_escape_string过滤绝对是必须的。 PHP也有内置的filter_input,这是一个很好的开始。

2)这是一个很大的话题,它取决于正在输出的数据的上下文。 对于HTML,有一些解决scheme,比如htmlpurifier。 作为一个经验法则,总是逃避你输出的任何东西。

这两个问题太大,不能在一个单一的post,但有很多的post,更详细的:

方法PHP输出

更安全的PHP输出

如果你使用的是PostgreSQL,PHP的input可以用pg_escape_string()

$ username = pg_escape_string($ _ POST ['username']);

从文档( http://php.net/manual/es/function.pg-escape-string.php ):

pg_escape_string()转义一个查询数据库的string。 它以PostgreSQL格式返回一个没有引号的转义string。

只是想在输出转义的主题上添加一下,如果使用php DOMDocument来创buildhtml输出,它会自动在正确的上下文中进行转义。 一个属性(value =“”)和一个<span>的内部文本是不相等的。 为了避免XSS,请阅读: OWASP XSS预防备忘单

避免input和转义数据过滤错误的最简单的方法是使用像Symphony , Nette等PHP框架或框架的一部分(模板引擎,数据库层,ORM)。

像Twig或Latte这样的模板引擎默认情况下会输出转义 – 如果根据上下文(网页的HTML或Javascript部分)正确地转义了输出,则不必手动解决。

框架自动清理input,你不应该直接使用$ _POST,$ _GET或$ _SESSIONvariables,而是通过路由,会话处理等机制。

而对于数据库(模型)层,还有像Doctrine这样的ORM框架,或者像Nette数据库一样的PDO包装。

您可以在这里阅读更多关于它的信息 – 什么是软件框架?

有一个filter扩展( howto-link , manual ),对于所有的GPCvariables来说,它都能很好地工作。 这不是一个神奇的事情,但你仍然必须使用它。

使用PHP清理用户input的最佳BASIC方法:

 function sanitizeString($var) { $var = stripslashes($var); $var = strip_tags($var); $var = htmlentities($var); return $var; } function sanitizeMySQL($connection, $var) { $var = $connection->real_escape_string($var); $var = sanitizeString($var); return $var; }