【问题标题】:Using PHP as template language使用 PHP 作为模板语言
【发布时间】:2010-05-05 04:24:09
【问题描述】:

编辑: 很重要的一点,专用的模板语言显然是要走的路。谢谢!

我编写了这个快速课程来通过 PHP 进行模板化——我想知道如果我曾经向用户开放模板,这是否很容易被利用(不是近期计划,而是考虑未来)。

class Template {

private $allowed_methods = array(
    'if', 
    'switch', 
    'foreach',
    'for', 
    'while'
);

private function secure_code($template_code) {
    $php_section_pattern = '/\<\?(.*?)\?\>/';
    $php_method_pattern = '/([a-zA-Z0-9_]+)[\s]*\(/';
    preg_match_all($php_section_pattern, $template_code, $matches);
    foreach (array_unique($matches[1]) as $index => $code_chunk) {
        preg_match_all($php_method_pattern, $code_chunk, $sub_matches);
        $code_allowed = true;
        foreach ($sub_matches[1] as $method_name) {
            if (!in_array($method_name, $this->allowed_methods)) {
                $code_allowed = false;
                break;
            }
        }
        if (!$code_allowed) {
            $template_code = str_replace($matches[0][$index], '', $template_code);
        }
    }
    return $template_code;      
}

public function render($template_code, $params) {
    extract($params);
    ob_start();
    eval('?>'.$this->secure_code($template_code).'<?php ');
    $result = ob_get_contents();
    ob_end_clean();
    return $result;     
}

}

示例用法:

$template_code = '<?= $title ?><? foreach ($photos as $photo): ?><img src="<?= $photo ?>"><? endforeach ?>';
$params = array('title' => 'My Title', 'photos' => array('img1.jpg', 'img2.jpg'));
$template = new Template;
echo $template->render($template_code, $params);

这里的想法是我将模板(PHP 代码)存储在数据库中,然后通过使用正则表达式的类运行它,以仅允许允许的方法(if、for 等)。任何人都看到了一种明显的方法来利用它并运行任意 PHP?如果是这样,我可能会采用更标准的模板语言,例如 Smarty...

【问题讨论】:

  • 你真的不应该这样做。有一千种制造麻烦的方法,你将不可避免地错过。试图阻止它们中的每一个都会使模板变得无用。
  • 明白了,也明白了。这实际上仅供内部使用,但我想知道我自己的教化的利用可能性。
  • 请注意,Smarty 也不完全安全 (smarty.net/manual/en/language.function.php.php)。
  • @tadamson:嗯,我认为有办法禁用该标签...也许不是。
  • 如果您得到了答案,请点击旁边的绿色大勾号接受。谢谢,祝你好运。 :)

标签: php templates


【解决方案1】:

当然..

$template_code = '<?= `rm -rf *`; ?>';

编辑:

马上就想不出别的了。但是您应该知道,如果在同一个 Template 实例上多次调用渲染,您的作用域就会受到影响。

例如,如果您render('&lt;?php $this-&gt;allowed_methods[] = "eval"; ?&gt;') .. 那么Template 的实例将具有eval 作为下一次渲染的可接受函数.. ;)

【讨论】:

  • 聪明。即使你解决了这个问题,可能还有其他人......可能更安全只是不让他们编写 PHP。
  • 啊,是的。我将过滤掉反引号运算符...您还能想到其他吗?
【解决方案2】:

这不是一个好主意。即使您修复了直接的安全漏洞,无疑也会有其他漏洞。我想说,如果您真的想为用户提供这种能力,请使用实际的模板语言,例如Smarty,或者自己编写。 PHP 作为一种供内部使用的模板语言非常棒,但不是所有用户都可以使用的开放语言。有很多方法可以利用它,即使你能抓住它们,你要做的工作比编写一个真正的模板引擎还要多。

【讨论】:

    【解决方案3】:

    您可以将 { 和 } 与变量一起使用并运行任何函数。

    $template_code = '<?php $f = "phpinfo"; ${"f"}(); ?>';
    

    此外,由于您只是在运行代码,因此它可以访问函数 render 可以访问的所有变量。包括调用 global 从几乎任何地方修改变量或超级全局变量,例如 $_SESSION(一个这样的选项可能是保存登录信息的会话变量,并使用 javascript 通过 ajax 发布到另一个站点)。

    $a = "hello";
    $template_code = '<?php global $a; $a = "test"; ?>';
    $params = array('title' => 'My Title', 'photos' => array('img1.jpg', 'img2.jpg'));
    $template = new Template;
    echo $template->render($template_code, $params);
    echo $a;
    

    另一个,滥用允许的函数,通过创建一个与允许的函数同名的变量和任何函数名的值。

    $template_code = '<?php $if="phpinfo"; $if(); ?>';
    

    【讨论】:

      【解决方案4】:

      我错误地认为这样的事情会起作用?

      <?php
      /* ?> trick your parser by using a comment */
      // do whatever unfiltered
      

      如果你真的想做这样的事情,请使用tokenizer 来解析源代码。我不推荐它。事实上,我不鼓励它!

      【讨论】:

      • +1 你是对的。 render('&lt;?php /* ?&gt; */ echo("lol"); ?&gt;') 有效。请注意,Kunal,有很多方法可以解决这个问题:)
      • 哈哈,注意到了。它是真正的模板语言。
      【解决方案5】:

      如果您有用户贡献内容,而这些用户并不完全值得信赖,我建议您使用 allowlisting 而不是 denylisting 标记。

      例如,考虑 Stack Overflow 允许的 Markdown 格式。它支持非常短的格式选项列表,其他所有内容都被视为文字文本。大多数用户更喜欢简单的界面,而不是“编写任何你想要的代码!但注意不要破坏应用程序!”的界面。

      我的经验法则是:允许用户输入数据和内容;绝不允许用户输入代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-16
        • 2010-11-30
        • 1970-01-01
        • 2010-10-17
        • 1970-01-01
        • 2019-01-11
        相关资源
        最近更新 更多