cool new CSRF protection system

This commit is contained in:
flash 2018-10-02 21:16:42 +02:00
parent 5097cb812a
commit c21d1b65ac
25 changed files with 276 additions and 124 deletions

View file

@ -67,6 +67,26 @@ body {
&--legacy {
--background-color: #fbeeff;
}
&--bg-blend {
background-blend-mode: multiply;
}
/*&--bg-slide {
}*/
&--bg-cover {
background-repeat: no-repeat;
}
&--bg-stretch {
background-size: 100% 100%;
background-repeat: no-repeat;
}
&--bg-tile {
background-repeat: repeat;
}
}
// Input elements

View file

@ -30,6 +30,7 @@ require_once 'src/audit_log.php';
require_once 'src/changelog.php';
require_once 'src/colour.php';
require_once 'src/comments.php';
require_once 'src/csrf.php';
require_once 'src/general.php';
require_once 'src/git.php';
require_once 'src/manage.php';
@ -261,10 +262,11 @@ MIG;
tpl_add_function('vsprintf', true);
tpl_add_function('perms_check', true);
tpl_add_function('bg_settings', true, 'user_background_settings_strings');
tpl_add_function('csrf', true, 'csrf_html');
tpl_add_function('git_commit_hash');
tpl_add_function('git_branch');
tpl_add_function('csrf_token', false, 'tmp_csrf_token');
tpl_add_function('csrf_token');
tpl_add_function('startup_time', false, function (float $time = MSZ_STARTUP) {
return microtime(true) - $time;
});
@ -301,6 +303,8 @@ MIG;
}
}
csrf_init('soapsoapsoap', empty($userDisplayInfo) ? ip_remote_address() : $_COOKIE['msz_sid']);
$privateInfo = $app->getPrivateInfo();
if (!$misuzuBypassLockdown && $privateInfo['enabled'] && !$app->hasActiveSession()) {

View file

@ -44,7 +44,7 @@ switch ($authMode) {
return;
}
if (isset($_GET['s']) && tmp_csrf_verify($_GET['s'])) {
if (csrf_verify('logout', $_GET['s'] ?? '')) {
set_cookie_m('uid', '', -3600);
set_cookie_m('sid', '', -3600);
user_session_delete($app->getSessionId());
@ -159,6 +159,11 @@ switch ($authMode) {
}
while ($isSubmission) {
if (!csrf_verify('passforgot', $_POST['csrf'] ?? '')) {
tpl_var('auth_forgot_error', 'Possible request forgery detected, refresh and try again.');
break;
}
if (empty($authEmail)) {
tpl_var('auth_forgot_error', 'Please enter an e-mail address.');
break;
@ -257,6 +262,11 @@ MSG;
break;
}
if (!csrf_verify('login', $_POST['csrf'] ?? '')) {
$authLoginError = 'Possible request forgery detected, refresh and try again.';
break;
}
$getUser = Database::prepare('
SELECT `user_id`, `password`
FROM `msz_users`
@ -340,6 +350,11 @@ MSG;
break;
}
if (!csrf_verify('register', $_POST['csrf'] ?? '')) {
$authRegistrationError = 'Possible request forgery detected, refresh and try again.';
break;
}
$checkSpamBot = mb_strtolower($_POST['auth']['meow'] ?? '');
$spamBotValid = [
'19', '21', 'nineteen', 'nine-teen', 'nine teen', 'twentyone', 'twenty-one', 'twenty one',

View file

@ -15,7 +15,7 @@ if ($isXHR) {
return;
}
if (!tmp_csrf_verify($_REQUEST['csrf'] ?? '')) {
if (!csrf_verify('comments', $_REQUEST['csrf'] ?? '')) {
echo render_info_or_json($isXHR, "Couldn't verify this request, please refresh the page and try again.", 403);
return;
}

View file

@ -83,7 +83,7 @@ if (!forum_may_have_topics($forum['forum_type'])) {
}
if ($postRequest) {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
if (!csrf_verify('settings', $_POST['csrf'] ?? '')) {
echo 'Could not verify request.';
return;
}

View file

@ -73,7 +73,7 @@ switch ($_GET['v'] ?? null) {
$changeId = (int)($_GET['c'] ?? 0);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && tmp_csrf_verify($_POST['csrf'] ?? '')) {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrf_verify('changelog_add', $_POST['csrf'] ?? '')) {
if (!empty($_POST['change']) && is_array($_POST['change'])) {
if ($changeId > 0) {
$postChange = Database::prepare('
@ -260,7 +260,7 @@ switch ($_GET['v'] ?? null) {
$tagId = (int)($_GET['t'] ?? 0);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && tmp_csrf_verify($_POST['csrf'] ?? '')) {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrf_verify('changelog_tag', $_POST['csrf'] ?? '')) {
if (!empty($_POST['tag']) && is_array($_POST['tag'])) {
if ($tagId > 0) {
$updateTag = Database::prepare('
@ -325,7 +325,7 @@ switch ($_GET['v'] ?? null) {
$actionId = (int)($_GET['a'] ?? 0);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && tmp_csrf_verify($_POST['csrf'] ?? '')) {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrf_verify('changelog_action', $_POST['csrf'] ?? '')) {
if (!empty($_POST['action']) && is_array($_POST['action'])) {
if ($actionId > 0) {
$updateAction = Database::prepare('

View file

@ -113,7 +113,7 @@ switch ($_GET['v'] ?? null) {
}
if ($isPostRequest) {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
if (!csrf_verify('users_edit', $_POST['csrf'] ?? '')) {
echo 'csrf err';
break;
}
@ -303,7 +303,7 @@ switch ($_GET['v'] ?? null) {
}
if ($isPostRequest) {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
if (!csrf_verify('users_role', $_POST['csrf'] ?? '')) {
echo 'csrf err';
break;
}

View file

@ -57,7 +57,7 @@ $avatarProps = $app->getAvatarProps();
$backgroundProps = $app->getBackgroundProps();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
if (!csrf_verify('settings', $_POST['csrf'] ?? '')) {
$settingsErrors[] = MSZ_TMP_USER_ERROR_STRINGS['csrf'];
} else {
if (!empty($_POST['profile']) && is_array($_POST['profile'])) {
@ -156,6 +156,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
switch ($_POST['background']['mode'] ?? '') {
case 'delete':
user_background_delete($settingsUserId);
user_background_set_settings($settingsUserId, MSZ_USER_BACKGROUND_ATTACHMENT_NONE);
break;
case 'upload':
@ -164,40 +165,55 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
break;
}
if (empty($_FILES['background'])
|| !is_array($_FILES['background'])
|| empty($_FILES['background']['name']['file'])) {
if (empty($_POST['background'])
|| !is_array($_POST['background'])) {
break;
}
if ($_FILES['background']['error']['file'] !== UPLOAD_ERR_OK) {
$settingsErrors[] = sprintf(
MSZ_TMP_USER_ERROR_STRINGS['avatar']['upload'][$_FILES['background']['error']['file']]
?? MSZ_TMP_USER_ERROR_STRINGS['avatar']['upload']['_'],
$_FILES['background']['error']['file'],
byte_symbol($backgroundProps['max_size'], true),
$backgroundProps['max_width'],
$backgroundProps['max_height']
if (!empty($_FILES['background']['name']['file'])) {
if ($_FILES['background']['error']['file'] !== UPLOAD_ERR_OK) {
$settingsErrors[] = sprintf(
MSZ_TMP_USER_ERROR_STRINGS['avatar']['upload'][$_FILES['background']['error']['file']]
?? MSZ_TMP_USER_ERROR_STRINGS['avatar']['upload']['_'],
$_FILES['background']['error']['file'],
byte_symbol($backgroundProps['max_size'], true),
$backgroundProps['max_width'],
$backgroundProps['max_height']
);
break;
}
$setBackground = user_background_set_from_path(
$settingsUserId,
$_FILES['background']['tmp_name']['file'],
$backgroundProps
);
break;
if ($setBackground !== MSZ_USER_BACKGROUND_NO_ERRORS) {
$settingsErrors[] = sprintf(
MSZ_TMP_USER_ERROR_STRINGS['avatar']['set'][$setBackground]
?? MSZ_TMP_USER_ERROR_STRINGS['avatar']['set']['_'],
$setBackground,
byte_symbol($backgroundProps['max_size'], true),
$backgroundProps['max_width'],
$backgroundProps['max_height']
);
}
}
$setBackground = user_background_set_from_path(
$settingsUserId,
$_FILES['background']['tmp_name']['file'],
$backgroundProps
);
$backgroundSettings = in_array($_POST['background']['attach'] ?? '', MSZ_USER_BACKGROUND_ATTACHMENTS_NAMES)
? array_flip(MSZ_USER_BACKGROUND_ATTACHMENTS_NAMES)[$_POST['background']['attach']]
: MSZ_USER_BACKGROUND_ATTACHMENTS[0];
if ($setBackground !== MSZ_USER_BACKGROUND_NO_ERRORS) {
$settingsErrors[] = sprintf(
MSZ_TMP_USER_ERROR_STRINGS['avatar']['set'][$setBackground]
?? MSZ_TMP_USER_ERROR_STRINGS['avatar']['set']['_'],
$setBackground,
byte_symbol($backgroundProps['max_size'], true),
$backgroundProps['max_width'],
$backgroundProps['max_height']
);
if (!empty($_POST['background']['attr']['blend'])) {
$backgroundSettings |= MSZ_USER_BACKGROUND_ATTRIBUTE_BLEND;
}
if (!empty($_POST['background']['attr']['slide'])) {
$backgroundSettings |= MSZ_USER_BACKGROUND_ATTRIBUTE_SLIDE;
}
user_background_set_settings($settingsUserId, $backgroundSettings);
break;
}
}
@ -220,7 +236,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} elseif ((int)$session['user_id'] !== $settingsUserId) {
$settingsErrors[] = 'You may only end your own sessions.';
} elseif ((int)$session['session_id'] === $app->getSessionId()) {
header('Location: /auth.php?m=logout&s=' . tmp_csrf_token());
header('Location: /auth.php?m=logout&s=' . csrf_token('logout'));
return;
} else {
user_session_delete($session['session_id']);
@ -334,11 +350,17 @@ switch ($settingsMode) {
$getAccountInfo = Database::prepare(sprintf(
'
SELECT %s, `email`, `user_about_content`, `user_about_parser`
SELECT
%1$s, `email`, `user_about_content`, `user_about_parser`,
`user_background_settings` & 0x0F as `user_background_attachment`,
(`user_background_settings` & %2$d) > 0 as `user_background_attr_blend`,
(`user_background_settings` & %3$d) > 0 as `user_background_attr_slide`
FROM `msz_users`
WHERE `user_id` = :user_id
',
pdo_prepare_array($profileFields, true, '`user_%s`')
pdo_prepare_array($profileFields, true, '`user_%s`'),
MSZ_USER_BACKGROUND_ATTRIBUTE_BLEND,
MSZ_USER_BACKGROUND_ATTRIBUTE_SLIDE
));
$getAccountInfo->bindValue('user_id', $settingsUserId);
$accountInfo = $getAccountInfo->execute() ? $getAccountInfo->fetch(PDO::FETCH_ASSOC) : [];
@ -354,6 +376,7 @@ switch ($settingsMode) {
'settings_profile_fields' => $profileFields,
'settings_disable_account_options' => $disableAccountOptions,
'account_info' => $accountInfo,
'background_attachments' => MSZ_USER_BACKGROUND_ATTACHMENTS_NAMES,
]);
break;

114
src/csrf.php Normal file
View file

@ -0,0 +1,114 @@
<?php
define('MSZ_CSRF_TOLERANCE', 15 * 60); // DO NOT EXCEED 16-BIT INTEGER SIZES, SHIT _WILL_ BREAK
define('MSZ_CSRF_HTML', '<input type="hidden" name="%1$s" value="%2$s">');
define('MSZ_CSRF_SECRET_STORE', '_msz_csrf_secret');
define('MSZ_CSRF_IDENTITY_STORE', '_msz_csrf_identity');
define('MSZ_CSRF_TOKEN_STORE', '_msz_csrf_tokens');
define('MSZ_CSRF_HASH_ALGO', 'sha256');
define('MSZ_CSRF_TOKEN_LENGTH', 76); // 8 + 4 + 64
// the following two functions DO NOT depend on csrf_init().
// $realm = Some kinda identifier for whatever's trying to do a validation.
// $identity = When the user is logged in I recommend just using their ID, otherwise IP will be fine.
function csrf_token_create(
string $realm,
string $identity,
string $secretKey,
?int $timestamp = null,
int $tolerance = MSZ_CSRF_TOLERANCE
): string {
$timestamp = $timestamp ?? time();
$token = bin2hex(pack('Vv', $timestamp, $tolerance));
return $token . csrf_token_hash(
MSZ_CSRF_HASH_ALGO,
$realm,
$identity,
$secretKey,
$timestamp,
$tolerance
);
}
function csrf_token_hash(
string $algo,
string $realm,
string $identity,
string $secretKey,
int $timestamp,
int $tolerance
): string {
return hash_hmac(
$algo,
implode(',', [$realm, $identity, $timestamp, $tolerance]),
$secretKey
);
}
function csrf_token_verify(
string $realm,
string $token,
string $identity,
string $secretKey
): bool {
if (empty($token) || strlen($token) !== MSZ_CSRF_TOKEN_LENGTH) {
return false;
}
[$timestamp, $tolerance] = [0, 0];
extract(unpack('Vtimestamp/vtolerance', hex2bin(substr($token, 0, 12))));
if (time() > $timestamp + $tolerance) {
return false;
}
// remove timestamp + tolerance from token
$token = substr($token, 12);
$compare = csrf_token_hash(
MSZ_CSRF_HASH_ALGO,
$realm,
$identity,
$secretKey,
$timestamp,
$tolerance
);
return hash_equals($compare, $token);
}
// Sets some defaults
function csrf_init(string $secretKey, string $identity): void
{
$GLOBALS[MSZ_CSRF_SECRET_STORE] = $secretKey;
$GLOBALS[MSZ_CSRF_IDENTITY_STORE] = $identity;
$GLOBALS[MSZ_CSRF_TOKEN_STORE] = [];
}
function csrf_token(string $realm): string
{
if (array_key_exists($realm, $GLOBALS[MSZ_CSRF_TOKEN_STORE])) {
return $GLOBALS[MSZ_CSRF_TOKEN_STORE][$realm];
}
return $GLOBALS[MSZ_CSRF_TOKEN_STORE][$realm] = csrf_token_create(
$realm,
$GLOBALS[MSZ_CSRF_IDENTITY_STORE],
$GLOBALS[MSZ_CSRF_SECRET_STORE]
);
}
function csrf_verify(string $realm, string $token): bool
{
return csrf_token_verify(
$realm,
$token,
$GLOBALS[MSZ_CSRF_IDENTITY_STORE],
$GLOBALS[MSZ_CSRF_SECRET_STORE]
);
}
function csrf_html(string $realm, string $name = 'csrf'): string
{
return sprintf(MSZ_CSRF_HTML, $name, csrf_token($realm));
}

View file

@ -5,7 +5,7 @@
method="post" action="/comments.php?m=create"
id="comment-{{ reply_mode ? 'reply-' ~ reply_to.comment_id : 'create-' ~ category.category_id }}">
<input type="hidden" name="comment[category]" value="{{ category.category_id }}">
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
{{ 'comments'|csrf|raw }}
{% if reply_mode %}
<input type="hidden" name="comment[reply]" value="{{ reply_to.comment_id }}">
@ -86,14 +86,14 @@
<div class="comment__actions">
{% if perms.can_vote %}
<a class="comment__action comment__action--link comment__action--like{% if comment.comment_user_vote == 'Like' %} comment__action--voted{% endif %}"
href="/comments.php?m=vote&amp;c={{ comment.comment_id }}&amp;v={{ comment.comment_user_vote == 'Like' ? '0' : '1' }}&amp;csrf={{ csrf_token() }}">
href="/comments.php?m=vote&amp;c={{ comment.comment_id }}&amp;v={{ comment.comment_user_vote == 'Like' ? '0' : '1' }}&amp;csrf={{ csrf_token('comments') }}">
Like
{% if comment.comment_likes > 0 %}
({{ comment.comment_likes|number_format }})
{% endif %}
</a>
<a class="comment__action comment__action--link comment__action--dislike{% if comment.comment_user_vote == 'Dislike' %} comment__action--voted{% endif %}"
href="/comments.php?m=vote&amp;c={{ comment.comment_id }}&amp;v={{ comment.comment_user_vote == 'Dislike' ? '0' : '-1' }}&amp;csrf={{ csrf_token() }}">
href="/comments.php?m=vote&amp;c={{ comment.comment_id }}&amp;v={{ comment.comment_user_vote == 'Dislike' ? '0' : '-1' }}&amp;csrf={{ csrf_token('comments') }}">
Dislike
{% if comment.comment_dislikes > 0 %}
({{ comment.comment_dislikes|number_format }})
@ -431,7 +431,7 @@
var replyCsrf = document.createElement('input');
replyCsrf.name = 'csrf';
replyCsrf.value = '{{ csrf_token() }}';
replyCsrf.value = '{{ csrf_token("comments") }}';
replyCsrf.type = 'hidden';
commentReplyInput.appendChild(replyCsrf);
@ -571,7 +571,7 @@
commentVoteLock = false;
};
xhr.open('GET', '/comments.php?m=vote&c={0}&v={1}&csrf={{ csrf_token() }}'.replace('{0}', id).replace('{1}', vote));
xhr.open('GET', '/comments.php?m=vote&c={0}&v={1}&csrf={{ csrf_token("comments") }}'.replace('{0}', id).replace('{1}', vote));
xhr.setRequestHeader('X-Misuzu-XHR', 'comments');
xhr.send();
}

View file

@ -82,7 +82,7 @@
</li>
{% endif %}
<li class="header__user__action">
<a class="header__user__link" href="/auth.php?m=logout&amp;s={{ csrf_token() }}">Log out</a>
<a class="header__user__link" href="/auth.php?m=logout&amp;s={{ csrf_token('logout') }}">Log out</a>
</li>
{% else %}
<li class="header__user__action">

View file

@ -11,6 +11,8 @@
{% if not prevent_registration %}
<form class="container container--new auth" method="post" action="">
<input type="hidden" name="auth[mode]" value="register">
{{ 'register'|csrf|raw }}
<div class="container__title">Register</div>
{% if auth_register_error is defined %}
@ -44,6 +46,8 @@
{% if not prevent_password_reset %}
<form class="container container--new auth" method="post" action="">
<input type="hidden" name="auth[mode]" value="forgot">
{{ 'passforgot'|csrf|raw }}
<div class="container__title">Forgot password</div>
{% if auth_forgot_error is defined %}

View file

@ -7,7 +7,7 @@
<div class="auth__form">
<p class="auth__paragraph">We couldn't verify that you were actually the person attempting to log out.</p>
<p class="auth__paragraph">Press the button below to verify the logout request, otherwise click back in your browser or close this tab.</p>
<a href="?m=logout&amp;s={{ csrf_token() }}" class="input__button input__button--new">Logout</a>
<a href="?m=logout&amp;s={{ csrf_token('logout') }}" class="input__button input__button--new">Logout</a>
</div>
</div>
{% endblock %}

View file

@ -3,6 +3,7 @@
<form class="container container--new auth" method="post" action="/auth.php">
<input type="hidden" name="auth[mode]" value="login">
{{ 'login'|csrf|raw }}
<div class="auth__header">
<div class="avatar avatar--new auth__avatar" id="login-avatar"

View file

@ -315,6 +315,7 @@
<div class="forum__posting__content">
<input type="hidden" name="post[{{ is_reply ? 'topic' : 'forum' }}]" value="{{ target_id }}">
{{ 'forum_post'|csrf|raw }}
{#<div class="forum__posting__errors">
<p class="forum__posting__error">Error: Your post contained too much text, shorten it a bit or split it out in two posts.</p>
@ -331,7 +332,7 @@
</div>
<div class="forum__posting__buttons">
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Submit</button>
<button class="input__button">Submit</button>
</div>
</div>
</form>

View file

@ -3,6 +3,8 @@
{% block manage_content %}
<div class="container">
<form action="?v=action{{ edit_action is defined ? '&a=' ~ edit_action.action_id : '' }}" method="post">
{{ 'changelog_action'|csrf|raw }}
<h1 class="container__title">
{% if edit_action is defined %}
Editing <span style="{{ edit_action.action_colour|html_colour }};color:var(--user-colour)">{{ edit_action.action_name }}</span> ({{ edit_action.action_id }})
@ -56,7 +58,7 @@
</label>
<div>
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Save</button>
<button class="input__button">Save</button>
</div>
</form>
</div>

View file

@ -7,6 +7,8 @@
{% block manage_content %}
<div class="container">
<form action="?v=change{{ edit_change is defined ? '&c=' ~ edit_change.change_id : '' }}" method="post">
{{ 'changelog_add'|csrf|raw }}
<h1 class="container__title">
{% if edit_change is defined %}
Editing #{{ edit_change.change_id }}
@ -57,7 +59,7 @@
</label>
<div>
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Save</button>
<button class="input__button">Save</button>
</div>
</form>
@ -82,7 +84,7 @@
</label>
<div>
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Remove</button>
<button class="input__button" name="csrf" value="{{ csrf_token('changelog_add') }}">Remove</button>
</div>
</form>
{% endif %}
@ -103,7 +105,7 @@
</label>
<div>
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Add</button>
<button class="input__button" name="csrf" value="{{ csrf_token('changelog_add') }}">Add</button>
</div>
</form>
{% endif %}

View file

@ -3,6 +3,8 @@
{% block manage_content %}
<div class="container">
<form action="?v=tag{{ edit_tag is defined ? '&t=' ~ edit_tag.tag_id : '' }}" method="post">
{{ 'changelog_tag'|csrf|raw }}
<h1 class="container__title">
{% if edit_tag is defined %}
Editing {{ edit_tag.tag_name }} ({{ edit_tag.tag_id }})
@ -42,56 +44,8 @@
{% endif %}
<div>
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Save</button>
<button class="input__button">Save</button>
</div>
</form>
{% if edit_change is defined %}
<h1 class="container__title">
Tags
</h1>
{% if edit_change_assigned_tags|length > 0 %}
<form class="container" method="post" action="" style="display:inline-block">
<label class="form__label">
<div class="form__label__text">Assigned Tags</div>
<div class="form__label__input">
<select name="remove_tag" class="input input--select">
{% for tag in edit_change_assigned_tags %}
<option value="{{ tag.tag_id }}">
{{ tag.tag_name }}
</option>
{% endfor %}
</select>
</div>
</label>
<div>
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Remove</button>
</div>
</form>
{% endif %}
{% if edit_change_available_tags|length > 0 %}
<form class="container" method="post" action="" style="display:inline-block">
<label class="form__label">
<div class="form__label__text">Available Tags</div>
<div class="form__label__input">
<select name="add_tag" class="input input--select">
{% for tag in edit_change_available_tags %}
<option value="{{ tag.tag_id }}">
{{ tag.tag_name }}
</option>
{% endfor %}
</select>
</div>
</label>
<div>
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Add</button>
</div>
</form>
{% endif %}
{% endif %}
</div>
{% endblock %}

View file

@ -3,6 +3,8 @@
{% block manage_content %}
<form action="?v=role{{ edit_role is defined ? '&r=' ~ edit_role.role_id : '' }}" method="post">
{{ 'users_role'|csrf|raw }}
<div class="container">
<h1 class="container__title">
{% if edit_role is defined %}
@ -93,6 +95,6 @@
</div>
{% endif %}
<button class="input__button" name="csrf" value="{{ csrf_token() }}">{{ edit_role is defined ? 'Update role' : 'Create role' }}</button>
<button class="input__button">{{ edit_role is defined ? 'Update role' : 'Create role' }}</button>
</form>
{% endblock %}

View file

@ -6,6 +6,8 @@
{% block manage_content %}
{% if can_manage_users %}
<form method="post" enctype="multipart/form-data" action="">
{{ 'users_edit'|csrf|raw }}
<div class="container">
<div class="container__title">
Viewing <span style="{{ view_user.colour|html_colour }}">{{ view_user.username }}</span> ({{ view_user.user_id }})
@ -145,7 +147,7 @@
</div>
{% endif %}
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Update</button>
<button class="input__button">Update</button>
</form>
{% endif %}
@ -154,7 +156,7 @@
<form method="post" action="" class="container">
<div class="container__title">Manage Roles</div>
<div class="container__content">
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
{{ 'users_edit'|csrf|raw }}
<label class="form__label">
<div class="form__label__text">Has Roles</div>
@ -192,7 +194,7 @@
</div>
</label>
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Add</button>
<button class="input__button" name="csrf" value="{{ csrf_token('users_edit') }}">Add</button>
</div>
</form>
{% endif %}

View file

@ -14,7 +14,7 @@
</style>
{% endif %}
</head>
<body class="main">
<body class="main{% if current_user.user_background_settings is defined %} {{ current_user.user_background_settings|bg_settings('main--bg-%s')|join(' ') }}{% endif %}">
{% include '_layout/header.twig' %}
<div class="main__wrapper">

View file

@ -14,7 +14,7 @@
<div class="container container--translucent">
<div class="container__title">Account</div>
<form action="" method="post" class="settings__account">
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
{{ 'settings'|csrf|raw }}
<div class="settings__account__row">
{% if settings_perms.edit_profile %}
@ -136,7 +136,7 @@
<div class="container__title">Avatar</div>
<form action="" method="post" class="settings__images" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="{{ background.max_size }}">
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
{{ 'settings'|csrf|raw }}
<div class="settings__images__sections">
<div class="settings__images__requirements">
@ -207,7 +207,7 @@
<div class="container__title">Background</div>
<form action="" method="post" class="settings__images" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="{{ background.max_size }}">
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
{{ 'settings'|csrf|raw }}
<div class="settings__images__sections">
<div class="settings__images__requirements">
@ -218,6 +218,24 @@
<li class="settings__images__requirement">May not be larger than <strong>{{ background.max_width }}x{{ background.max_height }}</strong>.</li>
<li class="settings__images__requirement">Animated gif images are <strong>not</strong> allowed.</li>
</ul>
<select name="background[attach]" class="input__select">
{% for key, value in background_attachments %}
<option value="{{ value }}"{% if account_info.user_background_attachment == key %} selected{% endif %}>
{{ value|capitalize }}
</option>
{% endfor %}
</select>
<label>
<input type="checkbox" name="background[attr][blend]"{% if account_info.user_background_attr_blend %} checked{% endif %}>
Blend
</label>
<label>
<input type="checkbox" name="background[attr][slide]"{% if account_info.user_background_attr_slide %} checked{% endif %}>
Slide
</label>
</div>
</div>
@ -275,7 +293,7 @@
</div>
<form method="post" action="" enctype="multipart/form-data" class="settings__about">
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
{{ 'settings'|csrf|raw }}
<textarea name="about[text]" class="input__textarea settings__about__text" id="about-textarea">{{ account_info.user_about_content|escape }}</textarea>

View file

@ -13,7 +13,8 @@
</div>
<form class="settings__sessions__actions" method="post" action="?m=sessions">
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
{{ 'settings'|csrf|raw }}
<button class="input__button" name="session_action" value="kill-all">
Kill all active sessions
</button>
@ -65,8 +66,9 @@
{% endif %}
<form class="settings__sessions__column settings__sessions__column--options" method="post" action="?m=sessions">
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
<input type="hidden" name="session" value="{{ session.session_id }}">
{{ 'settings'|csrf|raw }}
<button class="input__button settings__sessions__button">
{{ session.session_id == active_session_id ? 'Logout' : 'Kill' }}
</button>

View file

@ -42,8 +42,8 @@
{% block content %}
{% if is_editing %}
<form class="profile" method="post" action="/settings.php" enctype="multipart/form-data">
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
<input type="hidden" name="user" value="{{ profile.user_id }}">
{{ 'settings'|csrf|raw }}
{% if perms.edit_avatar %}
<input class="settings__avatar__input"

View file

@ -133,18 +133,6 @@ function get_country_name(string $code): string
}
}
// this is temporary, don't scream at me for using md5
// BIG TODO: make these functions not dependent on sessions so they can be used outside of those.
function tmp_csrf_verify(string $token): bool
{
return hash_equals(tmp_csrf_token(), $token);
}
function tmp_csrf_token(): string
{
return md5($_COOKIE['msz_sid'] ?? 'this is very insecure lmao');
}
function crop_image_centred_path(string $filename, int $target_width, int $target_height): \Imagick
{
return crop_image_centred(new \Imagick($filename), $target_width, $target_height);