CSRF protection

This commit is contained in:
Michael Save 2012-08-27 15:19:05 +10:00
parent 4a9d497a94
commit 6229b82a43
8 changed files with 82 additions and 44 deletions

View file

@ -686,6 +686,7 @@
$config['error']['404'] = _('Page not found.');
$config['error']['modexists'] = _('That mod <a href="?/users/%d">already exists</a>!');
$config['error']['invalidtheme'] = _('That theme doesn\'t exist!');
$config['error']['csrf'] = _('Invalid security token! Please go back and try again.');
/*
* =========================
@ -754,9 +755,6 @@
* Mod settings
* ====================
*/
// Server-side confirm button for actions like deleting posts, for when Javascript is disabled or the DOM isn't loaded.
$config['mod']['server-side_confirm'] = true;
// Whether or not to lock moderator sessions to the IP address that was logged in with.
$config['mod']['lock_ip'] = true;

View file

@ -86,11 +86,11 @@ function error($message, $priority = true) {
function loginForm($error=false, $username=false, $redirect=false) {
global $config;
die(Element('page.html', Array(
'index'=>$config['root'],
'title'=>_('Login'),
'config'=>$config,
'body'=>Element('login.html', Array(
die(Element('page.html', array(
'index' => $config['root'],
'title' => _('Login'),
'config' => $config,
'body' => Element('login.html', array(
'config'=>$config,
'error'=>$error,
'username'=>utf8tohtml($username),
@ -205,12 +205,13 @@ function truncate($body, $url, $max_lines = false, $max_chars = false) {
return $body;
}
function confirmLink($text, $title, $confirm, $href) {
global $config, $mod;
if ($config['mod']['server-side_confirm'])
return '<a onclick="if (confirm(\'' . htmlentities(addslashes($confirm)) . '\')) document.location=\'?/' . htmlentities(addslashes($href)) . '\';return false;" title="' . htmlentities($title) . '" href="?/confirm/' . $href . '">' . $text . '</a>';
else
return '<a onclick="return confirm(\'' . htmlentities(addslashes($confirm)) . '\')" title="' . htmlentities($title) . '" href="?/' . $href . '">' . $text . '</a>';
function secure_link_confirm($text, $title, $confirm_message, $href) {
global $config;
return '<a onclick="if (confirm(\'' . htmlentities(addslashes($confirm_message)) . '\')) document.location=\'?/' . htmlentities(addslashes($href . '/' . make_secure_link_token($href))) . '\';return false;" title="' . htmlentities($title) . '" href="?/' . $href . '">' . $text . '</a>';
}
function secure_link($href) {
return $href . '/' . make_secure_link_token($href);
}
class Post {
@ -264,15 +265,15 @@ class Post {
// Delete
if (hasPermission($config['mod']['delete'], $board['uri'], $this->mod))
$built .= ' ' . confirmLink($config['mod']['link_delete'], 'Delete', 'Are you sure you want to delete this?', $board['uri'] . '/delete/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_delete'], 'Delete', 'Are you sure you want to delete this?', $board['uri'] . '/delete/' . $this->id);
// Delete all posts by IP
if (hasPermission($config['mod']['deletebyip'], $board['uri'], $this->mod))
$built .= ' ' . confirmLink($config['mod']['link_deletebyip'], 'Delete all posts by IP', 'Are you sure you want to delete all posts by this IP address?', $board['uri'] . '/deletebyip/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip'], 'Delete all posts by IP', 'Are you sure you want to delete all posts by this IP address?', $board['uri'] . '/deletebyip/' . $this->id);
// Delete all posts by IP (global)
if (hasPermission($config['mod']['deletebyip_global'], $board['uri'], $this->mod))
$built .= ' ' . confirmLink($config['mod']['link_deletebyip_global'], 'Delete all posts by IP across all boards', 'Are you sure you want to delete all posts by this IP address, across all boards?', $board['uri'] . '/deletebyip/' . $this->id . '/global');
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip_global'], 'Delete all posts by IP across all boards', 'Are you sure you want to delete all posts by this IP address, across all boards?', $board['uri'] . '/deletebyip/' . $this->id . '/global');
// Ban
if (hasPermission($config['mod']['ban'], $board['uri'], $this->mod))
@ -362,15 +363,15 @@ class Thread {
// Mod controls (on posts)
// Delete
if (hasPermission($config['mod']['delete'], $board['uri'], $this->mod))
$built .= ' ' . confirmLink($config['mod']['link_delete'], 'Delete', 'Are you sure you want to delete this?', $board['uri'] . '/delete/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_delete'], 'Delete', 'Are you sure you want to delete this?', $board['uri'] . '/delete/' . $this->id);
// Delete all posts by IP
if (hasPermission($config['mod']['deletebyip'], $board['uri'], $this->mod))
$built .= ' ' . confirmLink($config['mod']['link_deletebyip'], 'Delete all posts by IP', 'Are you sure you want to delete all posts by this IP address?', $board['uri'] . '/deletebyip/' . $this->id);
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip'], 'Delete all posts by IP', 'Are you sure you want to delete all posts by this IP address?', $board['uri'] . '/deletebyip/' . $this->id);
// Delete all posts by IP (global)
if (hasPermission($config['mod']['deletebyip_global'], $board['uri'], $this->mod))
$built .= ' ' . confirmLink($config['mod']['link_deletebyip_global'], 'Delete all posts by IP across all boards', 'Are you sure you want to delete all posts by this IP address, across all boards?', $board['uri'] . '/deletebyip/' . $this->id . '/global');
$built .= ' ' . secure_link_confirm($config['mod']['link_deletebyip_global'], 'Delete all posts by IP across all boards', 'Are you sure you want to delete all posts by this IP address, across all boards?', $board['uri'] . '/deletebyip/' . $this->id . '/global');
// Ban
if (hasPermission($config['mod']['ban'], $board['uri'], $this->mod))
@ -393,16 +394,16 @@ class Thread {
if (hasPermission($config['mod']['bumplock'], $board['uri'], $this->mod))
if ($this->bumplocked)
$built .= ' <a title="Allow thread to be bumped" href="?/' . $board['uri'] . '/bumpunlock/' . $this->id . '">' . $config['mod']['link_bumpunlock'] . '</a>';
$built .= ' <a title="Allow thread to be bumped" href="?/' . secure_link($board['uri'] . '/bumpunlock/' . $this->id) . '">' . $config['mod']['link_bumpunlock'] . '</a>';
else
$built .= ' <a title="Prevent thread from being bumped" href="?/' . $board['uri'] . '/bumplock/' . $this->id . '">' . $config['mod']['link_bumplock'] . '</a>';
$built .= ' <a title="Prevent thread from being bumped" href="?/' . secure_link($board['uri'] . '/bumplock/' . $this->id) . '">' . $config['mod']['link_bumplock'] . '</a>';
// Lock
if (hasPermission($config['mod']['lock'], $board['uri'], $this->mod))
if ($this->locked)
$built .= ' <a title="Unlock thread" href="?/' . $board['uri'] . '/unlock/' . $this->id . '">' . $config['mod']['link_unlock'] . '</a>';
$built .= ' <a title="Unlock thread" href="?/' . secure_link($board['uri'] . '/unlock/' . $this->id) . '">' . $config['mod']['link_unlock'] . '</a>';
else
$built .= ' <a title="Lock thread" href="?/' . $board['uri'] . '/lock/' . $this->id . '">' . $config['mod']['link_lock'] . '</a>';
$built .= ' <a title="Lock thread" href="?/' . secure_link($board['uri'] . '/lock/' . $this->id) . '">' . $config['mod']['link_lock'] . '</a>';
if (hasPermission($config['mod']['move'], $board['uri'], $this->mod))
$built .= ' <a title="Move thread to another board" href="?/' . $board['uri'] . '/move/' . $this->id . '">' . $config['mod']['link_move'] . '</a>';

View file

@ -150,3 +150,9 @@ function create_pm_header() {
return $header;
}
function make_secure_link_token($uri) {
global $mod, $config;
return substr(sha1($config['cookies']['salt'] . '-' . $uri . '-' . $mod['id']), 0, 8);
}

View file

@ -60,7 +60,7 @@ function mod_login() {
}
function mod_confirm($request) {
mod_page(_('Confirm action'), 'mod/confirm.html', array('request' => $request));
mod_page(_('Confirm action'), 'mod/confirm.html', array('request' => $request, 'token' => make_secure_link_token($request)));
}
function mod_logout() {
@ -563,7 +563,7 @@ function mod_ban() {
error($config['error']['noaccess']);
if (!isset($_POST['ip'], $_POST['reason'], $_POST['length'], $_POST['board'])) {
mod_page(_('New ban'), 'mod/ban_form.html', array());
mod_page(_('New ban'), 'mod/ban_form.html', array('token' => make_secure_link_token('ban')));
return;
}
@ -883,10 +883,12 @@ function mod_move($originBoard, $postID) {
if (count($boards) <= 1)
error(_('Impossible to move thread; there is only one board.'));
mod_page(_('Move thread'), 'mod/move.html', array('post' => $postID, 'board' => $originBoard, 'boards' => $boards));
$security_token = make_secure_link_token($originBoard . '/move/' . $postID);
mod_page(_('Move thread'), 'mod/move.html', array('post' => $postID, 'board' => $originBoard, 'boards' => $boards, 'token' => $security_token));
}
function mod_ban_post($board, $delete, $post) {
function mod_ban_post($board, $delete, $post, $token = false) {
global $config, $mod;
if (!openBoard($board))
@ -895,6 +897,8 @@ function mod_ban_post($board, $delete, $post) {
if (!hasPermission($config['mod']['delete'], $board))
error($config['error']['noaccess']);
$security_token = make_secure_link_token($board . '/ban' . ($delete ? '&delete' : '') . '/' . $post);
$query = prepare(sprintf('SELECT `ip`, `thread` FROM `posts_%s` WHERE `id` = :id', $board));
$query->bindValue(':id', $post);
$query->execute() or error(db_error($query));
@ -903,7 +907,7 @@ function mod_ban_post($board, $delete, $post) {
$thread = $_post['thread'];
$ip = $_post['ip'];
if (isset($_POST['new_ban'], $_POST['reason'], $_POST['length'], $_POST['board'])) {
require_once 'inc/mod/ban.php';
@ -939,7 +943,8 @@ function mod_ban_post($board, $delete, $post) {
'post' => $post,
'board' => $board,
'delete' => (bool)$delete,
'boards' => listBoards()
'boards' => listBoards(),
'token' => $security_token
);
mod_page(_('New ban'), 'mod/ban_form.html', $args);