禁用了“魔术引号”,为什么PHP/WordPress仍然自动转义我的POST数据?

47

这是一个简单的问题,但答案却很难找到。

get_magic_quotes_gpc() 返回 0。我再说一遍,魔术引号已经关闭了。魔术引号似乎在 php.ini 中被禁用了(而不是在运行时)。

然而,所有 POST 数据,包括单引号('),在 PHP 中访问时都被转义了。这可能是什么原因造成的呢?


在准备测试用例时,我发现了问题的普遍来源。我们正在引导 WordPress,因为我们的应用程序与 WordPress 多站点安装集成在一起。当我禁用 WordPress 引导时,自动转义就会被禁用。WordPress 的自动转义代码可能位于哪里?


3
请提供一个简明的测试脚本,以便您重现此问题。也许问题只是在测试方法上。 - deceze
你可能想要检查你的浏览器发送的头信息。 - Corubba
ini_get报告它关闭了吗? - Milo LaMar
感谢大家的反馈! :) - rinogo
我可以向您保证,这个问题也发生在2019年11月的PHP 7.1.33(2019-10-23)和WordPress 5.2.4(2019-10-14)版本中...(魔术引号在PHP 5.4.0中被删除。) - Peter Mortensen
6个回答

45

我想我找到了答案。问题(错误): http://core.trac.wordpress.org/ticket/18322

解决方法: http://codex.wordpress.org/Function_Reference/stripslashes_deep

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

注意:如@Alexandar O'Mara所建议的那样,您可能需要重新考虑像这样覆盖superglobals。 如果适合您的情况,例如,您可以使用替代方案“本地删除”,如$post = array_map('stripslashes_deep', $_POST);

另请参见@quickshiftin的出色答案。


14
谢谢您分享有关如此荒谬的 WordPress 行为的信息。 - Your Common Sense
3
两年过去了,我们仍然存在这个漏洞。请注意不能多次使用此功能,因为如果你使用了它,其他插件也使用了它,结果会是不可预测的。 - ViliusL
4
如果VilliusL担心这个插件会与其他插件冲突,记住,您不必覆盖原始的超全局变量($_POST, $_GET等)。您可以像这样做: $post_copy = array_map('stripslashes_deep', $_POST); (请注意,$post_copy不是超全局变量,因此您应使用 global $post_copy; 或将$post_copy作为参数传递)。 - rinogo
7
警告: 直接使用本答案中的代码可能会导致安全漏洞。请参考WordPress工单中的以下引用: "目前魔术引号 必要的,因为删除它们可能会很容易地让我们面临意外的安全漏洞。即使我们在核心中修复了所有这些问题,仍然可能有数百个(保守估计)插件会突然变得不安全,因为它们假设数据被转义而实际上并没有。" - Alexander O'Mara
2
18322在2019年11月22日重新开放(这几乎是8年后)。 - Peter Mortensen
显示剩余2条评论

19

在 @rinogo 的回答上进行深入解释,并提供另一种解决方法。


wp-settings.php 中有一个无条件调用 wp_magic_quotes 的函数。

// Add magic quotes and set up $_REQUEST ( $_GET + $_POST )
wp_magic_quotes();

无论如何,WordPress都会转义引号

function wp_magic_quotes() {
    // If already slashed, strip.
    // Escape with wpdb.
    // Force REQUEST to be GET + POST.
}

有趣的是,这个调用是在插件加载之后、主题加载之前进行的。所以,在你的插件顶部。

// A hack to cope with un-configurable call to wp_magic_quotes
// E.G. Make the original $_POST available through a global $_REAL_POST
$_REAL_GET     = $_GET;
$_REAL_POST    = $_POST;
$_REAL_COOKIE  = $_COOKIE;
$_REAL_REQUEST = $_REQUEST;

那么,您可以在需要的地方自由使用 $_REAL_POST 等代替 $_POST(记住它是一个 全局变量,而不是 超全局变量)。此外,请记住,虽然您的插件已在主题之前加载,但如果主题调用其中一个使用 $_POST 的插件函数,则应从 $_REAL_POST 读取未转义的值。


有趣的解决方案!你已经在插件中实现了这个特定的解决方案吗? - rinogo
1
为了完全消除烦人的转义,您可以在wp-settings.php中注释掉wp_magic_quotes();,这样它就不会再将转义应用于POST、GET等。 - adamj
2
@adamj 这会破坏依赖于对它们进行转义的 Wordpress 代码;不是一个好主意。 - quickshiftin
1
@quickshiftin 自从将其注释掉以来,我没有遇到任何问题。不过你说得很对。 - adamj
2
感谢提供“wp-settings”解决方案,我在将WordPress和CI集成在一起时遇到了问题,WordPress会修改所有的帖子数据,甚至在CI中也是如此。 - Bankzilla
显示剩余7条评论

2

我刚刚处理了这个问题,并找到了一个我认为非常好的解决方法。它确保GPCs 从不被斜杠分割。我只需将以下代码放在插件文件的顶部(我认为它也适用于主题的顶部):

