PHP如何安全地使用$_GET

ooxxoo
  • 327

本人新手,在《PHP安全编码》中提到“不要直接使用$_GET”,同时又提到“可以尝试在php.ini中开启magic_quotes_gpc,这样对于所有由用户GET、POST、COOKIE中传入的特殊字符都会转义”,我很纠结,是否开启magic_quotes_gpc就可以直接使用$_GET?

如下面例子中的代码本人感觉很不安全,因为没有做验证,但又不知道如何改进,希望大神能帮忙,谢谢。

<?php
    $name = $_GET["name"];
    $user = $_GET["user"];
    mysql_query("SELECT $name FROM $user");
回复
阅读 15.3k
15 个回答
KAYS53N
  • 645
纯数字的参数intval强制取整
其他参数值进行过滤或者转义

    protected function zaddslashes($string, $force = 0, $strip = FALSE)
    {   
        if (!defined('MAGIC_QUOTES_GPC'))
        {   
         define('MAGIC_QUOTES_GPC', '');
        }   
        if (!MAGIC_QUOTES_GPC || $force)
        {   
         if (is_array($string)) {
             foreach ($string as $key => $val)
             {   
                 $string[$key] = $this->zaddslashes($val, $force, $strip);
             }   
         }   
         else
         {   
             $string = ($strip ? stripslashes($string) : $string);
             $string = htmlspecialchars($string);
         }   
        }   
        return $string;
     }

我一般都对$_POST $_GET都使用zaddslashes,然后整数的参数值进行取整,防止XSS和SQL注入

HTTP头信息:IP、浏览器信息、等也进行转义过滤防止HTTP头注入

magic_quotes_gpc开启后会影响图片上传等,自动将内容引号转义

注意PHP5.5包括之后已经将magic_quotes_gpc配置废弃

除此之外,最好在服务器方面也做关键词过滤,Mysql为业务使用到的库创建用户和赋权

可以把自己应用放服务器上,装个sqlmap注入下自己试试,以上几点做到一般情况下不会被搞,至少不会被getshell

乌云白帽子路过...

用户bPcDTY
  • 8k

1. 像用户名这种有格式的东西,应该取到之后马上用正则表达式验证,比如preg_match('/\A\w{6,14}\z/', $user)就验证了它是不是6~14位的数字字母下划线。不要使用系统自带的filter,那个管不了什么用。

2. 不要使用mysql开头的函数,要使用mysqli,而且最好进行参数化查询而不是addslashes,后者会有编码问题。

3. 输出html的时候,默认使用htmlspecialchars,这个函数可以指定编码。或者你可以用现成的模板引擎。

4. 比较字符串的时候,强制使用strcmp,不要使用双等号。

5. 不要使用$_REQUEST

这样可以防掉95%。至于magic_quotes_gpc,应该早就废了吧。

现在市面上这些书真的害人不浅,随便下结论或者是楼主没有仔细读。为什么直接使用 $_GET $_POST 不安全?

看了楼主的例子,应该是直接将$_GET 用在拼接SQL上,这样肯定是不安全的,所以核心信息应该是:直接将 $_GET/$_POST等用户输入的内容用于SQL拼接是不安全的,如何避免?

例如:

sqlselect * from user where username= "$_GET['username']"

如果用户提交的username 为

sql" 0 or true or username ="

那么就会变成这样:

sqlselect * from user where username = "0" or true  or username=""

那么会返回所有的用户记录。当然这里只是粗暴的举了一个例子。实际上要做XSS要熟知代码或者猜测到运行的逻辑等了解很多东西。

最简单的过滤方法是使用 mysql_real_escape_string 函数

至于 magic_quotes,是php最失败的一个改进,现已废弃,就算是低版本的PHP也一定要关闭它。

如果 $_GET
首先判断是否为空
然后对值进行判断
举个例子:
只接受数字 那就使用 php自带函数 is_numeric

一般 GET 也只是传递某个标识
总之在数据库或其他操作之前 先验证 传过来的变量 是否为自己预期的值

可以先对post或者get请求过滤,php有内置函数进行过滤,然后在使用

zuoyefeng
  • 1
新手上路,请多包涵

同意二楼的要有一个过滤,不过只是系统自带的肯定不行,最好自己再根据情况做一个过滤等之类的处理。

eechen
  • 15.1k

