在PHP中清理查询字符串

12

我有一个带查询字符串的网页。

在PHP中,我有以下代码:

$querystring=$_SERVER["QUERY_STRING"];
echo "<html><head></head><body>
<a href='index.php?$querystring'>test</a>
</body></html>";

我需要对查询字符串进行清理吗?
如果需要,如何进行清理?如果不进行清理可能会有哪些攻击方式?

5个回答

21
更新:自 PHP 8.1 起,FILTER_SANITIZE_STRING 已被弃用,请参阅官方 PHP 文档:https://www.php.net/manual/en/filter.filters.sanitize.php
如果您正在运行PHP >= 5.2.0,可以使用filter_input或filter_input_array。
假设您的URL和查询字符串是这样的:http://example.com/?liquor=gin&mixer=tonic&garnish=lime。
要进行过滤,您可以按照以下步骤操作。
/*
 FILTER_SANITIZE_STRING removes most dangerous characters. That may 
 not always be what you want. Read the PHP filters docs. 
 
 We are also overwriting the $_GET array (the query string) with the sanitized
 versions of these variables.
*/

$_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);

/* 
rebuild query string using white listed variables, 
not $_GET to prevent variable injection as Mārtiņš Briedis 
suggests above.
*/

$qv['liquor']  = $_GET['liquor'];
$qv['mixer']   = $_GET['mixer'];
$qv['garnish'] = $_GET['garnish'];

# build and URL encode the query string using the above array.
$querystring = http_build_query( $qv );

如果你创建一个函数,并传递一个允许的键的数组进行迭代,可能会更加“优雅”(我认为这样做也使得代码更易读)。 - Sybille Peters
另外,你可能想要做一些像 $_GET['liquor'] ?? '' 这样的操作,以防参数未填写。 - Sybille Peters
FILTER_SANITIZE_STRING自PHP 8.1起已被弃用。请参考https://www.php.net/manual/en/filter.filters.sanitize.php。 - Sybille Peters

10

为了防止任何 XSS 攻击,您应该使用 htmlspecialchars($query, ENT_QUOTES)

echo "<html><head></head><body>
<a href='index.php?".htmlspecialchars($querystring, ENT_QUOTES)."'>test</a>
</body></html>"

但是,你仍然应该白名单任何参数,因为聪明的攻击者可能会伪造查询并尝试跨站请求伪造攻击。


1
假设您正在 PHP 5.x 中将查询参数作为变量访问,但容易受到 XSS 攻击。
易受 XSS 攻击。
<?php
// http://example.com/mypage.php?a=hi&b=wow&c=<script type='text/javascript'>alert('XSS Attacked!');</script>

try{
    $q = $_SERVER['QUERY_STRING'];
    parse_str( $q, $arr );
    extract($arr);
    echo '<pre>';
    echo 'a is = ' . $a;
    echo PHP_EOL;
    echo 'b is = ' . $b;
    echo PHP_EOL;
    echo 'c is = ' . $c;
    echo '</pre>';

}
catch(Exception $e){
    error_log($e->getMessage());
}


?>

预防$_SERVER['QUERY_STRING']中的XSS攻击

为了预防来自$_SERVER['QUERY_STRING']的XSS攻击,

  • 使用htmlentities读取$_SERVER['QUERY_STRING']并解码查询字符串使用html_entity_decode
  • 使用parse_str提取查询参数的键值数组。
  • 使用filter_var_array过滤和清理数组,将要清理的数组作为第一个参数,并使用FILTER_SANITIZE_ENCODED作为第二个参数。
  • 使用extract使键成为相应值的php变量。
<?php
// http://example.com/mypage.php?a=hi&b=wow&c=<script type='text/javascript'>alert('XSS Attacked!');</script>
try{
    $q = htmlentities($_SERVER['QUERY_STRING']);
    parse_str( html_entity_decode($q), $arr );
    $arr=filter_var_array($arr, FILTER_SANITIZE_ENCODED);
    extract($arr);
    echo '<pre>';
    echo 'a is = ' . $a;
    echo PHP_EOL;
    echo 'b is = ' .  $b;
    echo PHP_EOL;
    echo 'c is = ' .  $c;
    echo '</pre>';

}
catch(Exception $e){
    error_log($e->getMessage());
}

?>

0
在这种情况下,您应该使用urlencode函数。
当您要输出查询参数的值作为链接标题时,htmlspecialchars / htmlentities更合适,但不适用于href / src属性。

-3

您可以使用多种方式对查询进行清理,但那不是做这件事的地方。即使您通过GET发送了一个安全的查询,某人仍然可以在地址栏上或使用篡改数据更改查询。您必须在index.php(或任何其他处理数据的地方)上进行清理。如果您正在使用MySQL,则必须以以下方式进行清理:

$field = mysql_real_scape($_GET['field']);

1
为什么人们总是推荐mysql_real_escape()来防止XSS/CSRF注入1!!!?! SQL注入和XSS/CSRF是完全不同的安全问题。 - John Cartwright
对于John Cartwright所说的内容,我表示赞同。Steve,你的建议可以防止SQL注入。但在这种情况下,我们想要过滤输出——即添加到我们的HTML中——以防止XSS攻击。 - webinista

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接