add_action( 'init', 'unslash_gpc' );
function unslash_gpc() {
    $_GET       = array_map('stripslashes_deep', $_GET);
    $_POST      = array_map('stripslashes_deep', $_POST);
    $_COOKIE    = array_map('stripslashes_deep', $_COOKIE);
    $_SERVER    = array_map('stripslashes_deep', $_SERVER);
    $_REQUEST   = array_map('stripslashes_deep', $_REQUEST);
}

现在一切都完美了!


1
但是为了明确起见,这也会永久修改超全局变量,对吗? - rinogo
1
所有其他插件/主题/核心也将使用未经魔法引用的值?最好使用插件范围变量。无论如何,我喜欢你的解决方案! - rinogo

1

这里提供的最佳答案是将其复制以供个人使用,例如:

$post = array_map('stripslashes_deep', $_POST);

然而,这里存在一个理论问题:由于您正在使用副本,因此无法将任何更改持久化到超全局变量(好吧,我不是说这是一种好的实践,好吗?)。

解决方案:访问器方法

为了以明确的方式解决这个混乱且没有任何副作用,我创建了“访问器方法”,它们透明地应用 stripslashes_deep()addslashes_deep()* 来获取/设置以下超全局数组的请求:

* 我不得不从 WordPress 的 stripslashes_deep() 中组合出 addslashes_deep()

  • $_GET
  • $_POST
  • $_COOKIE
  • $_SERVER
  • $_REQUEST

您可以像下面这样使用它们:

echo _get('username');    // echo stripslashes_deep($_GET['username']);
_cookie('name', 'value'); // $_COOKIE['name'] = addslashes_deep('value');

这是代码(我称之为gpcsr.php):
<?php

// cat stripslashes_deep() | sed 's/stripslashes/addslashes/g'
function addslashes_deep( $value ) {
    if ( is_array($value) ) {
        $value = array_map('addslashes_deep', $value);
    } elseif ( is_object($value) ) {
        $vars = get_object_vars( $value );
        foreach ($vars as $key=>$data) {
            $value->{$key} = addslashes_deep( $data );
        }
    } elseif ( is_string( $value ) ) {
        $value = addslashes($value);
    }

    return $value;
}

function _generic_slashes_wrap(&$arr, $key, $value = null) {
    if (func_num_args() === 2) return stripslashes_deep($arr[$key]);
    else $arr[$key] = addslashes_deep($value);
}

function _get       ($key, $value = null) { if (func_num_args() === 1) return _generic_slashes_wrap($_GET,      $key); else _generic_slashes_wrap($_GET,        $key, $value); }
function _post      ($key, $value = null) { if (func_num_args() === 1) return _generic_slashes_wrap($_POST,     $key); else _generic_slashes_wrap($_POST,       $key, $value); }
function _cookie    ($key, $value = null) { if (func_num_args() === 1) return _generic_slashes_wrap($_COOKIE,   $key); else _generic_slashes_wrap($_COOKIE,     $key, $value); }
function _server    ($key, $value = null) { if (func_num_args() === 1) return _generic_slashes_wrap($_SERVER,   $key); else _generic_slashes_wrap($_SERVER,     $key, $value); }
function _request   ($key, $value = null) { if (func_num_args() === 1) return _generic_slashes_wrap($_REQUEST,  $key); else _generic_slashes_wrap($_REQUEST,    $key, $value); }

?>

1
WordPress通过使用WordPress函数stripslashes_deep来解决这个问题。因此,@rinogo答案中提到的片段将变成:
$_GET     = stripslashes_deep($_GET);
$_POST    = stripslashes_deep($_POST);
$_COOKIE  = stripslashes_deep($_COOKIE);
$_REQUEST = stripslashes_deep($_REQUEST);

另外需要注意的是,WordPress并没有提及$_SERVER全局变量,因此我认为它不会受到影响。

无论get_magic_quotes_gpc()返回什么值,WordPress都会向$_POST/$_GET/$_REQUEST/$_COOKIE添加反斜杠。因此,在使用这些变量时,应始终使用stripslashes()或stipslashes_deep()。


只有在WordPress中,您才会先转义数据,然后在紧接着的下一行取消转义。 - Your Common Sense
@你的常识 是不是很讽刺?WordPress总是做这样的事情,几年后你会得到一个新版本,其中有禁用实际功能的选项,通过在wp-config中定义一个常量或其他方式... - George Dimitriadis

-1

或者,就像我一样。在load.php的wp_magic_quotes()方法中注释掉所有实现。

我不需要魔术引号。这给我带来了比它值得的更多的头痛。个人而言,我更喜欢维护自己的输入净化纪律。我只是不想开始形成不良的编程习惯。

但是,我确实理解WordPress包含这样一个“功能”的冲动。也许开发社区最好提供一个全局选项来禁用它。


1
如果这个解决方案对你有用,那太好了!但是需要注意两个潜在的问题:1)修改核心代码容易出错,而且甚至很危险(从安全角度来看),特别是在转义等方面。2)许多插件都是根据wp_magic_quotes()设计的。修改这个实现可能会改变它们的行为。关于保留/取消 wp_magic_quotes() 的更多讨论:https://core.trac.wordpress.org/ticket/18322 - rinogo
请看我在我的解决方案下面给@adamj的评论,这不是一个好主意,因为你会破坏WordPress代码,而这些值需要被转义。 - quickshiftin

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