Тема: требуется замена confirm_referer()
Чтобы понять, что функция confirm_referer PunBB 1.2.14 является очень хилой "защитой" не надо быть большим специалистом.
Есть по-крайней мере два железных аргумента чтобы переделать ее или полностью отказаться в пользу других проверок.
- Атака CSRF (см. Cross-Site Request Forgery и CSRF/XSRF FAQ). Злоумышленник может воспользоваться формой от вашего имени, пока сессия не закрыта.
(update: ему все-таки придется подделать http_referer)
- Непригодность при использовании ЧПУ, т.к. HTTP_REFERER будет другим, но при этом должен работать и старый вариант.
В первой ссылке есть пример как усилить защиту форм через добавление специального постоянно меняющегося ключа в параметры сессии. Он же вставляется в скрытые поля форм, затем одно сравнивается с другим $_POST['token'] == $_SESSION['token']).
Якобы на сегодняшний день это самый распостраненный способ защиты от атаки от чужого имени.
Но во втором источнике сказано, что такая защита будет работать если у вас (как у потенциальной жертвы) браузер с самыми новыми патчами и все дыры в плагинах закрыты. На сегодняшний день дыры очень даже возможны в Acrobat и Flash плагинах. Через них злодеи могут добраться до параметров вашей сессии и тогда все насмарку. Не очень понятно, но надо быть начеку!
Что делают разработчики PunBB в этом плане? В текущей developer версии 1.3 уже нет функции confirm_referer. Вместо этого используется проверка на изменяющийся ключик с именем csrf_token. Этот ключик записывается в параметры соединения, но не через механизм сессий PHP, а в таблице online. (update: см. ниже - ключик не меняется)
Рекомендую к изучению:
- текущие билды PunBB 1.3 dev.punbb.org/browser/branches/punbb-1.3-dev
- сессии - правильное использование php.spb.ru/session.html
статья хорошая, но устарел синтаксис. сейчас рекомендуется использовать $_SESSION
Добавлено спустя 1 час 22 минуты 37 секунд:
вот набросал такую тестовую штучку. вызов confirm_referer() делается как и прежде, но теперь проверка усиливается через скрытое поле в форме и параметр сессии.
Теперь ЧПУ не помешает, атака тоже не должна пройти.
файл test.php:
<?php
session_start();
// I immitate PunBB environment. Put YOUR forum root into this string:
$pun_config['o_base_url'] = 'http://mypunbb.ru';
$lang_common['Bad referrer'] = 'Bad HTTP_REFERER. You were referred to this page from an unauthorized source. If the problem persists please make sure that \'Base URL\' is correctly set in Admin/Options and that you are visiting the forum by navigating to that URL. More information regarding the referrer check can be found in the PunBB documentation.';
function unescape($str)
{
return (get_magic_quotes_gpc() == 1) ? stripslashes($str) : $str;
}
function message($s)
{
die($s);
}
function compose_ref()
{
global $pun_config;
$ref = md5(uniqid(rand(), TRUE));
$key = md5(str_replace('www.', '', $pun_config['o_base_url']).$_SERVER['SCRIPT_NAME']);
$_SESSION[$key] = $ref . ':' . time();
return $ref;
}
//
// New version of confirm_referer()
// Do not avoid SEF URLs and CSRF attacks!
//
function confirm_referer($script)
{
global $pun_config, $lang_common;
// HTTP_REFERER is the same domain
if (!preg_match('#^'.preg_quote(str_replace('www.', '', $pun_config['o_base_url']), '#').'#i', str_replace('www.', '', (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''))))
message($lang_common['Bad referrer']);
$key = md5(str_replace('www.', '', $pun_config['o_base_url']).'/'.$script);
// there are secret field and page was visited in this session
if (!isset($_POST['ref']) || !isset($_SESSION[$key]))
message($lang_common['Bad referrer']);
list($ref, $time) = explode(':', $_SESSION[$key]);
// secret field is valid and time is not over (5 minutes)
if ($_POST['ref'] != $ref || time() - $time > 300)
message($lang_common['Bad referrer']);
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Test of new confirm_referer</title>
</head>
<body>
<h1>Test of new confirm_referer()</h1>
<?php
if (isset($_POST['in']))
{
confirm_referer('test.php');
$in = unescape($_POST['in']);
echo 'OK. Your text is: "'.htmlspecialchars($in).'"<br /><br />'."\n";
}
else
$in = '';
?>
<form action="test.php" method="post">
<input type="hidden" name="ref" value="<?php echo compose_ref() ?>" />
<input type="text" name="in" value="<?php echo htmlspecialchars($in) ?>" />
<input type="submit" value="OK" />
</form>
</html>прошу высказываться