diff --git a/display.php b/display.php index 6c670ff..de6082d 100644 --- a/display.php +++ b/display.php @@ -5,7 +5,7 @@ require_once('./get_data.lib.php'); $queue = get_queue(); if ($queue) session_cache_limiter('nocache'); else session_cache_limiter('none'); -session_set_cookie_params(strtotime('+1 year'), '/', '.xxx.org.za', TRUE, TRUE); +session_set_cookie_params(array('lifetime' => strtotime('+1 year'), 'path' => '/', 'domain' => '.xxx.org.za', 'secure' => TRUE, 'httponly' => TRUE, 'samesite' => 'Lax')); session_start(); if (isset($_SESSION['editor'])) { $is_admin = TRUE; diff --git a/publish/addeditor.php b/publish/addeditor.php index a5c4c17..5a9bd64 100644 --- a/publish/addeditor.php +++ b/publish/addeditor.php @@ -4,9 +4,12 @@ if ( ! isset($_SERVER['HTTPS'])) { } require_once("../session.lib.php"); $added = FALSE; +$csrf_failed = FALSE; if (!isset($_SESSION['editor'])) { header("Location: https://xxx.org.za/publish/login"); return; +} else if (isset($_POST['adduser']) && !csrf_valid()) { + $csrf_failed = TRUE; } else if (isset($_POST['adduser'])) { require_once('../open_db.lib.php'); open_db('open'); @@ -48,8 +51,12 @@ if (!isset($_SESSION['editor'])) {

Added editor

+ +

Your session has expired. Please try again.

+
" method="post"> +
diff --git a/publish/edit.php b/publish/edit.php index f3acd09..a610714 100644 --- a/publish/edit.php +++ b/publish/edit.php @@ -77,6 +77,11 @@ if (isset($_POST['action'])) { if (!isset($action) && $id) { $action = 'edit'; } +// A POST without a valid CSRF token only gets a preview +if (isset($_POST['action']) && !csrf_valid()) { + $validate['csrf'] = 'Your session has expired. Please check the story, then send it again.'; + $action = 'preview'; +} switch ($action) { case 'submit': // to queue diff --git a/publish/index.php b/publish/index.php index 9a23d27..ea4774d 100644 --- a/publish/index.php +++ b/publish/index.php @@ -64,6 +64,12 @@ if (isset($_POST['action'])) else $action = NULL; +// A POST without a valid CSRF token only gets a preview +if ($action && !csrf_valid()) { + $validate['csrf'] = 'Your session has expired. Please check your story, then send it again.'; + $action = 'preview'; +} + switch ($action) { case 'post': // post to database case 'save': // post to queue diff --git a/publish/login.php b/publish/login.php index 3a7fe43..da88274 100644 --- a/publish/login.php +++ b/publish/login.php @@ -9,6 +9,8 @@ if (isset($_SESSION['editor'])) { session_write_close(); header("Location: https://xxx.org.za/publish/edit"); return; +} else if (isset($_POST['login']) && !csrf_valid()) { + $denied = TRUE; } else if (isset($_POST['login'])) { include_once('../open_db.lib.php'); $table = "editors"; @@ -80,6 +82,7 @@ if ($denied) ?> " method="post"> +
diff --git a/publish/logout.php b/publish/logout.php index 9460fdf..644bec1 100644 --- a/publish/logout.php +++ b/publish/logout.php @@ -7,6 +7,11 @@ include_once('../session.lib.php'); if ( ! isset($_SERVER['HTTPS'])) { header('Location: https://' . $_SERVER["SERVER_NAME"] . $_SERVER['REQUEST_URI']); } +// Only log out from our own logout link, which carries the CSRF token +if (!csrf_valid($_GET['token'] ?? '')) { + header("Location: https://xxx.org.za/"); + exit; +} if (isset($_SESSION['editor'])) { if (isset($_COOKIE[session_name()])) { setcookie(session_name(), '', strtotime('-1 day'), '/'); diff --git a/publish/write.inc.php b/publish/write.inc.php index 5f56007..8ef0548 100644 --- a/publish/write.inc.php +++ b/publish/write.inc.php @@ -21,6 +21,7 @@ if (count($validate) !== 0) { ?> +
diff --git a/session.lib.php b/session.lib.php index 022f36c..07c6055 100644 --- a/session.lib.php +++ b/session.lib.php @@ -6,7 +6,7 @@ $is_admin = FALSE; if (isset($_SERVER['HTTPS'])) { session_cache_limiter('private'); - session_set_cookie_params(strtotime('+1 year'), '/', '.xxx.org.za', TRUE, TRUE); + session_set_cookie_params(array('lifetime' => strtotime('+1 year'), 'path' => '/', 'domain' => '.xxx.org.za', 'secure' => TRUE, 'httponly' => TRUE, 'samesite' => 'Lax')); session_start(); if (isset($_SESSION['editor'])) { @@ -72,7 +72,7 @@ function create_persistent_login($username) { open_db('close'); // Create cookie with key - setcookie('PersistentAuth', "$username|$key", $validity, '/', '.xxx.org.za', TRUE, TRUE); + setcookie('PersistentAuth', "$username|$key", array('expires' => $validity, 'path' => '/', 'domain' => '.xxx.org.za', 'secure' => TRUE, 'httponly' => TRUE, 'samesite' => 'Lax')); return true; } @@ -97,7 +97,28 @@ function delete_persistent_login() { open_db('close'); // Delete cookie - setcookie("PersistentAuth", false, strtotime('-1 year'), '/', '.xxx.org.za', TRUE, TRUE); + setcookie("PersistentAuth", '', array('expires' => strtotime('-1 year'), 'path' => '/', 'domain' => '.xxx.org.za', 'secure' => TRUE, 'httponly' => TRUE, 'samesite' => 'Lax')); return true; } + +/* + * CSRF protection: every form that changes something carries a random + * token from the session, and the handler checks it before acting. + */ +function csrf_token() { + if (empty($_SESSION['csrf_token']) || !is_string($_SESSION['csrf_token'])) + $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); + return $_SESSION['csrf_token']; +} + +function csrf_field() { + return ''; +} + +function csrf_valid($token = NULL) { + if ($token === NULL) + $token = $_POST['csrf_token'] ?? ''; + return is_string($token) && isset($_SESSION['csrf_token']) && + hash_equals((string)$_SESSION['csrf_token'], $token); +} diff --git a/sidebar.inc.php b/sidebar.inc.php index 59d10ca..35aa597 100644 --- a/sidebar.inc.php +++ b/sidebar.inc.php @@ -29,7 +29,7 @@ if (preg_match("#^(/$|/index|/article)#", $_SERVER['REQUEST_URI'])) {
  • Add/Edit
  • Add editor'."\n"; - echo '
  • Logout
  • '."\n"; + echo '
  • Logout
  • '."\n"; } //echo '
  • Publishing policy
  • '; ?>