2010-05-05 80 views
3

編輯:偉大的點周圍,專門的模板語言顯然是要走的路。謝謝!使用PHP作爲模板語言

我寫了這個快速課程,通過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代碼),然後通過使用正則表達式只允許類中運行允許的方法(如果,等等)。任何人都看到一個明顯的方式來利用這個並運行任意的PHP?如果是的話,我可能會去一個模板語言如Smarty的較爲標準的路線......

+2

你*真*不應該這樣做。有一千種方法會造成你不可避免會錯過的麻煩。試圖阻止它們中的每一個都會使模板無用。 – 2010-05-05 04:47:45

+0

瞭解,並認爲很多。這真的只是供內部使用,但是想知道我自己造就的可能性。 – Kunal 2010-05-05 04:50:35

+1

只要注意,Smarty並不完全安全(http://www.smarty.net/manual/en/language.function.php.php)。 – tadamson 2010-05-05 05:25:32

回答

4

當然..

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

編輯:

想不出什麼別的馬上。但是你應該知道你的範圍已經被破壞了,如果在同一個Template實例上渲染被多次調用過。

例如,如果您render('<?php $this->allowed_methods[] = "eval"; ?>') ..然後的Template該實例將有eval爲可接受的功能在接下來的渲染..;)

+2

聰明。即使你修復這種情況,也可能有其他的......可能更安全,而不是讓他們編寫PHP。 – mpen 2010-05-05 04:36:45

+0

啊,是的。我會過濾掉反撥操作員...你能想到其他什麼嗎? – Kunal 2010-05-05 04:38:16

+0

@Kunal - 請參閱我的編輯 – Matt 2010-05-05 04:53:27

1

我錯在思考這樣的事情會的工作?

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

如果你真的想這樣做,使用tokenizer解析源。我不推薦它。事實上,我不鼓勵它!

+0

+1你是對的。 'render(' */echo(「lol」);?>')'起作用。注意,Kunal,有許多方法來妥協:) – Matt 2010-05-05 05:09:25

+0

哈哈,指出。真正的模板語言。 – Kunal 2010-05-05 05:12:22

4

這不是一個好主意。即使你修復了直接的安全漏洞,毫無疑問你會錯過其他的漏洞。我想說,如果你真的想給用戶這種能力,可以使用實際的模板語言,如Smarty,或者自己寫。 PHP作爲內部使用的模板語言非常好,但並不是所有用戶都可以使用的開放語言。有很多方法可以被利用,即使你可以捕獲所有的東西,你也會做更多的工作,而不是編寫一個真正的模板引擎。

3

您可以在變量中使用{和}並運行任何函數。

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

另外,因爲您只是運行代碼,所以它可以訪問函數render可以訪問的所有變量。包括調用全局來修改幾乎任何地方的變量或者像$ _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(); ?>'; 
+0

忘記'$ {「f」}();'符號 - 好點。 – Matt 2010-05-05 05:05:34

1

如果您的用戶貢獻內容,而且這些用戶並不完全可信的,我建議你白名單而不是黑名單標記。

例如,考慮Stack Overflow允許的Markdown格式。它支持非常短的格式化選項列表,其他所有內容都被認爲是文本文本。大多數用戶更喜歡簡單的界面,而不是一個界面,上面寫着「編寫你想要的任何代碼!但要小心不要打破應用程序!」我的經驗法則是:允許用戶輸入數據和內容;絕不允許用戶輸入代碼。