Combine common CSRF logic to a trait
This commit is contained in:
parent
49d68f0671
commit
b1fa45ee3a
|
|
@ -36,6 +36,7 @@ class Controller extends ControllerBase
|
||||||
use \System\Traits\AssetMaker;
|
use \System\Traits\AssetMaker;
|
||||||
use \System\Traits\ConfigMaker;
|
use \System\Traits\ConfigMaker;
|
||||||
use \System\Traits\EventEmitter;
|
use \System\Traits\EventEmitter;
|
||||||
|
use \System\Traits\SecurityController;
|
||||||
use \Backend\Traits\ErrorMaker;
|
use \Backend\Traits\ErrorMaker;
|
||||||
use \Backend\Traits\WidgetMaker;
|
use \Backend\Traits\WidgetMaker;
|
||||||
use \October\Rain\Extension\ExtendableTrait;
|
use \October\Rain\Extension\ExtendableTrait;
|
||||||
|
|
@ -763,55 +764,4 @@ class Controller extends ControllerBase
|
||||||
$hiddenHints = UserPreference::forUser()->get('backend::hints.hidden', []);
|
$hiddenHints = UserPreference::forUser()->get('backend::hints.hidden', []);
|
||||||
return array_key_exists($name, $hiddenHints);
|
return array_key_exists($name, $hiddenHints);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Security
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the request data / headers for a valid CSRF token.
|
|
||||||
* Returns false if a valid token is not found. Override this
|
|
||||||
* method to disable the check.
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function verifyCsrfToken()
|
|
||||||
{
|
|
||||||
if (!Config::get('cms.enableCsrfProtection')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_array(Request::method(), ['HEAD', 'GET', 'OPTIONS'])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$token = Request::input('_token') ?: Request::header('X-CSRF-TOKEN');
|
|
||||||
|
|
||||||
if (!strlen($token) || !strlen(Session::token())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash_equals(
|
|
||||||
Session::token(),
|
|
||||||
$token
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the back-end should force a secure protocol (HTTPS) enabled by config.
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function verifyForceSecure()
|
|
||||||
{
|
|
||||||
if (Request::secure() || Request::ajax()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo if year >= 2018 change default from false to null
|
|
||||||
$forceSecure = Config::get('cms.backendForceSecure', false);
|
|
||||||
if ($forceSecure === null) {
|
|
||||||
$forceSecure = !Config::get('app.debug', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !$forceSecure;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,12 @@ use App;
|
||||||
use View;
|
use View;
|
||||||
use Lang;
|
use Lang;
|
||||||
use Flash;
|
use Flash;
|
||||||
use Crypt;
|
|
||||||
use Config;
|
use Config;
|
||||||
use Session;
|
use Session;
|
||||||
use Request;
|
use Request;
|
||||||
use Response;
|
use Response;
|
||||||
use Exception;
|
use Exception;
|
||||||
use BackendAuth;
|
use BackendAuth;
|
||||||
use Carbon\Carbon;
|
|
||||||
use Twig\Environment as TwigEnvironment;
|
use Twig\Environment as TwigEnvironment;
|
||||||
use Twig\Cache\FilesystemCache as TwigCacheFilesystem;
|
use Twig\Cache\FilesystemCache as TwigCacheFilesystem;
|
||||||
use Cms\Twig\Loader as TwigLoader;
|
use Cms\Twig\Loader as TwigLoader;
|
||||||
|
|
@ -27,7 +25,6 @@ use System\Twig\Extension as SystemTwigExtension;
|
||||||
use October\Rain\Exception\AjaxException;
|
use October\Rain\Exception\AjaxException;
|
||||||
use October\Rain\Exception\ValidationException;
|
use October\Rain\Exception\ValidationException;
|
||||||
use October\Rain\Parse\Bracket as TextParser;
|
use October\Rain\Parse\Bracket as TextParser;
|
||||||
use Symfony\Component\HttpFoundation\Cookie;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Symfony\Component\HttpFoundation\Response as BaseResponse;
|
use Symfony\Component\HttpFoundation\Response as BaseResponse;
|
||||||
|
|
||||||
|
|
@ -42,6 +39,7 @@ class Controller
|
||||||
{
|
{
|
||||||
use \System\Traits\AssetMaker;
|
use \System\Traits\AssetMaker;
|
||||||
use \System\Traits\EventEmitter;
|
use \System\Traits\EventEmitter;
|
||||||
|
use \System\Traits\SecurityController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Cms\Classes\Theme A reference to the CMS theme processed by the controller.
|
* @var \Cms\Classes\Theme A reference to the CMS theme processed by the controller.
|
||||||
|
|
@ -1605,68 +1603,4 @@ class Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Security
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds anti-CSRF cookie.
|
|
||||||
* Adds a cookie with a token for CSRF checks to the response.
|
|
||||||
*
|
|
||||||
* @param BaseResponse $response The response object to add the cookie to
|
|
||||||
* @return BaseResponse
|
|
||||||
*/
|
|
||||||
protected function addXsrfCookie(BaseResponse $response)
|
|
||||||
{
|
|
||||||
$config = Config::get('session');
|
|
||||||
|
|
||||||
$response->headers->setCookie(
|
|
||||||
new Cookie(
|
|
||||||
'XSRF-TOKEN',
|
|
||||||
Session::token(),
|
|
||||||
Carbon::now()->addMinutes((int) $config['lifetime'])->getTimestamp(),
|
|
||||||
$config['path'],
|
|
||||||
$config['domain'],
|
|
||||||
$config['secure'],
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
$config['same_site'] ?? null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the request data / headers for a valid CSRF token.
|
|
||||||
* Returns false if a valid token is not found. Override this
|
|
||||||
* method to disable the check.
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function verifyCsrfToken()
|
|
||||||
{
|
|
||||||
if (!Config::get('cms.enableCsrfProtection', true)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_array(Request::method(), ['HEAD', 'GET', 'OPTIONS'])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$token = Request::input('_token') ?: Request::header('X-CSRF-TOKEN');
|
|
||||||
|
|
||||||
if (!$token && $header = Request::header('X-XSRF-TOKEN')) {
|
|
||||||
$token = Crypt::decrypt($header, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strlen($token) || !strlen(Session::token())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash_equals(
|
|
||||||
Session::token(),
|
|
||||||
$token
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
<?php namespace System\Traits;
|
||||||
|
|
||||||
|
use Crypt;
|
||||||
|
use Config;
|
||||||
|
use Request;
|
||||||
|
use Session;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Symfony\Component\HttpFoundation\Response as BaseResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Cookie;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security Controller Trait
|
||||||
|
* Adds cross-site scripting protection methods to a controller based class
|
||||||
|
*
|
||||||
|
* @package october\system
|
||||||
|
* @author Alexey Bobkov, Samuel Georges
|
||||||
|
*/
|
||||||
|
trait SecurityController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Adds anti-CSRF cookie.
|
||||||
|
* Adds a cookie with a token for CSRF checks to the response.
|
||||||
|
*
|
||||||
|
* @param BaseResponse $response The response object to add the cookie to
|
||||||
|
* @return BaseResponse
|
||||||
|
*/
|
||||||
|
protected function addXsrfCookie(BaseResponse $response)
|
||||||
|
{
|
||||||
|
$config = Config::get('session');
|
||||||
|
|
||||||
|
$response->headers->setCookie(
|
||||||
|
new Cookie(
|
||||||
|
'XSRF-TOKEN',
|
||||||
|
Session::token(),
|
||||||
|
Carbon::now()->addMinutes((int) $config['lifetime'])->getTimestamp(),
|
||||||
|
$config['path'],
|
||||||
|
$config['domain'],
|
||||||
|
$config['secure'],
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
$config['same_site'] ?? null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the request data / headers for a valid CSRF token.
|
||||||
|
* Returns false if a valid token is not found. Override this
|
||||||
|
* method to disable the check.
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function verifyCsrfToken()
|
||||||
|
{
|
||||||
|
if (!Config::get('cms.enableCsrfProtection', true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array(Request::method(), ['HEAD', 'GET', 'OPTIONS'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = Request::input('_token') ?: Request::header('X-CSRF-TOKEN');
|
||||||
|
|
||||||
|
if (!$token && $header = Request::header('X-XSRF-TOKEN')) {
|
||||||
|
$token = Crypt::decrypt($header, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strlen($token) || !strlen(Session::token())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash_equals(
|
||||||
|
Session::token(),
|
||||||
|
$token
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the back-end should force a secure protocol (HTTPS) enabled by config.
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function verifyForceSecure()
|
||||||
|
{
|
||||||
|
if (Request::secure() || Request::ajax()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo if year >= 2018 change default from false to null
|
||||||
|
$forceSecure = Config::get('cms.backendForceSecure', false);
|
||||||
|
if ($forceSecure === null) {
|
||||||
|
$forceSecure = !Config::get('app.debug', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !$forceSecure;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue