diff --git a/misuzu.php b/misuzu.php index da98bed1..53662df6 100644 --- a/misuzu.php +++ b/misuzu.php @@ -39,7 +39,6 @@ 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/manage.php'; require_once 'src/news.php'; require_once 'src/perms.php'; @@ -456,10 +455,8 @@ MIG; } } - csrf_settings( - Config::get('csrf.secret', Config::TYPE_STR, 'insecure'), - empty($userDisplayInfo) ? ip_remote_address() : $cookieData['session_token'] - ); + CSRF::setGlobalSecretKey(Config::get('csrf.secret', Config::TYPE_STR, 'soup')); + CSRF::setGlobalIdentity(empty($userDisplayInfo) ? ip_remote_address() : $cookieData['session_token']); if(Config::get('private.enabled', Config::TYPE_BOOL)) { $onLoginPage = $_SERVER['PHP_SELF'] === url('auth-login'); diff --git a/public/auth/login.php b/public/auth/login.php index 4246a848..798e2d89 100644 --- a/public/auth/login.php +++ b/public/auth/login.php @@ -24,7 +24,7 @@ $ipAddress = ip_remote_address(); $remainingAttempts = user_login_attempts_remaining($ipAddress); while(!empty($_POST['login']) && is_array($_POST['login'])) { - if(!csrf_verify_request()) { + if(!CSRF::validateRequest()) { $notices[] = 'Was unable to verify the request, please try again!'; break; } diff --git a/public/auth/logout.php b/public/auth/logout.php index 69649732..8e03e58c 100644 --- a/public/auth/logout.php +++ b/public/auth/logout.php @@ -8,7 +8,7 @@ if(!user_session_active()) { return; } -if(csrf_verify_request()) { +if(CSRF::validateRequest()) { setcookie('msz_auth', '', -9001, '/', '', !empty($_SERVER['HTTPS']), true); user_session_stop(true); url_redirect('index'); diff --git a/public/auth/password.php b/public/auth/password.php index 2536c2bc..262bf7c4 100644 --- a/public/auth/password.php +++ b/public/auth/password.php @@ -30,7 +30,7 @@ $remainingAttempts = user_login_attempts_remaining($ipAddress); while($canResetPassword) { if(!empty($reset) && $userId > 0) { - if(!csrf_verify_request()) { + if(!CSRF::validateRequest()) { $notices[] = 'Was unable to verify the request, please try again!'; break; } @@ -73,7 +73,7 @@ while($canResetPassword) { } if(!empty($forgot)) { - if(!csrf_verify_request()) { + if(!CSRF::validateRequest()) { $notices[] = 'Was unable to verify the request, please try again!'; break; } diff --git a/public/auth/register.php b/public/auth/register.php index 589c8168..3ca9c6ef 100644 --- a/public/auth/register.php +++ b/public/auth/register.php @@ -18,7 +18,7 @@ $restricted = ip_blacklist_check(ip_remote_address()) ? 'blacklist' : (user_warning_check_ip(ip_remote_address()) ? 'ban' : ''); while(!$restricted && !empty($register)) { - if(!csrf_verify_request()) { + if(!CSRF::validateRequest()) { $notices[] = 'Was unable to verify the request, please try again!'; break; } diff --git a/public/auth/twofactor.php b/public/auth/twofactor.php index ba2bb521..04656ef3 100644 --- a/public/auth/twofactor.php +++ b/public/auth/twofactor.php @@ -26,7 +26,7 @@ if(empty($tokenInfo['user_totp_key'])) { } while(!empty($twofactor)) { - if(!csrf_verify_request()) { + if(!CSRF::validateRequest()) { $notices[] = 'Was unable to verify the request, please try again!'; break; } diff --git a/public/comments.php b/public/comments.php index e2c11a1f..84353d60 100644 --- a/public/comments.php +++ b/public/comments.php @@ -15,7 +15,7 @@ if($isXHR) { return; } -if(!csrf_verify_request()) { +if(!CSRF::validateRequest()) { echo render_info_or_json($isXHR, "Couldn't verify this request, please refresh the page and try again.", 403); return; } @@ -36,7 +36,7 @@ if(user_warning_check_expiration($currentUserId, MSZ_WARN_SILENCE) > 0) { return; } -csrf_http_header(); +header(CSRF::header()); $commentPerms = comments_get_perms($currentUserId); $commentId = !empty($_GET['c']) && is_string($_GET['c']) ? (int)$_GET['c'] : 0; diff --git a/public/forum/index.php b/public/forum/index.php index 1a48b6e2..c5e4813e 100644 --- a/public/forum/index.php +++ b/public/forum/index.php @@ -10,7 +10,7 @@ switch($indexMode) { case 'mark': $markEntireForum = $forumId === 0; - if(user_session_active() && csrf_verify_request()) { + if(user_session_active() && CSRF::validateRequest()) { forum_mark_read($markEntireForum ? null : $forumId, user_session_current('user_id', 0)); } diff --git a/public/forum/poll.php b/public/forum/poll.php index 6055464a..69bca4c2 100644 --- a/public/forum/poll.php +++ b/public/forum/poll.php @@ -13,7 +13,7 @@ if($isXHR) { return; } -if(!csrf_verify_request()) { +if(!CSRF::validateRequest()) { echo render_info_or_json($isXHR, "Couldn't verify this request, please refresh the page and try again.", 403); return; } @@ -34,7 +34,7 @@ if(user_warning_check_expiration($currentUserId, MSZ_WARN_SILENCE) > 0) { return; } -csrf_http_header(); +header(CSRF::header()); if(empty($_POST['poll']['id']) || !ctype_digit($_POST['poll']['id'])) { echo render_info_or_json($isXHR, "Invalid request.", 400); diff --git a/public/forum/post.php b/public/forum/post.php index 27df9a0e..655a1a89 100644 --- a/public/forum/post.php +++ b/public/forum/post.php @@ -19,7 +19,7 @@ if($isXHR) { return; } -$postRequestVerified = csrf_verify_request(); +$postRequestVerified = CSRF::validateRequest(); if(!empty($postMode) && !user_session_active()) { echo render_info_or_json($isXHR, 'You must be logged in to manage posts.', 401); @@ -47,7 +47,7 @@ if($isXHR) { return; } - csrf_http_header(); + header(CSRF::header()); } $postInfo = forum_post_get($postId, true); diff --git a/public/forum/posting.php b/public/forum/posting.php index 3b3f39df..8b1d3f3d 100644 --- a/public/forum/posting.php +++ b/public/forum/posting.php @@ -133,7 +133,7 @@ if(!empty($_POST)) { $topicType = isset($_POST['post']['type']) ? (int)$_POST['post']['type'] : null; $postSignature = isset($_POST['post']['signature']); - if(!csrf_verify_request()) { + if(!CSRF::validateRequest()) { $notices[] = 'Could not verify request.'; } else { $isEditingTopic = empty($topic) || ($mode === 'edit' && $post['is_opening_post']); diff --git a/public/forum/topic.php b/public/forum/topic.php index ecf4b5a9..79fd8768 100644 --- a/public/forum/topic.php +++ b/public/forum/topic.php @@ -82,12 +82,12 @@ if(in_array($moderationMode, $validModerationModes, true)) { return; } - if(!csrf_verify_request()) { + if(!CSRF::validateRequest()) { echo render_info_or_json($isXHR, "Couldn't verify this request, please refresh the page and try again.", 403); return; } - csrf_http_header(); + header(CSRF::header()); if(!user_session_active()) { echo render_info_or_json($isXHR, 'You must be logged in to manage posts.', 401); diff --git a/public/manage/changelog/change.php b/public/manage/changelog/change.php index 59ab689b..08dd8797 100644 --- a/public/manage/changelog/change.php +++ b/public/manage/changelog/change.php @@ -10,7 +10,7 @@ if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_P $changeId = (int)($_GET['c'] ?? 0); -if($_SERVER['REQUEST_METHOD'] === 'POST' && csrf_verify_request()) { +if($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { if(!empty($_POST['change']) && is_array($_POST['change'])) { if($changeId > 0) { $postChange = DB::prepare(' diff --git a/public/manage/changelog/tag.php b/public/manage/changelog/tag.php index 9d53793c..3218028a 100644 --- a/public/manage/changelog/tag.php +++ b/public/manage/changelog/tag.php @@ -10,7 +10,7 @@ if(!perms_check_user(MSZ_PERMS_CHANGELOG, user_session_current('user_id'), MSZ_P $tagId = (int)($_GET['t'] ?? 0); -if(!empty($_POST['tag']) && is_array($_POST['tag']) && csrf_verify_request()) { +if(!empty($_POST['tag']) && is_array($_POST['tag']) && CSRF::validateRequest()) { if($tagId > 0) { $updateTag = DB::prepare(' UPDATE `msz_changelog_tags` diff --git a/public/manage/general/blacklist.php b/public/manage/general/blacklist.php index 68a4d0db..b78d1c99 100644 --- a/public/manage/general/blacklist.php +++ b/public/manage/general/blacklist.php @@ -11,10 +11,10 @@ if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), General $notices = []; if(!empty($_POST)) { - if(!csrf_verify_request()) { + if(!CSRF::validateRequest()) { $notices[] = 'Verification failed.'; } else { - csrf_http_header(); + header(CSRF::header()); if(!empty($_POST['blacklist']['remove']) && is_array($_POST['blacklist']['remove'])) { foreach($_POST['blacklist']['remove'] as $cidr) { diff --git a/public/manage/general/emoticon.php b/public/manage/general/emoticon.php index 7435cde4..44878a2f 100644 --- a/public/manage/general/emoticon.php +++ b/public/manage/general/emoticon.php @@ -12,7 +12,7 @@ $emoteId = !empty($_GET['e']) && is_string($_GET['e']) ? (int)$_GET['e'] : 0; $isNew = $emoteId <= 0; $emoteInfo = !$isNew ? Emoticon::byId($emoteId) : new Emoticon; -if(csrf_verify_request() && isset($_POST['emote_order']) && isset($_POST['emote_hierarchy']) && !empty($_POST['emote_url']) && !empty($_POST['emote_strings'])) { +if(CSRF::validateRequest() && isset($_POST['emote_order']) && isset($_POST['emote_hierarchy']) && !empty($_POST['emote_url']) && !empty($_POST['emote_strings'])) { $emoteInfo->setUrl($_POST['emote_url']) ->setHierarchy($_POST['emote_hierarchy']) ->setOrder($_POST['emote_order']) diff --git a/public/manage/general/emoticons.php b/public/manage/general/emoticons.php index 4151635f..a2255220 100644 --- a/public/manage/general/emoticons.php +++ b/public/manage/general/emoticons.php @@ -8,7 +8,7 @@ if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), General return; } -if(csrf_verify_request() && !empty($_GET['emote']) && is_string($_GET['emote'])) { +if(CSRF::validateRequest() && !empty($_GET['emote']) && is_string($_GET['emote'])) { $emoteId = (int)$_GET['emote']; $emoteInfo = Emoticon::byId($emoteId); diff --git a/public/manage/news/category.php b/public/manage/news/category.php index 20ef1622..bb9e94c6 100644 --- a/public/manage/news/category.php +++ b/public/manage/news/category.php @@ -11,7 +11,7 @@ if(!perms_check_user(MSZ_PERMS_NEWS, user_session_current('user_id'), MSZ_PERM_N $category = []; $categoryId = (int)($_GET['c'] ?? null); -if(!empty($_POST['category']) && csrf_verify_request()) { +if(!empty($_POST['category']) && CSRF::validateRequest()) { $originalCategoryId = (int)($_POST['category']['id'] ?? null); $categoryId = news_category_create( $_POST['category']['name'] ?? null, diff --git a/public/manage/news/post.php b/public/manage/news/post.php index b71ff875..cd53ad16 100644 --- a/public/manage/news/post.php +++ b/public/manage/news/post.php @@ -12,7 +12,7 @@ $post = []; $postId = (int)($_GET['p'] ?? null); $categories = news_categories_get(0, 0, false, false, true); -if(!empty($_POST['post']) && csrf_verify_request()) { +if(!empty($_POST['post']) && CSRF::validateRequest()) { $originalPostId = (int)($_POST['post']['id'] ?? null); $currentUserId = user_session_current('user_id'); $title = $_POST['post']['title'] ?? null; diff --git a/public/manage/users/role.php b/public/manage/users/role.php index cc8b825e..9abb1bf0 100644 --- a/public/manage/users/role.php +++ b/public/manage/users/role.php @@ -20,7 +20,7 @@ if($canEditPerms) { $permissions = manage_perms_list(perms_get_role_raw($roleId ?? 0)); } -if(!empty($_POST['role']) && is_array($_POST['role']) && csrf_verify_request()) { +if(!empty($_POST['role']) && is_array($_POST['role']) && CSRF::validateRequest()) { $roleHierarchy = (int)($_POST['role']['hierarchy'] ?? -1); if(!user_check_super($currentUserId) && ($roleId === null diff --git a/public/manage/users/user.php b/public/manage/users/user.php index 0248ad75..76a40bb4 100644 --- a/public/manage/users/user.php +++ b/public/manage/users/user.php @@ -22,7 +22,7 @@ $canEdit = $isSuperUser || user_check_authority($currentUserId, $userId); $canEditPerms = $canEdit && perms_check_user(MSZ_PERMS_USER, $currentUserId, MSZ_PERM_USER_MANAGE_PERMS); $permissions = manage_perms_list(perms_get_user_raw($userId)); -if(csrf_verify_request() && $canEdit) { +if(CSRF::validateRequest() && $canEdit) { if(!empty($_POST['roles']) && is_array($_POST['roles']) && array_test($_POST['roles'], 'ctype_digit')) { // Fetch existing roles $existingRoles = DB::prepare(' diff --git a/public/profile.php b/public/profile.php index 4ae08710..83b3e24b 100644 --- a/public/profile.php +++ b/public/profile.php @@ -62,7 +62,7 @@ if($isEditing) { ]); if(!empty($_POST) && is_array($_POST)) { - if(!csrf_verify_request()) { + if(!CSRF::validateRequest()) { $notices[] = MSZ_TMP_USER_ERROR_STRINGS['csrf']; } else { if(!empty($_POST['profile']) && is_array($_POST['profile'])) { diff --git a/public/relations.php b/public/relations.php index 34182915..8e3bb363 100644 --- a/public/relations.php +++ b/public/relations.php @@ -15,12 +15,12 @@ if($isXHR) { return; } -if(!csrf_verify_request()) { +if(!CSRF::validateRequest()) { echo render_info_or_json($isXHR, "Couldn't verify this request, please refresh the page and try again.", 403); return; } -csrf_http_header(); +header(CSRF::header()); if(!user_session_active()) { echo render_info_or_json($isXHR, 'You must be logged in to manage relations.', 401); diff --git a/public/settings/account.php b/public/settings/account.php index a178268a..59749a28 100644 --- a/public/settings/account.php +++ b/public/settings/account.php @@ -16,7 +16,7 @@ $currentUserId = user_session_current('user_id'); $currentEmail = user_email_get($currentUserId); $isRestricted = user_warning_check_restriction($currentUserId); $twoFactorInfo = user_totp_info($currentUserId); -$isVerifiedRequest = csrf_verify_request(); +$isVerifiedRequest = CSRF::validateRequest(); if(!$isRestricted && $isVerifiedRequest && !empty($_POST['role'])) { $roleId = (int)($_POST['role']['id'] ?? 0); diff --git a/public/settings/sessions.php b/public/settings/sessions.php index 28bf52ad..1179bc4a 100644 --- a/public/settings/sessions.php +++ b/public/settings/sessions.php @@ -12,7 +12,7 @@ $errors = []; $currentUserId = user_session_current('user_id'); $sessionActive = user_session_current('session_id'); -if(!empty($_POST['session']) && csrf_verify_request()) { +if(!empty($_POST['session']) && CSRF::validateRequest()) { $currentSessionKilled = false; if(is_array($_POST['session'])) { diff --git a/src/CSRF.php b/src/CSRF.php new file mode 100644 index 00000000..566de84f --- /dev/null +++ b/src/CSRF.php @@ -0,0 +1,112 @@ +setTolerance($tolerance); + $this->setTimestamp($timestamp ?? self::timestamp()); + } + + public static function timestamp(): int { + return time() - self::EPOCH; + } + + public static function setGlobalIdentity(string $identity): void { + self::$globalIdentity = $identity; + } + public static function setGlobalSecretKey(string $secretKey): void { + self::$globalSecretKey = $secretKey; + } + public static function validate(string $token, ?string $identity = null, ?string $secretKey = null): bool { + try { + return self::decode($token, $identity ?? self::$globalIdentity, $secretKey ?? self::$globalSecretKey)->isValid(); + } catch(Exception $ex) { + return false; + } + } + public static function token(?string $identity = null, int $tolerance = self::TOLERANCE, ?string $secretKey = null, ?int $timestamp = null): string { + return (new static($tolerance, $timestamp))->encode($identity ?? self::$globalIdentity, $secretKey ?? self::$globalSecretKey); + } + + // Should be replaced by filters eventually < + public static function header(...$args): string { + return 'X-Misuzu-CSRF: ' . self::token(...$args); + } + public static function validateRequest(?string $identity = null, ?string $secretKey = null): bool { + if(isset($_SERVER['HTTP_X_MISUZU_CSRF'])) { + $token = $_SERVER['HTTP_X_MISUZU_CSRF']; + } elseif(isset($_POST['_csrf']) && is_string($_POST['_csrf'])) { + $token = $_POST['_csrf']; + } elseif(isset($_REQUEST['csrf']) && is_string($_REQUEST['csrf'])) { + $token = $_REQUEST['csrf']; + } else { + return false; + } + + return self::validate($token, $identity, $secretKey); + } + // > + + public static function decode(string $token, string $identity, string $secretKey): CSRF { + $hash = substr($token, 12); + $unpacked = unpack('Vtimestamp/vtolerance', hex2bin(substr($token, 0, 12))); + + if(empty($hash) || empty($unpacked['timestamp']) || empty($unpacked['tolerance'])) + throw new InvalidArgumentException('Invalid token provided.'); + + $csrf = new static($unpacked['tolerance'], $unpacked['timestamp']); + + if(!hash_equals($csrf->getHash($identity, $secretKey), $hash)) + throw new InvalidArgumentException('Modified token.'); + + return $csrf; + } + + public function encode(string $identity, string $secretKey): string { + $token = bin2hex(pack('Vv', $this->getTimestamp(), $this->getTolerance())); + $token .= $this->getHash($identity, $secretKey); + return $token; + } + + public function getHash(string $identity, string $secretKey): string { + return hash_hmac(self::HASH_ALGO, "{$identity}|{$this->getTimestamp()}|{$this->getTolerance()}", $secretKey); + } + + public function getTimestamp(): int { + return $this->timestamp; + } + public function setTimestamp(int $timestamp): self { + if($timestamp < 0 || $timestamp > 0xFFFFFFFF) + throw new InvalidArgumentException('Timestamp must be within the constaints of an unsigned 32-bit integer.'); + $this->timestamp = $timestamp; + return $this; + } + + public function getTolerance(): int { + return $this->tolerance; + } + public function setTolerance(int $tolerance): self { + if($tolerance < 0 || $tolerance > 0xFFFF) + throw new InvalidArgumentException('Tolerance must be within the constaints of an unsigned 16-bit integer.'); + $this->tolerance = $tolerance; + return $this; + } + + public function isValid(): bool { + $currentTime = self::timestamp(); + return $currentTime >= $this->getTimestamp() && $currentTime <= $this->getTimestamp() + $this->getTolerance(); + } +} diff --git a/src/TwigMisuzu.php b/src/TwigMisuzu.php index 880cb26e..bfa29e17 100644 --- a/src/TwigMisuzu.php +++ b/src/TwigMisuzu.php @@ -32,8 +32,6 @@ final class TwigMisuzu extends Twig_Extension { public function getFunctions() { return [ new Twig_Function('get_browser', 'get_browser'), - new Twig_Function('csrf_token', 'csrf_token'), - new Twig_Function('csrf_input', 'csrf_html'), new Twig_Function('url_construct', 'url_construct'), new Twig_Function('warning_has_duration', 'user_warning_has_duration'), new Twig_Function('url', 'url'), @@ -44,6 +42,7 @@ final class TwigMisuzu extends Twig_Extension { new Twig_Function('forum_may_have_children', 'forum_may_have_children'), new Twig_Function('forum_may_have_topics', 'forum_may_have_topics'), new Twig_Function('forum_has_priority_voting', 'forum_has_priority_voting'), + new Twig_Function('csrf_token', fn() => CSRF::token()), new Twig_Function('git_commit_hash', fn(bool $long = false) => GitInfo::hash($long)), new Twig_Function('git_tag', fn() => GitInfo::tag()), new Twig_Function('git_branch', fn() => GitInfo::branch()), diff --git a/src/csrf.php b/src/csrf.php deleted file mode 100644 index 5e1ca719..00000000 --- a/src/csrf.php +++ /dev/null @@ -1,127 +0,0 @@ -'); -define('MSZ_CSRF_HASH_ALGO', 'sha256'); -define('MSZ_CSRF_TOKEN_LENGTH', 76); // 8 + 4 + 64 - -// the following three functions DO NOT depend on csrf_init(). -// $identity = When the user is logged in I recommend just using their session key, otherwise IP will be fine. -function csrf_token_create( - 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, - $identity, - $secretKey, - $timestamp, - $tolerance - ); -} - -function csrf_token_hash( - string $algo, - string $identity, - string $secretKey, - int $timestamp, - int $tolerance -): string { - return hash_hmac( - $algo, - implode(',', [$identity, $timestamp, $tolerance]), - $secretKey - ); -} - -function csrf_token_verify( - 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, - $identity, - $secretKey, - $timestamp, - $tolerance - ); - - return hash_equals($compare, $token); -} - -// Sets some defaults -function csrf_settings(?string $secretKey = null, ?string $identity = null): array { - static $settings = []; - - if(!empty($secretKey) && !empty($identity)) { - $settings = [ - 'secret_key' => $secretKey, - 'identity' => $identity, - ]; - } - - return $settings; -} - -function csrf_is_ready(): bool { - return !empty(csrf_settings()); -} - -function csrf_token(): string { - static $token = null; - - if(empty($token)) { - $settings = csrf_settings(); - $token = csrf_token_create( - $settings['identity'], - $settings['secret_key'] - ); - } - - return $token; -} - -function csrf_verify(string $token): bool { - $settings = csrf_settings(); - - return csrf_token_verify( - $token, - $settings['identity'], - $settings['secret_key'] - ); -} - -function csrf_verify_request(?string $token = null): bool { - if(empty($token)) { - $token = $_SERVER['HTTP_X_MISUZU_CSRF'] ?? $_REQUEST['csrf'] ?? ''; - } - - return csrf_verify($token); -} - -function csrf_html(): string { - return sprintf(MSZ_CSRF_HTML, csrf_token()); -} - -function csrf_http_header(string $name = 'X-Misuzu-CSRF'): void { - header("{$name}: " . csrf_token()); -} diff --git a/src/url.php b/src/url.php index 4f8bcf34..c3f446c1 100644 --- a/src/url.php +++ b/src/url.php @@ -189,8 +189,8 @@ function url_variable(string $value, array $variables): string { return constant(trim($value, '[]')); } - if(starts_with($value, '{') && ends_with($value, '}') && csrf_is_ready()) { - return csrf_token(); + if(starts_with($value, '{') && ends_with($value, '}')) { + return \Misuzu\CSRF::token(); } return $value; diff --git a/templates/_layout/input.twig b/templates/_layout/input.twig index c8c93ecc..ded87663 100644 --- a/templates/_layout/input.twig +++ b/templates/_layout/input.twig @@ -4,9 +4,10 @@ {% endspaceless %} {% endmacro %} -{% macro input_csrf() %}{# so we don't have to specify |raw every time #} +{% macro input_csrf() %} +{% from _self import input_hidden %} {% spaceless %} - {{ csrf_input()|raw }} + {{ input_hidden('_csrf', csrf_token()) }} {% endspaceless %} {% endmacro %}