如何使用PHP清理用户输入?
是否有一个catchall函数可以很好地清除SQL注入和XSS攻击的用户输入,同时仍然允许某些类型的HTML标记?否。如果没有任何数据的上下文,您无法对数据进行常规筛选。有时您希望将SQL查询作为输入,有时您希望将HTML作为输入 您需要过滤白名单上的输入——确保数据符合您期望的某些规范。然后,您需要在使用它之前转义它,这取决于您使用它的上下文如何使用PHP清理用户输入?,php,security,xss,sql-injection,user-input,Php,Security,Xss,Sql Injection,User Input,是否有一个catchall函数可以很好地清除SQL注入和XSS攻击的用户输入,同时仍然允许某些类型的HTML标记?否。如果没有任何数据的上下文,您无法对数据进行常规筛选。有时您希望将SQL查询作为输入,有时您希望将HTML作为输入 您需要过滤白名单上的输入——确保数据符合您期望的某些规范。然后,您需要在使用它之前转义它,这取决于您使用它的上下文 为SQL转义数据的过程(防止SQL注入)与为(X)HTML转义数据的过程(防止XSS)非常不同。有过滤器扩展(,),它可以很好地处理所有GPC变量。虽然
为SQL转义数据的过程(防止SQL注入)与为(X)HTML转义数据的过程(防止XSS)非常不同。有过滤器扩展(,),它可以很好地处理所有GPC变量。虽然这不是一个神奇的“一劳永逸”的东西,但您仍然需要使用它。要解决XSS问题,请查看。它是相当可配置的,并且有良好的业绩记录 至于SQL注入攻击,请确保检查用户输入,然后通过mysql\u real\u escape\u string()运行它。不过,该函数无法抵抗所有注入攻击,因此在将数据转储到查询字符串中之前检查数据非常重要 更好的解决方案是使用事先准备好的语句。和mysqli扩展支持这些功能。不,没有 首先,SQL注入是一个输入过滤问题,XSS是一个输出转义问题——因此您甚至不会在代码生命周期中同时执行这两个操作 基本经验法则
- 对于SQL查询,绑定参数(与PDO一样)或对查询变量(例如)使用驱动程序本机转义函数
- 用于过滤掉不需要的HTML
- 使用转义所有其他输出,并注意此处的第2和第3个参数
- 用户输入可以过滤是一种常见的误解。PHP甚至有一个(现在已弃用的)“特性”,名为,它建立在这个想法之上。这是胡说八道。忘记过滤(或清洁,或其他人们称之为过滤的东西)
为了避免出现问题,您应该做的很简单:每当您在外部代码中嵌入一段数据时,您必须根据该代码的格式规则来处理它。但您必须了解,这些规则可能太复杂,无法手动执行。例如,在SQL中,字符串、数字和标识符的规则都是不同的。为了您的方便,在大多数情况下,有一个专用的工具用于这种嵌入。例如,当您需要在SQL查询中使用PHP变量时,您必须使用一个准备好的语句,该语句将负责所有正确的格式/处理
另一个例子是HTML:如果在HTML标记中嵌入字符串,则必须使用转义。这意味着每个
echo
或print
语句都应该使用htmlspecialchars
第三个例子可能是shell命令:如果要将字符串(如参数)嵌入到外部命令中,并用调用它们,则必须使用and
另外,一个非常引人注目的例子是JSON。规则是如此之多和复杂,你永远无法手动遵守它们。这就是为什么您永远不应该手动创建JSON字符串,而应该始终使用一个专用函数,该函数将正确格式化每一位数据
诸如此类
您需要主动筛选数据的唯一情况是,您正在接受预格式化的输入。例如,如果您允许用户发布您计划在网站上显示的HTML标记。但是,您应该明智地不惜一切代价避免这种情况,因为无论您如何过滤它,它始终是一个潜在的安全漏洞。PHP现在有了新的nice函数,例如,现在有了一个内置的过滤器\u验证\u电子邮件
类型,您就可以从查找“终极电子邮件正则表达式”中解放出来
我自己的过滤器类(使用JavaScript突出显示错误字段)可以通过ajax请求或普通表单post启动。(参见下面的示例)
不要试图通过清理输入数据来阻止SQL注入 相反,不允许在创建SQL代码时使用数据。使用使用绑定变量的准备语句(即在模板查询中使用参数)。这是防止SQL注入的唯一方法
有关防止SQL注入的更多信息,请访问我的网站。在特定情况下,有一个技巧可以帮助您使用类似于
/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) 应始终假定用户输入是错误的
使用预先准备好的语句,或/或使用mysql\u real\u escape\u字符串进行过滤绝对是必须的。
PHP还内置了filter_输入,这是一个很好的起点
2) 这是一个很大的主题,它取决于输出数据的上下文。对于HTML,有一些解决方案,比如htmlpurifier。
根据经验法则,输出的任何内容都要转义
这两个问题都太大,无法在一篇文章中讨论,但有许多文章更详细:
PHP5.2引入了该函数
它支持大量的SANITIZE
,VALIDATE
过滤器。只是想在输出转义的主题上添加这些,如果使用php DOMDocument生成html输出,它将在正确的上下文中自动转义。属性(值=“”)和的内部文本不相等。
成为
if (isset($_GET['id'])) {
$id = $_GET['id'];
settype($id, 'integer');
$result = mysql_query("SELECT * FROM mytable WHERE id = '$id'");
# now use the result
}
$mysqli->set_charset("utf8");
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);
$pdo->exec("set names utf8");
$pdo = new PDO(
"mysql:host=$host;dbname=$db", $user, $pass,
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
)
);
mysql_set_charset('utf8')
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "' OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);explicit set the character set
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);disable emulating prepared statements to prevent fallback to emulating statements that MySQL can't prepare natively (to prevent injection)
$var = $pdo->quote("' OR 1=1 /*");not only escapes the literal, but also quotes it (in single-quote ' characters)
$stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);explicit set the character set
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);disable emulating prepared statements to prevent fallback to emulating statements that MySQL can't prepare natively (to prevent injection)
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(["' OR 1=1 /*"]);
ctype_digit — Check for numeric character(s);
$value = (int) $value;
$value = intval($value);
$var = filter_var('0755', FILTER_VALIDATE_INT, $options);
is_string() — Find whether the type of a variable is string
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
$newstr = filter_var($str, FILTER_SANITIZE_STRING);
$search_html = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
function clean_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
& (ampersand) becomes &
" (double quote) becomes "
' (single quote) becomes '
< (less than) becomes <
> (greater than) becomes >