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