Add CSRF protection to admin forms

This commit is contained in:
Jordan Milne 2014-03-20 20:18:56 -03:00 committed by Ricky Ramirez
parent 45afdb4bba
commit d25dec3563
4 changed files with 46 additions and 3 deletions

View File

@ -31,10 +31,14 @@ function ajaxFunc() {
if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'get') {
jsonQuery();
} else if ($_REQUEST['type'] == 'settings') {
updateSettings();
} else {
addEdit();
checkCsrfToken();
if ($_REQUEST['type'] == 'settings') {
updateSettings();
} else {
addEdit();
}
}
}
@ -100,6 +104,9 @@ function jsonQuery() {
function jsonError($error) {
jsonForAjax(Array('error' => $error));
// Die even if not an AJAX request, should this call error_log() ?
die();
}
function jsonForAjax($arr) {
@ -213,6 +220,28 @@ function pickFilename($arr=Array(), $ext='.jpg') {
return $filename;
}
function checkCsrfToken() {
if(!constant_time_compare(CSRF_TOKEN, $_POST['csrf_token'])) {
jsonError('Invalid CSRF token');
}
}
// based on version from utils.py
function constant_time_compare($expected, $actual)
{
$actual_len = strlen($actual);
$expected_len = strlen($expected);
$res = $actual_len ^ $expected_len;
if ($expected_len > 0) {
for ($i = 0; $i < $actual_len; ++$i) {
$res |= ord($expected[$i % $expected_len]) ^ ord($actual[$i]);
}
}
return $res === 0;
}
function sanitize_file_name( $filename ) {
$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}");
$filename = str_replace($special_chars, '', $filename);

View File

@ -17,6 +17,9 @@
<link rel="stylesheet" href="./style.css" type="text/css" />
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700,300italic' rel='stylesheet' type='text/css'>
<script type="text/javascript">
window.CSRF_TOKEN = <?= json_encode(CSRF_TOKEN, JSON_HEX_TAG) ?>;
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" type="text/javascript"></script>
<script src="js/bootstrap.min.js" type="text/javascript"></script>
<script src="js/jquery-ui.min.js" type="text/javascript"></script>
@ -53,6 +56,7 @@
<form action="" enctype="multipart/form-data" method="post" class="well form-horizontal" role="form">
<input type="hidden" name="type" value="videos" />
<input type="hidden" name="csrf_token" value="<?= htmlentities(CSRF_TOKEN) ?>" />
<div class="form-group row">
<div class="col-lg-5">
<div class="row">
@ -190,6 +194,7 @@
<form action="" enctype="multipart/form-data" method="post" class="well form-horizontal" role="form">
<input type="hidden" name="type" value="skins" />
<input type="hidden" name="csrf_token" value="<?= htmlentities(CSRF_TOKEN) ?>" />
<div class="form-group row">
<div class="col-lg-5">
<div class="row">
@ -338,6 +343,7 @@
<form action="" enctype="multipart/form-data" method="post" class="well form-horizontal" role="form">
<input type="hidden" name="type" value="channels" />
<input type="hidden" name="csrf_token" value="<?= htmlentities(CSRF_TOKEN) ?>" />
<div class="form-group row">
<div class="col-lg-5">
<div class="row">
@ -528,6 +534,7 @@
<hr />
<form id="general-settings" action="" enctype="multipart/form-data" method="post" class="well form-horizontal" role="form">
<input type="hidden" name="type" value="settings" />
<input type="hidden" name="csrf_token" value="<?= htmlentities(CSRF_TOKEN) ?>" />
<div class="form-group row">
<div class="col-lg-4">
<div class="input-group">
@ -578,6 +585,7 @@
<h3>Default Channels</h3>
<form id="default-channels" action="" enctype="multipart/form-data" method="post" class="well form-horizontal" role="form">
<input type="hidden" name="type" value="default-channels" />
<input type="hidden" name="csrf_token" value="<?= htmlentities(CSRF_TOKEN) ?>" />
<div class="form-group row control-group">
<div class="col-lg-6">
<div class="input-group">
@ -632,6 +640,7 @@
<h3>Recommended Channels</h3>
<form id="recommended-channels" action="" enctype="multipart/form-data" method="post" class="well form-horizontal" role="form">
<input type="hidden" name="type" value="recommended-channels" />
<input type="hidden" name="csrf_token" value="<?= htmlentities(CSRF_TOKEN) ?>" />
<div class="form-group row control-group">
<div class="col-lg-6">
<div class="input-group">

View File

@ -411,6 +411,7 @@ $.each(channelTypes, function(i, channelType) {
feed = JSON.stringify(feed);
postData = {
'csrf_token': window.CSRF_TOKEN,
'ajax' : true,
'type' : 'settings',
};

View File

@ -6,6 +6,10 @@ define('UPLOAD_URL', 'uploads/');
define('AWS_BUCKET', 'reddittv');
define('USE_SQLITE', false);
// Since user auth is done via .htpasswd, there's no need to support per-user
// CSRF tokens. Replace this when / if user auth gets added to admin panel.
define('CSRF_TOKEN', 'REPLACE_ME');
// Include RedbeanPHP
require_once(dirname(__FILE__).'/rb.php');