Add CSRF protection to every form that changes data.
The logout link carries the token too, and the cookies get SameSite=Lax.
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ require_once('./get_data.lib.php');
|
|||||||
$queue = get_queue();
|
$queue = get_queue();
|
||||||
if ($queue) session_cache_limiter('nocache');
|
if ($queue) session_cache_limiter('nocache');
|
||||||
else session_cache_limiter('none');
|
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();
|
session_start();
|
||||||
if (isset($_SESSION['editor'])) {
|
if (isset($_SESSION['editor'])) {
|
||||||
$is_admin = TRUE;
|
$is_admin = TRUE;
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ if ( ! isset($_SERVER['HTTPS'])) {
|
|||||||
}
|
}
|
||||||
require_once("../session.lib.php");
|
require_once("../session.lib.php");
|
||||||
$added = FALSE;
|
$added = FALSE;
|
||||||
|
$csrf_failed = FALSE;
|
||||||
if (!isset($_SESSION['editor'])) {
|
if (!isset($_SESSION['editor'])) {
|
||||||
header("Location: https://xxx.org.za/publish/login");
|
header("Location: https://xxx.org.za/publish/login");
|
||||||
return;
|
return;
|
||||||
|
} else if (isset($_POST['adduser']) && !csrf_valid()) {
|
||||||
|
$csrf_failed = TRUE;
|
||||||
} else if (isset($_POST['adduser'])) {
|
} else if (isset($_POST['adduser'])) {
|
||||||
require_once('../open_db.lib.php');
|
require_once('../open_db.lib.php');
|
||||||
open_db('open');
|
open_db('open');
|
||||||
@@ -48,8 +51,12 @@ if (!isset($_SESSION['editor'])) {
|
|||||||
<?php if ($added) { ?>
|
<?php if ($added) { ?>
|
||||||
<p>Added editor <?= form_value($username) ?></p>
|
<p>Added editor <?= form_value($username) ?></p>
|
||||||
<?php } else { ?>
|
<?php } else { ?>
|
||||||
|
<?php if ($csrf_failed) { ?>
|
||||||
|
<p><strong>Your session has expired. Please try again.</strong></p>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
<form action="<?= form_value("https://xxx.org.za".$_SERVER['REQUEST_URI']) ?>" method="post">
|
<form action="<?= form_value("https://xxx.org.za".$_SERVER['REQUEST_URI']) ?>" method="post">
|
||||||
|
<?= csrf_field() ?>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
|
|
||||||
<label for="username">Username: </label>
|
<label for="username">Username: </label>
|
||||||
|
|||||||
@@ -77,6 +77,11 @@ if (isset($_POST['action'])) {
|
|||||||
if (!isset($action) && $id) {
|
if (!isset($action) && $id) {
|
||||||
$action = 'edit';
|
$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) {
|
switch ($action) {
|
||||||
case 'submit': // to queue
|
case 'submit': // to queue
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ if (isset($_POST['action']))
|
|||||||
else
|
else
|
||||||
$action = NULL;
|
$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) {
|
switch ($action) {
|
||||||
case 'post': // post to database
|
case 'post': // post to database
|
||||||
case 'save': // post to queue
|
case 'save': // post to queue
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ if (isset($_SESSION['editor'])) {
|
|||||||
session_write_close();
|
session_write_close();
|
||||||
header("Location: https://xxx.org.za/publish/edit");
|
header("Location: https://xxx.org.za/publish/edit");
|
||||||
return;
|
return;
|
||||||
|
} else if (isset($_POST['login']) && !csrf_valid()) {
|
||||||
|
$denied = TRUE;
|
||||||
} else if (isset($_POST['login'])) {
|
} else if (isset($_POST['login'])) {
|
||||||
include_once('../open_db.lib.php');
|
include_once('../open_db.lib.php');
|
||||||
$table = "editors";
|
$table = "editors";
|
||||||
@@ -80,6 +82,7 @@ if ($denied)
|
|||||||
?>
|
?>
|
||||||
|
|
||||||
<form action="<?php echo form_value("https://xxx.org.za".$_SERVER['REQUEST_URI']); ?>" method="post">
|
<form action="<?php echo form_value("https://xxx.org.za".$_SERVER['REQUEST_URI']); ?>" method="post">
|
||||||
|
<?= csrf_field() ?>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
|
|
||||||
<label for="username">Username: </label>
|
<label for="username">Username: </label>
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ include_once('../session.lib.php');
|
|||||||
if ( ! isset($_SERVER['HTTPS'])) {
|
if ( ! isset($_SERVER['HTTPS'])) {
|
||||||
header('Location: https://' . $_SERVER["SERVER_NAME"] . $_SERVER['REQUEST_URI']);
|
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($_SESSION['editor'])) {
|
||||||
if (isset($_COOKIE[session_name()])) {
|
if (isset($_COOKIE[session_name()])) {
|
||||||
setcookie(session_name(), '', strtotime('-1 day'), '/');
|
setcookie(session_name(), '', strtotime('-1 day'), '/');
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ if (count($validate) !== 0) {
|
|||||||
?>
|
?>
|
||||||
|
|
||||||
<form id="write" method="post" enctype="multipart/form-data" onsubmit="return review()" action="">
|
<form id="write" method="post" enctype="multipart/form-data" onsubmit="return review()" action="">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
|
|
||||||
|
|||||||
+24
-3
@@ -6,7 +6,7 @@ $is_admin = FALSE;
|
|||||||
|
|
||||||
if (isset($_SERVER['HTTPS'])) {
|
if (isset($_SERVER['HTTPS'])) {
|
||||||
session_cache_limiter('private');
|
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();
|
session_start();
|
||||||
|
|
||||||
if (isset($_SESSION['editor'])) {
|
if (isset($_SESSION['editor'])) {
|
||||||
@@ -72,7 +72,7 @@ function create_persistent_login($username) {
|
|||||||
open_db('close');
|
open_db('close');
|
||||||
|
|
||||||
// Create cookie with key
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,28 @@ function delete_persistent_login() {
|
|||||||
open_db('close');
|
open_db('close');
|
||||||
|
|
||||||
// Delete cookie
|
// 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;
|
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 '<input type="hidden" name="csrf_token" value="'.csrf_token().'" />';
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ if (preg_match("#^(/$|/index|/article)#", $_SERVER['REQUEST_URI'])) {
|
|||||||
<li><a href="/publish">Add</a>/<a href="/publish/edit" class="sidelink">Edit</a></li>
|
<li><a href="/publish">Add</a>/<a href="/publish/edit" class="sidelink">Edit</a></li>
|
||||||
<?php if (isset($is_admin) && $is_admin) {
|
<?php if (isset($is_admin) && $is_admin) {
|
||||||
echo ' <li><a href="/publish/addeditor">Add editor</a></li>'."\n";
|
echo ' <li><a href="/publish/addeditor">Add editor</a></li>'."\n";
|
||||||
echo ' <li><a href="/publish/logout">Logout</a></li>'."\n";
|
echo ' <li><a href="/publish/logout?token='.csrf_token().'">Logout</a></li>'."\n";
|
||||||
}
|
}
|
||||||
//echo ' <li><a href="/policy">Publishing policy</a></li>';
|
//echo ' <li><a href="/policy">Publishing policy</a></li>';
|
||||||
?>
|
?>
|
||||||
|
|||||||
Reference in New Issue
Block a user