cool new CSRF protection system
This commit is contained in:
parent
5097cb812a
commit
c21d1b65ac
25 changed files with 276 additions and 124 deletions
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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('
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,12 +165,12 @@ 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 (!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']]
|
||||
|
@ -198,6 +199,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
$backgroundProps['max_height']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$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 (!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
114
src/csrf.php
Normal 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));
|
||||
}
|
|
@ -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&c={{ comment.comment_id }}&v={{ comment.comment_user_vote == 'Like' ? '0' : '1' }}&csrf={{ csrf_token() }}">
|
||||
href="/comments.php?m=vote&c={{ comment.comment_id }}&v={{ comment.comment_user_vote == 'Like' ? '0' : '1' }}&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&c={{ comment.comment_id }}&v={{ comment.comment_user_vote == 'Dislike' ? '0' : '-1' }}&csrf={{ csrf_token() }}">
|
||||
href="/comments.php?m=vote&c={{ comment.comment_id }}&v={{ comment.comment_user_vote == 'Dislike' ? '0' : '-1' }}&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();
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
</li>
|
||||
{% endif %}
|
||||
<li class="header__user__action">
|
||||
<a class="header__user__link" href="/auth.php?m=logout&s={{ csrf_token() }}">Log out</a>
|
||||
<a class="header__user__link" href="/auth.php?m=logout&s={{ csrf_token('logout') }}">Log out</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="header__user__action">
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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&s={{ csrf_token() }}" class="input__button input__button--new">Logout</a>
|
||||
<a href="?m=logout&s={{ csrf_token('logout') }}" class="input__button input__button--new">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
12
utility.php
12
utility.php
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue