無標題文檔

Magic quotes 的问题

对于 Magic quotes,对于 PHPer 而言是个老生常谈的问题。今天无意间 看到篇文章 ,结合 PHP Manual 以及其回复,在这里做个简单的汇总。

简而言之,Magic quotes 开启后会自动转义输入的数据。其中,所有的单引号(')、双引号(")、反斜线、和 NULL 字符都会被转义(增加个反斜线), 其实这操作本质上调用的是 addslashes 函数

为什么使用 Magic quotes

方便快捷

PHP 的设计者在设计之初的构想就是能够快速方便的编程。例如插入数据库时,Magic quotes 会自动将数据转义,这很方便。

对初学者有利

Magic quotes 可以从一定程度上,让初学者带离脚本的安全风险。例如在没有任何保护措施的代码下,开启了 Magic quotes 后会少很多的风险,例如 注入问题 。当然,单一使用此方法,并不能完全阻止此类安全问题。

「我没有权限去关闭」

很显然你已经可能意识到了这个问题,但是主机空间并非完全由自己控制。

为什么不使用 Magic quotes

可移植性

无论此功能是否开启,它都会影响脚本的可移植性,因为它影响我们后续过滤数据的操作。

性能问题

在获取所有的外部数据之前都会被转义,这无疑会增加运行时的花销(而且并不是所有的数据都需要转义)。

造成困惑

正如上述所言,并非所有的数据都需要被转义。有可能出现的一种情况,就是当你为了获取未被转义的数据,而「疯狂的」使用 stripslashes 函数。

PHP6 已经不支持

PHP 的设计者显然已经意识到了自己的「错误」,所以在 PHP6 中已经将其废弃。

如何禁用 Magic quotes

按照本人观点,使用 php.ini 配置文件全局禁用 Magic quotes 是最靠谱的。参考下面的代码

; Magic quotes
;
; Magic quotes for incoming GET/POST/Cookie data.
magic_quotes_gpc = Off
; Magic quotes for runtime-generated data, e.g. data from SQL, from exec(), etc.
magic_quotes_runtime = Off
; Use Sybase-style magic quotes (escape ' with '' instead of ').
magic_quotes_sybase = Off

然而线上的主机可能无法让你修改 php.ini 文件,那么可以使用 .htaccess 文件禁用,加入下面的代码

php_flag magic_quotes_gpc Off

上述可移植的代码而言,无论是否禁用 magic_quotes,数据必须保持一致。那么下面的代码可以帮助您

<?php
if (get_magic_quotes_gpc()) {
    function stripslashes_deep($value) {
        $value = is_array($value) ?
            array_map('stripslashes_deep', $value) :
            stripslashes($value);
        return $value;
    }

    $_GET     = array_map('stripslashes_deep', $_GET);
    $_POST    = array_map('stripslashes_deep', $_POST);
    $_COOKIE  = array_map('stripslashes_deep', $_COOKIE);
    $_REQUEST = array_map('stripslashes_deep', $_REQUEST);
}

不要轻信 PHP_SELF

开门见山,考虑下面的代码( 原文连接 有详细的解释)

<html>
    <body>
        <?php
            if (isset($_REQUEST['submitted']) && $_REQUEST['submitted'] == '1') {
                echo "Form submitted!";
            }
        ?>
        <form action="<?php echo $_SERVER['PHP_SELF']; ?>">
            <input type="hidden" name="submitted" value="1" />
            <input type="submit" value="Submit!" />
        </form>
    </body>
</html>

看似准确无误的代码,但是暗藏着危险。让我们将其保存为 foo.php ,然后放到 PHP 环境中使用

foo.php/%22%3E%3Cscript%3Ealert('xss')%3C/script%3E%3Cfoo

访问,会发现弹出个 Javascript 的 alert -- 这很明显又是个 XSS 的注入漏洞 。究其原因,发现是在

echo $_SERVER['PHP_SELF'];

这条语句上直接输出了未过滤的值。追根数源,我们看下 PHP 手册的描述

'PHP_SELF'

The filename of the currently executing script, relative to the document root. 
For instance, $_SERVER['PHP_SELF'] in a script at the address 
http://example.com/test.php/foo.bar would be /test.php/foo.bar. The __FILE__ 
constant contains the full path and filename of the current (i.e. included) file.
If PHP is running as a command-line processor this variable contains the script 
name since PHP 4.3.0. Previously it was not available. 

原因很明确了,原来是 $_SERVER['PHP_SELF'] 虽然「看起来」是服务器提供的环境
变量,但这的确和 $_POST$_GET 一样,是可以被用户更改的。

其它类似的变量有很多,比如 $_COOKIE 等(如果用户想「把玩」他们的 cookie,那我们也是没有办法)。解决方案很简单,使用 strip_tagshtmlentities 等此类函数过滤或者转义。

echo htmlentities($_SERVER['PHP_SELF']); 

-- Split --

上述的例子让我们需要时刻保持谨慎 coding 的心态。Chris Shiflett 在他的 Blog 总结的相当直白 ,防止 XSS 的两个基本的安全思想就是

Filter input
Escape output

我将上面翻译成 「_过滤输入,转义输出_」。详细的内容,可以参考他 Blog 的 这篇文章 ,此处略。

使用 PHP Bug Scanner

很多时候「不留神」就会在脚本上留下隐患,比如常见的 注入漏洞 等等。由于加上 PHP 是动态类型的语言,所以在安全方面要尤其注意。

除了上述的注入漏洞以外,一些函数也能造成一定的安全问题。PHP Bug Scanner 则可以帮助你找到调用这些函数的语句,以便编写者确认这条语句是否有问题。

https://friable.rocks/_/2009_11_05/7531360c6777.jpg

另外,除了常见的 default.cfg,还有多种 perset 可以选择,比如需要检查 SQL 函数方面的调用函数,则选择 sql.cfg 即可。

https://friable.rocks/_/2009_11_05/7467760c6778.jpg

下面是此程序的 打包下载 。另, 提供 Zend 方面的 PHP 安全 Tips

PHP5.3 新特性 之 静态调用

Late Static Bindings

考虑下述代码的输出

<?php
class clsParent {
    static public function say( $str ) {
        self::do_print($str);
    }

    static public function do_print( $str ) {
        echo "parent says $str";
    }
}

class clsChild extends clsParent{
    static public function do_print( $str ) {
        echo "child says $str";
    }
}

clsChild::say('Hello');
?>

你可能很希望输出 'child says Hello',但是实际上输出的是 'parent says Hello'。这是因为 self:: 指向的是类的本身,也就是 clsParent(同道理,__CLASS__ 也是一样)。

针对这一情况,PHP5.3 [引入了

PHP5.3 新特性 之 命名空间

上次说过 ,PHP 5.3 的一个新的重要特性就是 命名空间 (namespace)。

这一特性在 PHP5.0x 时候就提出过,后来被取消并安排在 PHP6 中实现。而此次又再次「提前」到了 PHP5.3 发布,可见开发人员对其的重视以及谨慎的态度。

官方发布时说明 文档的内容 可能已过期(documentation maybe out dated),所以在这里简单的说明命名空间的用法:首先是声明一个命名空间,加入了新的关键字 namespace ,其应在类文件的开头

<?php
namespace Project::Module; 

class User {
    const STATUS_OK = true;

    function register($data) {
        ...
    }
    
    ...
}

然后在控制器中(可能是其他文件)就可以这样调用

$user = new Project::Module::User(); 
$user->register($register_info);

的确与平常的并无两样,但是我们可以将两个相互独立的类联系起来。比如

Project::Module::User; 
Project::Module::Blog;

这样就能从语言本身更容易描述和理解变量、类之间的关系,从而避免了「传统」上的 Project_Module_Blog 这样冗长的命名方式。

上面的说明可能很难说明使用命名空间带来了什么好处,新增加的 use 和 as 关键字或许能更好的说明问题。use 和 as 语句可以引用和声明 命名空间的「别名」。比如,上述的控制器中实例化类的代码可以这样写

use Project::Module;
$user = new Module::User(); 
$user->register($register_info);

甚至

use Project::Module::User as ModuleUser;
$user = new ModuleUser; 
$user->register($register_info);

类中的常量也可以通过命名空间访问,比如上述类中的 STATUS_OK 就可以通过命名空间

Project::Module::User::STATUS_OK

访问。进一步的,也可以用别名简化那么长的「变量名称」

use Project::Module::User::STATUS_OK as STATUS_OK;
echo STATUS_OK;

顺便提下「 超空间(The Global Namespace) 」的概念。所谓的「超空间」,就是没有指定命名空间的变量、类和函数。比如

function foo() {
    ...
}

这的函数,可以使用 foo() 执行的同时,也可以使用 ::foo(); 这样执行。

最后,配合使用 autoload 函数即可载入指定命名空间的类。简单的函数如下

function __autoload( $classname ) {
    $classname = strtolower( $classname );
    $classname = str_replace( '::', DIRECTORY_SEPARATOR, $classname );
    require_once( dirname( __FILE__ ) . '/' . $classname . '.class.php' );
}

这样,比如调用

__autoload('Project::Module::User');

就可以自动载入 Project_Module_User.class.php 文件(虽然这样看起来并不方便多少)。

我的照片

嗨!我叫「明城」,八零后、码农、宁波佬,现居杭州。除了这里,同时也欢迎您关注我的 GitHub (2) 、 TwitterInstagram 等。

这个 Blog 原先的名字叫 Gracecode.com 、现在叫 「無標題文檔」 。 其实无所谓叫什么名字,作为码农知道取名是件很难的事情。最后想到的这个名字,其实都没啥特别的含义,系统默认的文件名而已。

作为八零后,自认为还仅存点傲娇式的幽默感,以及对平淡生活的追求和向往。 为了免得对号入座和不必要的麻烦,声明本站点所持观点仅代表个人意见,不代表自己所服务公司的立场。

如果您想联系我,可以发我邮件 `echo bWluZ2NoZW5nQG91dGxvb2suY29tCg== | base64 -d`

文章

项目