使用PHP作为模板语言

时间:2010-05-05 04:24:09

标签: php templates

编辑: 各地的好点,专用的模板语言显然是要走的路。谢谢!

我写了这个快速课程,通过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 ......

5 个答案:

答案 0 :(得分:4)

不确定..

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

修改

不能马上想到任何其他事情。但是如果在同一个Template实例上多次调用渲染,你应该知道你的范围受到了损害。

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

答案 1 :(得分:4)

这不是一个好主意。即使您修复了直接的安全漏洞,毫无疑问也会有其他人错过。我想说如果你真的想给用户这种能力,可以使用Smarty之类的实际模板语言,或自己编写。 PHP非常适合作为内部使用的模板语言,但不是一个所有用户都可以使用的开放语言。有很多方法可以被利用,即使你能抓住它们,你也要做更多的工作而不是编写一个真正的模板引擎。

答案 2 :(得分:3)

你可以使用{和}变量并运行任何函数。

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

此外,因为您只是运行代码,所以它可以访问函数呈现可以访问的所有变量。包括调用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(); ?>';

答案 3 :(得分:1)

我认为这样的事情会起作用我错了吗?

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

如果您真的想要这样做,请使用tokenizer来解析来源。我不推荐它。事实上,我劝阻它!

答案 4 :(得分:1)

如果您有用户提供内容,并且这些用户并非完全值得信赖,我建议使用白名单而不是黑名单标记。

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

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