Использование PHP в качестве языка шаблонов - PullRequest
3 голосов
/ 05 мая 2010

Редактировать: Хорошие моменты повсюду, выделенный язык шаблонов - это, безусловно, путь. Спасибо!

Я написал этот быстрый класс для создания шаблонов с помощью 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 ...

Ответы [ 5 ]

4 голосов
/ 05 мая 2010

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

4 голосов
/ 05 мая 2010

Конечно ..

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

Edit:

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

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

3 голосов
/ 05 мая 2010

вы можете использовать {и} с переменными и запускать любую функцию.

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

Кроме того, поскольку вы просто запускаете код, он будет иметь доступ ко всем переменным, к которым может обращаться функция render. включая вызов 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(); ?>';
1 голос
/ 05 мая 2010

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

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

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

1 голос
/ 05 мая 2010

Я ошибаюсь, думая, что что-то подобное будет работать?

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...