diff --git a/admin/functions.php b/admin/functions.php index 3b22d56..ac94d56 100755 --- a/admin/functions.php +++ b/admin/functions.php @@ -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); diff --git a/admin/index.php b/admin/index.php index 6a22758..344b49e 100755 --- a/admin/index.php +++ b/admin/index.php @@ -17,6 +17,9 @@ + @@ -53,6 +56,7 @@
+
@@ -190,6 +194,7 @@ +
@@ -338,6 +343,7 @@ +
@@ -528,6 +534,7 @@
+
@@ -578,6 +585,7 @@

Default Channels

+
@@ -632,6 +640,7 @@

Recommended Channels

+
diff --git a/admin/js/admin.js b/admin/js/admin.js index 4e883c6..010c5a0 100755 --- a/admin/js/admin.js +++ b/admin/js/admin.js @@ -411,6 +411,7 @@ $.each(channelTypes, function(i, channelType) { feed = JSON.stringify(feed); postData = { + 'csrf_token': window.CSRF_TOKEN, 'ajax' : true, 'type' : 'settings', }; diff --git a/db/config-local.php.sample b/db/config-local.php.sample index 165251f..83a2846 100644 --- a/db/config-local.php.sample +++ b/db/config-local.php.sample @@ -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');