如何从注入中保护PHP中的模板引擎

时间:2014-08-17 14:18:40

标签: php security templates

我目前正在尝试在PHP中创建一个简单的模板引擎。我关心的主要是安全性,但模板教程却没有。让我们说我有一个数据库表,其中包含用户名和他的描述。用户可以在那里输入他想要的任何内容。

我的猜测是使用htmlspecialchars()函数,以防止javascript和html注入。但是'模板代码注入&#39 ;?如果我的模板规则是将[@key]替换为" value",则用户可以更新干扰我的模板处理程序的描述。我应该对待" ["," @","]"作为特殊字符,并在使用我的set方法时用ascii代码替换它们?

的template.php:

class Template {
    protected $file;
    protected $values = array();

    public function __construct($file) {
        $this->file = $file;
    }

    public function set($key, $value) {
        $this->values[$key] = $value;
    }

    public function output() {
        if (!file_exists($this->file)) {
            return "Error loading template file ($this->file).";
        }
        $output = file_get_contents($this->file);

        foreach ($this->values as $key => $value) {
            $tagToReplace = "[@$key]";
            $output = str_replace($tagToReplace, $value, $output);
        }

        return $output;
    }
}

example.tpl:

用户名:[@ name]
关于我:[@info]

的index.php:

include 'template.php';
$page = new Template('example.tpl');
$page->set('info', '[@name][@name][@name]I just injected some code.');
$page->set('name', 'Tom');
echo $page->output();

这会显示:

用户名:汤姆
关于我:TomTomTomI刚刚注入了一些代码。

我使用的代码基于:

http://www.broculos.net/2008/03/how-to-make-simple-html-template-engine.html

2 个答案:

答案 0 :(得分:1)

更改您的功能,仅针对已知密钥在未更改的模板中搜索一次:

public function output() {
    if (!file_exists($this->file)) {
        return "Error loading template file ($this->file).";
    }
    $output = file_get_contents($this->file);

    $keys = array_keys($this->values);
    $pattern = '$\[@(' . implode('|', array_map('preg_quote', $keys)) . ')\]$';

    $output = preg_replace_callback($pattern, function($match) {
        return $this->values[$match[1]];
    }, $output);

    return $output;
}

答案 1 :(得分:0)

我正在思考它,我认为这个解决方案最快,最简单:

foreach ($this->values as $key => $value) {
    $tagToReplace = "[@$key]";
    if (strpos($output, "[@$value]") !== FALSE)
      $value = '['.substr($value,1,-1).']';
    $output = str_replace($tagToReplace, $value, $output);
}

如果[$ value]在输出中,它会用html实体字符串替换值中的括号。

Used this html entity list

对于未来的采用者: 如果通过加载非隔离/非隔离文件来实现Template Parser,那么这种解决方案就可以了(如OP的情况那样,通过使用file_get_contents加载本地文件)。但是,如果它是通过INCLUDING PHP视图实现的,请注意当您将一些用户可修改的数据从数据库直接放入视图(不使用解析器,例如<?=$var;?>)然后使用模板解析器时,此检查将无法处理对于这个观点。在这种情况下,解析器无法知道这些数据是否是模板结构的一部分,并且此检查对它们不起作用。 (我不知道如何正确处理这种情况。)无论如何,我认为最好的做法是永远不要将敏感数据传递给模板解析器,即使你不在模板中使用它们。当攻击者然后欺骗解析器来评估他的自定义数据时,他将无法获得他没有的信息。更好的是,不要使用模板解析器。