防止XSS攻击,最简单粗暴的做法就是用htmlspecialchars把特殊字符(&,",',<,>)转换为HTML实体(&amp;&quot;&#039;&lt;&gt;)后输出.

<?php
echo htmlspecialchars($html, ENT_QUOTES, 'UTF-8');

防止XSS攻击,最复杂的就是自己写正则过滤,不过还好有HTMLPurifier库,除了能过滤XSS代码,还能把不完整的标签补全或者去掉.Yii框架也用到了这个XSS过滤库.
http://htmlpurifier.org/download

<?php
require dirname(__FILE__).'/htmlpurifier/library/HTMLPurifier.auto.php';
$purifier = new HTMLPurifier();
echo $purifier->purify($html);

上面说的是防御XSS,下面说防御SQL注入:
PDO和MySQLi等都提供有绑定参数查询的功能,而绑定参数的目的就是防止SQL注入.
http://php.net/manual/zh/pdo.prepared-statements.php

很多更成熟的数据库都支持预处理语句的概念.
什么是预处理语句?可以把它看作是想要运行的SQL的一种编译过的模板,它可以使用变量参数进行定制.
预处理语句可以带来两大好处:
1.查询仅需解析(或预处理)一次,但可以用相同或不同的参数执行多次.
当查询准备好后,数据库将分析/编译/优化执行该查询的计划.
对于复杂的查询,此过程要花费较长的时间,如果需要以不同参数多次重复相同的查询,那么该过程将大大降低应用程序的速度.
通过使用预处理语句,可以避免重复分析/编译/优化周期.
简言之,预处理语句占用更少的资源,因而运行得更快.
2.提供给预处理语句的参数不需要用引号括起来,驱动程序会自动处理.
如果应用程序只使用预处理语句,可以确保不会发生SQL注入.
然而,如果查询的其他部分是由未转义的输入来构建的,则仍存在SQL注入的风险.

预处理语句如此有用,以至于它们唯一的特性是在驱动程序不支持的时候,PDO将模拟处理.
这样可以确保不管数据库是否具有这样的功能,都可以确保应用程序可以用相同的数据访问模式.

<?php
$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();

上面参与查询的变量$calories被绑定为整型,$colour被绑定为长度为12个字符的字符串型.
MySQLi里同样提供有像PDO bindParam一样的bind_param,这时就不需要用addslashes,mysqli_real_escape_string之类的函数了,也不需要依赖magic_quotes_gpc配置了(该配置从PHP5.4开始已经被移除).
用PDO操作MySQL时注意禁用模拟预处理,这样才会使用真正的预处理,这样才能确保程序先发送SQL模板给MySQL编译,然后再传参数过去执行,这样可以确保这些参数不被SQL注入.MySQLi扩展的预处理默认就是真正的预处理.

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

这些都可以通过MySQL的general_log日志或者WireShark观察到.
225047_UF1o_561214.png

<?php
$stmt = $mysqli->prepare('SELECT * FROM users WHERE username = ?');
$stmt->bind_param('s', $_GET['username']); //s表示用户名被绑定为字符串型,整型用i.
$stmt->execute();

对于一些要求是整型的参数,其实还可以直接用intval($calories)拿到整型值.

如果你要验证和过滤一些用户输入的数据,又不想自己写正则表达式,那就可以用filter_input/filter_var函数,比如验证邮箱,IP等:
http://php.net/manual/zh/filter.filters.php

lazyboy
  • 1.5k

建议你先看看
joomla-framework的input类库,地址在这里
或者先把joomla-framework的input类库在你的代码中使用(前期就不要考虑性能之类的问题),先搞明白对于$_GET(包括$_POST、$_FILES、$_COOKIE等)要进行怎样的处理,然后在以后的使用过程中自己选择是否实现简单版本。

大鱼11211
  • 1
新手上路,请多包涵

为什么这么普遍的问题,php不能做一个统一的控制呢 :(

为什么不用PDO?

bk7477890
  • 1
新手上路,请多包涵

防止注入的话PDO就可以了,防止XSS的话建议使用安全厂商提供的一些XSS过滤脚本在输出时过滤.

我觉得数据类型要控制好,另外就是防止SQL注入攻击了。
还有就是最好用Session保存,别一样的内容总提交做SQL。

AlphaGooo
  • 1.1k

什么是$_GET? $_GET是一类通过 URL 参数传递给当前脚本的变量的数组。而$HTTP_GET_VARS 包含相同的信息, 但它不是一个超全局变量。 (注意 $HTTP_GET_VARS 和 $_GET 是不同的变量,PHP 处理它们的方式不同).

那么$_GET()的安全考虑主要是在获取值的时候进行的安全考虑的,在进行数据的过滤时防止XSS攻击,PDO攻击中的SQL注入等安全问题,这些都是通过$_GET()传递值来威胁到系统安全的,还有敏感数据最好进行加密处理,不要太过于明显的暴露,以防被劫持利用等危险。

那么一些简单的数据过滤的方式有:

对于XXS攻击(跨站脚本攻击)都是由于对用户的输入没有进行严格的过滤造成的,所以我们必须在所有数据进入我们的网站和数据库之前把可能的危险拦截。针对非法的HTML代码包括单双引号等,可以使用htmlentities()来进行数据过滤。 但是要注意一点,htmlentities()默认编码为 ISO-8859-1,如果你的非法脚本编码为其它,那么可能无法过滤掉,同时浏览器却可以识别和执行。

对于PDO攻击中的SQL注入,在php编写程序中,我们可以使用类似如下的PDO预处理绑定语句来有效的防止sql注入问题:

$stmt  =  $dbh -> prepare ( "INSERT INTO REGISTRY (name, value) VALUES (:name, :value)" );
$stmt -> bindParam ( ':name' ,  $name );
$stmt -> bindParam ( ':value' ,  $value );
$name  =  'one' ;
$value  =  1 ;
$stmt -> execute ();

或者使用?号占位符来达到相同的效果:

$stmt  =  $dbh -> prepare ( "INSERT INTO REGISTRY (name, value) VALUES (?, ?)" );
futurePlayer
  • 538

防不住xss。这个说的是$_GET这个方法,你要是封装好了了那还说这个干嘛?

宣传栏