<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML ><HEAD ><TITLE >使用 Register Globals</TITLE ><META NAME="GENERATOR" CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK REL="HOME" TITLE="PHP 手册" HREF="index.html"><LINK REL="UP" TITLE="安全" HREF="security.html"><LINK REL="PREVIOUS" TITLE="错误报告" HREF="security.errors.html"><LINK REL="NEXT" TITLE="用户提交的数据" HREF="security.variables.html"><META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8"></HEAD ><BODY CLASS="chapter" BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#840084" ALINK="#0000FF" ><DIV CLASS="NAVHEADER" ><TABLE SUMMARY="Header navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TH COLSPAN="3" ALIGN="center" >PHP 手册</TH ></TR ><TR ><TD WIDTH="10%" ALIGN="left" VALIGN="bottom" ><A HREF="security.errors.html" ACCESSKEY="P" >上一页</A ></TD ><TD WIDTH="80%" ALIGN="center" VALIGN="bottom" ></TD ><TD WIDTH="10%" ALIGN="right" VALIGN="bottom" ><A HREF="security.variables.html" ACCESSKEY="N" >下一页</A ></TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="chapter" ><H1 ><A NAME="security.globals" >章 29. 使用 Register Globals</A ></H1 ><P > 可能 PHP 中最具争议的变化就是从 PHP <A HREF="http://www.php.net/releases/4_2_0.php" TARGET="_top" >4.2.0</A > 版开始配置文件中 <A HREF="ini.core.html#ini.register-globals" >register_globals</A > 的默认值从 on 改为 off 了。对此选项的依赖是如此普遍以至于很多人根本不知道它的存在而以为 PHP 本来就是这么工作的。本节会解释用这个指令如何写出不安全的代码,但要知道这个指令本身没有不安全的地方,误用才会。 </P ><P > 当 register_globals 打开以后,各种变量都被注入代码,例如来自 HTML 表单的请求变量。再加上 PHP 在使用变量之前是无需进行初始化的,这就使得更容易写出不安全的代码。这是个很艰难的抉择,但 PHP 社区还是决定默认关闭此选项。当打开时,人们使用变量时确实不知道变量是哪里来的,只能想当然。但是 register_globals 的关闭改变了这种代码内部变量和客户端发送的变量混杂在一起的糟糕情况。下面举一个错误使用 register_globals 的例子: </P ><P > <TABLE WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" CLASS="EXAMPLE" ><TR ><TD ><DIV CLASS="example" ><A NAME="AEN6751" ></A ><P ><B >例 29-1. 错误使用 register_globals = on 的例子</B ></P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" CELLPADDING="5" ><TR ><TD ><code><font color="#000000"> <font color="#0000BB"><?php<br /></font><font color="#FF8000">// 当用户合法的时候,赋值 $authorized = true<br /></font><font color="#007700">if (</font><font color="#0000BB">authenticated_user</font><font color="#007700">()) {<br /> </font><font color="#0000BB">$authorized </font><font color="#007700">= </font><font color="#0000BB">true</font><font color="#007700">;<br />}<br /><br /></font><font color="#FF8000">// 由于并没有事先把 $authorized 初始化为 false,<br />// 当 register_globals 打开时,可能通过GET auth.php?authorized=1 来定义该变量值<br />// 所以任何人都可以绕过身份验证<br /></font><font color="#007700">if (</font><font color="#0000BB">$authorized</font><font color="#007700">) {<br /> include </font><font color="#DD0000">"/highly/sensitive/data.php"</font><font color="#007700">;<br />}<br /></font><font color="#0000BB">?></font> </font> </code></TD ></TR ></TABLE ></DIV ></TD ></TR ></TABLE > </P ><P > 当 register_globals = on 的时候,上面的代码就会有危险了。如果是 off,<CODE CLASS="varname" >$authorized</CODE > 就不能通过如 URL 请求等方式来改变,这样就好多了,尽管初始化变量是一个良好的编程习惯。比如说,如果在上面的代码执行之前加入 <TT CLASS="literal" >$authorized = false</TT > 的话,无论 register_globals 是 on 还是 off 都可以,因为用户状态被初始化为未经认证。 </P ><P > 另一个例子是关于<A HREF="ref.session.html" >会话</A >的。当 register_globals = on 的时候,<CODE CLASS="varname" >$username</CODE > 也可以用在下面的代码中,但要意识到 <CODE CLASS="varname" >$username</CODE > 也可能会从其它途径进来,比如说通过 URL 的 GET。 </P ><P > <TABLE WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" CLASS="EXAMPLE" ><TR ><TD ><DIV CLASS="example" ><A NAME="AEN6762" ></A ><P ><B >例 29-2. 使用会话时同时兼容 register_globals on 和 off 的例子</B ></P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" CELLPADDING="5" ><TR ><TD ><code><font color="#000000"> <font color="#0000BB"><?php<br /></font><font color="#FF8000">// 我们不知道 $username 的来源,但很清楚 $_SESSION 是<br />// 来源于会话数据<br /></font><font color="#007700">if (isset(</font><font color="#0000BB">$_SESSION</font><font color="#007700">[</font><font color="#DD0000">'username'</font><font color="#007700">])) {<br /><br /> echo </font><font color="#DD0000">"Hello <b></font><font color="#007700">{</font><font color="#DD0000">$_SESSION</font><font color="#007700">[</font><font color="#DD0000">'username'</font><font color="#007700">]}</font><font color="#DD0000"></b>"</font><font color="#007700">;<br /><br />} else {<br /><br /> echo </font><font color="#DD0000">"Hello <b>Guest</b><br />"</font><font color="#007700">;<br /> echo </font><font color="#DD0000">"Would you like to login?"</font><font color="#007700">;<br /><br />}<br /></font><font color="#0000BB">?></font> </font> </code></TD ></TR ></TABLE ></DIV ></TD ></TR ></TABLE > </P ><P > 采取相应的预防措施以便在伪造变量输入的时候给予警告是完全有可能的。如果事先确切知道变量是哪里来的,就可以检查所提交的数据是否是从不正当的表单提交而来。不过这不能保证变量未被伪造,这需要攻击者去猜测应该怎样去伪造。如果不在乎请求数据来源的话,可以使用 <CODE CLASS="varname" >$_REQUEST</CODE > 数组,它包括了 GET、POST 和 COOKIE 的所有数据。详情可参见本手册的<A HREF="language.variables.external.html" >来自 PHP 之外的变量</A >。 </P ><P > <TABLE WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" CLASS="EXAMPLE" ><TR ><TD ><DIV CLASS="example" ><A NAME="AEN6769" ></A ><P ><B >例 29-3. 探测有害变量</B ></P ><TABLE BORDER="0" BGCOLOR="#E0E0E0" CELLPADDING="5" ><TR ><TD ><code><font color="#000000"> <font color="#0000BB"><?php<br /></font><font color="#007700">if (isset(</font><font color="#0000BB">$_COOKIE</font><font color="#007700">[</font><font color="#DD0000">'MAGIC_COOKIE'</font><font color="#007700">])) {<br /><br /> </font><font color="#FF8000">// MAGIC_COOKIE 来自 cookie<br /> // 这样做是确保是来自 cookie 的数据<br /><br /></font><font color="#007700">} elseif (isset(</font><font color="#0000BB">$_GET</font><font color="#007700">[</font><font color="#DD0000">'MAGIC_COOKIE'</font><font color="#007700">]) || isset(</font><font color="#0000BB">$_POST</font><font color="#007700">[</font><font color="#DD0000">'MAGIC_COOKIE'</font><font color="#007700">])) {<br /><br /> </font><font color="#0000BB">mail</font><font color="#007700">(</font><font color="#DD0000">"admin@example.com"</font><font color="#007700">, </font><font color="#DD0000">"Possible breakin attempt"</font><font color="#007700">, </font><font color="#0000BB">$_SERVER</font><font color="#007700">[</font><font color="#DD0000">'REMOTE_ADDR'</font><font color="#007700">]);<br /> echo </font><font color="#DD0000">"Security violation, admin has been alerted."</font><font color="#007700">;<br /> exit;<br /><br />} else {<br /><br /> </font><font color="#FF8000">// 这一次请求中并没有设置 MAGIC_COOKIE 变量<br /><br /></font><font color="#007700">}<br /></font><font color="#0000BB">?></font> </font> </code></TD ></TR ></TABLE ></DIV ></TD ></TR ></TABLE > </P ><P > 当然,单纯地关闭 register_globals 并不代表所有的代码都安全了。对于每一段提交上来的数据,都要对其进行具体的检查。永远要验证用户数据和对变量进行初始化!把 <A HREF="function.error-reporting.html" ><B CLASS="function" >error_reporting()</B ></A > 设为 <TT CLASS="constant" ><B >E_NOTICE</B ></TT > 级别可以检查未初始化的变量。 </P ><P > 更多关于模拟 register_globals 为 on 或 off 的信息,请见此 <A HREF="faq.misc.html#faq.misc.registerglobals" >FAQ</A >。 </P ><DIV CLASS="note" ><BLOCKQUOTE CLASS="note" ><P ><B >Superglobals 可用性说明: </B >自 PHP 4.1.0 起,可以使用超全局数组变量例如 <CODE CLASS="varname" >$_GET</CODE >,<CODE CLASS="varname" >$_POST</CODE > 和 <CODE CLASS="varname" >$_SERVER</CODE > 等等。更多信息请阅读手册中的 <A HREF="language.variables.predefined.html" >superglobals</A >。</P ></BLOCKQUOTE ></DIV ></DIV ><DIV CLASS="NAVFOOTER" ><HR ALIGN="LEFT" WIDTH="100%"><TABLE SUMMARY="Footer navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" ><A HREF="security.errors.html" ACCESSKEY="P" >上一页</A ></TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="index.html" ACCESSKEY="H" >起始页</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" ><A HREF="security.variables.html" ACCESSKEY="N" >下一页</A ></TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >错误报告</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="security.html" ACCESSKEY="U" >上一级</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >用户提交的数据</TD ></TR ></TABLE ></DIV ></BODY ></HTML >