无法理解语法错误,eval()'d代码中的意外'='

时间:2015-01-14 15:55:12

标签: php eval

我搜索了不同的表格,但不明白如何解决这里首先发现的问题(first linksecond link),但它给了我eval中的错误。我无法弄清楚如何在foreach循环中解决该代码。

foreach($_POST as $key => $value) {
        if(!strstr($key, 'removeFile')){
                //initialize variables using eval
                eval("$" . $key . " = '" . sanitize($value) . "';");
        }
}

2 个答案:

答案 0 :(得分:2)

首先,我对您的代码存在的问题:

  1. eval是非常非常需要的,非常危险,谨慎使用。我已经在PHP中开发了超过10年,并且从未真正遇到需要eval的情况。这也不例外。不需要评估
  2. 您正在清理整个$_POST数组。这很棒,但是有这样的特殊功能,例如:filter_input_arrayarray_filter以及更多......更不用说现成的开源项目和框架了已经包含一个可靠的请求验证组件。
  3. 始终检查函数的返回值,您似乎正在使用strstr,但要厌倦返回不同类型的函数(如strstr:如果返回false,则返回0没有找到针,但如果在干草堆字符串的开头找到针,则返回if。您的sanitize($value)声明可能无法正常使用。
  4. 您假设$sanitized = array_filter($_POST, 'sanitize');//call sanitize on all values foreach ($sanitized as $key => $value) { if (!isset($$key) && strstr($key, 'removeFile') === false) $$key = $value; } 值不包含任何单引号。为什么?因为如果他们这样做,你最终会在你的evaled字符串中出现语法错误
  5. 尽管如此,您可以使用变量 轻松编写代码 添加一个简单的检查,不要踩到现有变量范围:

    $_POST

    但实际上,null值属于一起,它们是请求的一部分,并且应该保持分组...在数组中,或在某种对象中。不要将每个值分配给它自己的变量,因为很快你就会忘记设置哪些变量,哪些变量没有。使用unset变量创建该变量,赋值//request 1: POST => id=123&foo=bar foreach ($sanitized as $k => $v) $$k = $v; $query = 'SELECT x, y, z FROM tbl WHERE id = ?';//using posted ID as value $stmt = $db->prepare($query); $stmt->execute(array($id)); ,因此您现在所拥有的 非常 容易出错的代码:

    $id

    一切都很好,因为$_POST已设置,但从不信任网络,请不要认为只是因为设置了//request 2: POST => foo=bar&page=2 foreach ($sanitized as $k => $v) $$k = $v; $query = 'SELECT x, y, z FROM tbl WHERE id = ?';//using posted ID as value $stmt = $db->prepare($query); $stmt->execute(array($id));//id is null 所有密钥将被设置,它们的值将是正确的:

    //request 3: POST => id=123&foo=bar&page=2
    foreach ($sanitized as $k => $v)
        $$k = $v;
    //$id is 123, $foo is bar and $page = 2
    $query = 'SELECT x, y, z FROM tbl WHERE id = ? LIMIT 10';//using posted ID as value
    //a lot more code containing this statement:
    $page = someFunc();
    $log->write('someFunc returned log: '.$page);
    //more code
    $offset = 10*($page-1);//<-- page is not what we expected it to be
    $query .= sprintf(' OFFSET %d', $offset);
    $stmt = $db->prepare($query);
    $stmt->execute(array($id));
    

    现在我们遇到了问题。这只是一个示例,说明代码可能会导致问题。想象一下脚本增长了一点,看看这个:

    {{1}}

    现在这可能看起来很牵强,而且很愚蠢,但请相信我:所有这些事情发生,比我想知道的还要多。添加一些意外覆盖现有变量的代码会一直发生。特别是在程序代码中。不要盲目解开阵列。保留该单个变量,并使用键来避免:

    • 灰发
    • 突然,戏剧性的秃头
    • 失去理智
    • 出血性溃疡
    • 在工作环境中:灾难性的数据丢失
    • 突然失去工作
    • ...因为像这样的代码让独角兽哭了, bronies 会追捕你

答案 1 :(得分:1)

作为您链接的帖子的第一个答案,问题是当使用双引号时,PHP认为您的eval()代码以变量开头。由于情况并非如此,您有两种选择。使用单引号并记住转义单引号,在代码中声明一个字符串或转义美元符号。

加分说明

对于您要解决的问题,存在更优雅的解决方案。我能想到的最佳解决方案是使用extract函数。这有两个主要好处。

  1. 适用于所有关联数组
  2. 您可以指定不同的标记,以帮助您区分提取的变量并避免变量注入。
  3. 您可以使用的一个标志是EXTR_PREFIX_ALL。这将使用您自己的前缀为所有提取的变量添加前缀。然后,您将使用前缀“PREFIX_”访问变量,如下所示:

    $array = [
        'variable1' => 'foo', 
        'variable2' => 'bar' 
    ];
    
    extract($array, EXTR_PREFIX_ALL, 'PREFIX_');
    
    $value1 = $PREFIX_variable1; // Equals: foo
    $value2 = $PREFIX_variable2; // Equals: bar
    

    关于代码注入的一点

    假设您有一些代码:

    $login = false;
    

    $login变量确定用户是否已登录。 然后在某处使用''extract'函数和以下数组而不使用任何标志。

    $array = [
        'login' => true,
        'foo'   => 'bar'
    ];
    
    extract($array);
    

    现在,您的$login变量将设置为true,发布数据的用户将覆盖初始设置,并且无需有效登录即可访问您的网站。请记住,这是一个过于简化的示例,但仍然有效。

    为了克服这个问题,您可以像我之前展示的那样使用标志EXTR_SKIP或前缀。如果已经定义了具有相同名称的变量,则EXTR_SKIP标志将跳过数组元素。所以现在你的代码不会覆盖你的$login变量。

    extract($array, EXTR_SKIP); // Skip existing variables
    // Or
    extract($array, EXTR_PREFIX_ALL, 'prefix'); // Prefix them all.
    

    希望这可以为您的需求提供正确的选择。

    问候。