2010-05-05 3 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

Вы * действительно * не должны этого делать. Есть тысячи способов вызвать проблемы, которые вы неизбежно пропустите. Попытка блокировать все и каждого из них в любом случае сделает шаблоны бесполезными. –

+0

Понял, и понял, как много. Это действительно только для внутреннего использования, но задавалось вопросом о возможностях эксплойта для моего собственного назидания. – Kunal

+1

Просто отметить, Smarty не совсем безопасен (http://www.smarty.net/manual/en/language.function.php.php). – tadamson

ответ

4

Конечно ..

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

Edit:

не могу думать ни о чем прямо сейчас. Но вы должны знать, что ваша область скомпрометирована. IF-рендер вызывается более одного раза на одном и том же экземпляре Template.

Например, если вы render('<?php $this->allowed_methods[] = "eval"; ?>') .. то, что экземпляр Template будет eval как приемлемая функция для следующего рендеринга ..;)

+2

Умный. И даже если вы исправите этот случай, есть, вероятно, другие ... вероятно, безопаснее только * не * позволить им писать PHP. – mpen

+0

А, да. Я отфильтрую операторов backtick ... можете ли вы придумать что-нибудь еще? – Kunal

+0

@ Kunal - см. Мое редактирование – Matt

1

я неправ, думая, что-то, как это будет работать?

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

Если вы действительно хотите сделать что-то вроде этого, используйте tokenizer разобрать источник. Однако я не рекомендую. На самом деле, я препятствую этому!

+0

+1 ты прав. 'render (' */echo (" lol ");?>')' работает. Обратите внимание, Кунал, есть много способов, чтобы скомпрометировать это :) – Matt

+0

Ха-ха, отметил. Реальный язык шаблонов. – Kunal

4

Это не очень хорошая идея. Даже если вы исправите немедленные дыры в безопасности, несомненно, будут другие, которых вы пропустили. Я бы сказал, если вы действительно хотите предоставить пользователям эту возможность, используйте фактический язык шаблонов, такой как Smarty, или напишите свой собственный. PHP отлично подходит как язык шаблонов для внутреннего использования, но не как открытый, который могут использовать все ваши пользователи. Есть только так много способов, которыми это может быть использовано, даже если вы можете их поймать, вы сделали бы больше работы, чем писать реальный механизм шаблонов.

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(); ?>'; 
+0

Забыл о '$ {" f "}();' нотация - хорошая точка. – Matt

1

Если пользователи предоставляют данные, и эти пользователи не являются абсолютно надежными, я бы рекомендовал белый список вместо чёрного списка разметки.

Например, рассмотрите форматирование Markdown, разрешенное переполнением стека. Он поддерживает очень короткий список параметров форматирования, а все остальное считается литералом. Большинство пользователей счастливы с простым интерфейсом, а не с интерфейсом, который говорит «Напишите любой код, который вы хотите! Но будьте осторожны, чтобы не разорвать приложение!»

Мое эмпирическое правило: позволяет пользователям вводить данные и содержимое; никогда не позволяют пользователям вводить код.

Смежные вопросы