Add CSRF stateless protection to forms
This commit is contained in:
parent
ba50bbba2c
commit
c82952de75
|
@ -8,6 +8,14 @@ class Users
|
|||
{
|
||||
protected ?stdClass $current = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (!session_id()) {
|
||||
// Protect the cookie : CSRF/JS stealing the cookie
|
||||
session_set_cookie_params(['samesite' => 'Lax', 'httponly' => true]);
|
||||
}
|
||||
}
|
||||
|
||||
static public function generatePassword(): string
|
||||
{
|
||||
$password = base64_encode(random_bytes(16));
|
||||
|
|
49
www/_inc.php
49
www/_inc.php
|
@ -25,21 +25,16 @@ if (!file_exists(DB_FILE)) {
|
|||
$db->exec(file_get_contents(__DIR__ . '/../schema.sql'));
|
||||
|
||||
if (!LDAP::enabled()) {
|
||||
@session_start();
|
||||
$users = new Users;
|
||||
$p = 'karadavdemo';
|
||||
$users->create('demo', $p, 10, true);
|
||||
$_SESSION['install_password'] = $p;
|
||||
$users->login('demo', $p);
|
||||
$_SESSION['install_password'] = $p;
|
||||
}
|
||||
|
||||
$db->exec('END;');
|
||||
}
|
||||
|
||||
if (isset($_COOKIE[session_name()]) && !isset($_SESSION)) {
|
||||
@session_start();
|
||||
}
|
||||
|
||||
function html_head(string $title): void
|
||||
{
|
||||
$title = htmlspecialchars($title);
|
||||
|
@ -102,3 +97,45 @@ function http_log(string $message, ...$params): void
|
|||
file_put_contents(LOG_FILE, $msg, FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
function html_csrf()
|
||||
{
|
||||
$expire = time() + 1800;
|
||||
$random = random_bytes(10);
|
||||
$action = $_SERVER['REQUEST_URI'];
|
||||
$token = hash_hmac('sha256', $expire . $random . $action, STORAGE_PATH . session_id());
|
||||
|
||||
return sprintf('<input type="hidden" name="_c_" value="%s:%s:%s" />', $token, base64_encode($random), $expire);
|
||||
}
|
||||
|
||||
function csrf_check(): bool
|
||||
{
|
||||
if (empty($_POST['_c_'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$verify = strtok($_POST['_c_'], ':');
|
||||
$random = base64_decode(strtok(':'));
|
||||
$expire = strtok(false);
|
||||
|
||||
if ($expire < time()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$action = $_SERVER['REQUEST_URI'];
|
||||
|
||||
$token = hash_hmac('sha256', $expire . $random . $action, STORAGE_PATH . session_id());
|
||||
|
||||
return hash_equals($token, $verify);
|
||||
}
|
||||
|
||||
function html_csrf_error()
|
||||
{
|
||||
if (empty($_POST['_c_'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!csrf_check()) {
|
||||
echo '<p class="error">Sorry, but the form expired, please submit it again.</p>';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ if (empty($_GET['nc']) && $users->current()) {
|
|||
|
||||
$error = 0;
|
||||
|
||||
if (!empty($_POST['login']) && !empty($_POST['password'])) {
|
||||
if (!empty($_POST['login']) && !empty($_POST['password']) && csrf_check()) {
|
||||
if ($users->login($_POST['login'], $_POST['password'])) {
|
||||
$url = null;
|
||||
|
||||
|
@ -55,9 +55,11 @@ echo '
|
|||
|
||||
if (isset($_GET['nc'])) {
|
||||
printf('<input type="hidden" name="nc" value="%s" />', htmlspecialchars($_GET['nc']));
|
||||
echo '<p class="info">The NextCloud app is trying to access your data. Please login to continue.</p>';
|
||||
echo '<p class="info">An external application is trying to access your data. Please login to continue and allow access.</p>';
|
||||
}
|
||||
|
||||
echo html_csrf();
|
||||
|
||||
echo '
|
||||
<fieldset>
|
||||
<legend>Login</legend>
|
||||
|
|
|
@ -31,12 +31,12 @@ elseif (isset($_GET['create']) && !$ldap) {
|
|||
$create = true;
|
||||
}
|
||||
|
||||
if ($create && !empty($_POST['create']) && !empty($_POST['login']) && !empty($_POST['password'])) {
|
||||
if ($create && !empty($_POST['create']) && !empty($_POST['login']) && !empty($_POST['password']) && csrf_check()) {
|
||||
$users->create(trim($_POST['login']), trim($_POST['password']));
|
||||
header('Location: ' . WWW_URL . 'users.php');
|
||||
exit;
|
||||
}
|
||||
elseif ($edit && !empty($_POST['save']) && !empty($_POST['login'])) {
|
||||
elseif ($edit && !empty($_POST['save']) && !empty($_POST['login']) && csrf_check()) {
|
||||
if (!$ldap && empty($_POST['is_admin']) && $user->id == $me->id) {
|
||||
die("You cannot remove yourself from admins, ask another admin to do it.");
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ elseif ($edit && !empty($_POST['save']) && !empty($_POST['login'])) {
|
|||
header('Location: ' . WWW_URL . 'users.php');
|
||||
exit;
|
||||
}
|
||||
elseif ($delete && !empty($_POST['delete'])) {
|
||||
elseif ($delete && !empty($_POST['delete']) && csrf_check()) {
|
||||
$users->delete($user);
|
||||
header('Location: ' . WWW_URL . 'users.php');
|
||||
exit;
|
||||
|
@ -64,9 +64,13 @@ elseif ($delete && !empty($_POST['delete'])) {
|
|||
|
||||
html_head('Manage users');
|
||||
|
||||
html_csrf_error();
|
||||
|
||||
if ($create) {
|
||||
$csrf = html_csrf();
|
||||
echo <<<EOF
|
||||
<form method="post" action="">
|
||||
{$csrf}
|
||||
<fieldset>
|
||||
<legend>Create a new user</legend>
|
||||
<dl>
|
||||
|
@ -77,14 +81,17 @@ if ($create) {
|
|||
<dd><input type="submit" name="create" value="Create" /></dd>
|
||||
</dl>
|
||||
</fieldset>
|
||||
</form>
|
||||
EOF;
|
||||
}
|
||||
elseif ($edit) {
|
||||
$csrf = html_csrf();
|
||||
$login = htmlspecialchars($user->login);
|
||||
$is_admin = $user->is_admin ? 'checked="checked"' : '';
|
||||
$quota = $user ? round($user->quota / 1024 / 1024) : DEFAULT_QUOTA;
|
||||
|
||||
echo '<form method="post" action="">
|
||||
' . $csrf . '
|
||||
<fieldset>
|
||||
<legend>Edit user</legend>
|
||||
<dl>';
|
||||
|
@ -106,17 +113,21 @@ elseif ($edit) {
|
|||
<!--<dd>Set to -1 to have unlimited space</dd>-->
|
||||
<dd><input type="submit" name="save" value="Save" /></dd>
|
||||
</dl>
|
||||
</fieldset>';
|
||||
</fieldset>
|
||||
</form>';
|
||||
}
|
||||
elseif ($delete) {
|
||||
$csrf = html_csrf();
|
||||
$login = htmlspecialchars($user->login);
|
||||
echo <<<EOF
|
||||
<form method="post" action="">
|
||||
{$csrf}
|
||||
<fieldset>
|
||||
<legend>Delete user</legend>
|
||||
<h2>Do you want to delete the user "{$login}" and all their files?</h2>
|
||||
<dd><input type="submit" name="delete" value="Yes, delete" /></dd>
|
||||
</fieldset>
|
||||
</form>
|
||||
EOF;
|
||||
}
|
||||
else {
|
||||
|
|
Loading…
Reference in a new issue