mirror of
https://github.com/wintercms/winter.git
synced 2024-06-28 05:33:29 +02:00
Merge pull request #4720 from octobercms/csrf_fix
Implement XSRF checking for AJAX handlers. Credit to @bennothommo @daftspunk.
This commit is contained in:
commit
0de4f1903f
@ -6,12 +6,14 @@ use App;
|
||||
use View;
|
||||
use Lang;
|
||||
use Flash;
|
||||
use Crypt;
|
||||
use Config;
|
||||
use Session;
|
||||
use Request;
|
||||
use Response;
|
||||
use Exception;
|
||||
use BackendAuth;
|
||||
use Carbon\Carbon;
|
||||
use Twig\Environment as TwigEnvironment;
|
||||
use Twig\Cache\FilesystemCache as TwigCacheFilesystem;
|
||||
use Cms\Twig\Loader as TwigLoader;
|
||||
@ -25,6 +27,7 @@ use System\Twig\Extension as SystemTwigExtension;
|
||||
use October\Rain\Exception\AjaxException;
|
||||
use October\Rain\Exception\ValidationException;
|
||||
use October\Rain\Parse\Bracket as TextParser;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
/**
|
||||
@ -133,11 +136,29 @@ class Controller
|
||||
* Finds and serves the requested page.
|
||||
* If the page cannot be found, returns the page with the URL /404.
|
||||
* If the /404 page doesn't exist, returns the system 404 page.
|
||||
* * If the parameter is omitted, the current URL used.
|
||||
*
|
||||
* @param string $url Specifies the requested page URL.
|
||||
* If the parameter is omitted, the current URL used.
|
||||
* @return string Returns the processed page content.
|
||||
* @return Response Returns the processed page content.
|
||||
*/
|
||||
public function run($url = '/')
|
||||
{
|
||||
$response = $this->runInternal($url);
|
||||
|
||||
if (Config::get('cms.enableCsrfProtection') && $response instanceof \Symfony\Component\HttpFoundation\Response) {
|
||||
$this->addXsrfCookie($response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the request internally
|
||||
*
|
||||
* @param string $url Specifies the requested page URL.
|
||||
* @return Response Returns the processed page content.
|
||||
*/
|
||||
protected function runInternal($url = '/')
|
||||
{
|
||||
if ($url === null) {
|
||||
$url = Request::path();
|
||||
@ -147,6 +168,13 @@ class Controller
|
||||
$url = '/';
|
||||
}
|
||||
|
||||
/*
|
||||
* Check security token.
|
||||
*/
|
||||
if (!$this->verifyCsrfToken()) {
|
||||
return Response::make(Lang::get('system::lang.page.invalid_token.label'), 403);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hidden page
|
||||
*/
|
||||
@ -802,13 +830,6 @@ class Controller
|
||||
*/
|
||||
protected function runAjaxHandler($handler)
|
||||
{
|
||||
/*
|
||||
* Check security token.
|
||||
*/
|
||||
if (!$this->verifyCsrfToken()) {
|
||||
return Response::make(Lang::get('system::lang.page.invalid_token.label'), 403);
|
||||
}
|
||||
|
||||
/**
|
||||
* @event cms.ajax.beforeRunHandler
|
||||
* Provides an opportunity to modify an AJAX request
|
||||
@ -1583,6 +1604,32 @@ class Controller
|
||||
// Security
|
||||
//
|
||||
|
||||
/**
|
||||
* Adds anti-CSRF cookie.
|
||||
* Adds a cookie with a token for CSRF checks to the response.
|
||||
* @return Response
|
||||
*/
|
||||
protected function addXsrfCookie(\Illuminate\Http\Response $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
|
||||
@ -1601,6 +1648,10 @@ class Controller
|
||||
|
||||
$token = Request::input('_token') ?: Request::header('X-CSRF-TOKEN');
|
||||
|
||||
if (!$token && $header = Request::header('X-XSRF-TOKEN')) {
|
||||
$token = Crypt::decrypt($header);
|
||||
}
|
||||
|
||||
if (!strlen($token) || !strlen(Session::token())) {
|
||||
return false;
|
||||
}
|
||||
|
8
modules/system/assets/js/framework-min.js
vendored
8
modules/system/assets/js/framework-min.js
vendored
@ -14,6 +14,8 @@ useFiles=false}
|
||||
if($.type(loading)=='string'){loading=$(loading)}
|
||||
var requestHeaders={'X-OCTOBER-REQUEST-HANDLER':handler,'X-OCTOBER-REQUEST-PARTIALS':this.extractPartials(options.update)}
|
||||
if(useFlash){requestHeaders['X-OCTOBER-REQUEST-FLASH']=1}
|
||||
var csrfToken=getXSRFToken()
|
||||
if(csrfToken){requestHeaders['X-XSRF-TOKEN']=csrfToken}
|
||||
var requestData,inputName,data={}
|
||||
$.each($el.parents('[data-request-data]').toArray().reverse(),function extendRequest(){$.extend(data,paramToObj('data-request-data',$(this).data('request-data')))})
|
||||
if($el.is(':input')&&!$form.length){inputName=$el.attr('name')
|
||||
@ -112,6 +114,12 @@ function paramToObj(name,value){if(value===undefined)value=''
|
||||
if(typeof value=='object')return value
|
||||
try{return ocJSON("{"+value+"}")}
|
||||
catch(e){throw new Error('Error parsing the '+name+' attribute value. '+e)}}
|
||||
function getXSRFToken(){var cookieValue=null
|
||||
if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';')
|
||||
for(var i=0;i<cookies.length;i++){var cookie=jQuery.trim(cookies[i])
|
||||
if(cookie.substring(0,11)==('XSRF-TOKEN'+'=')){cookieValue=decodeURIComponent(cookie.substring(11))
|
||||
break}}}
|
||||
return cookieValue}
|
||||
$(document).on('change','select[data-request], input[type=radio][data-request], input[type=checkbox][data-request], input[type=file][data-request]',function documentOnChange(){$(this).request()})
|
||||
$(document).on('click','a[data-request], button[data-request], input[type=button][data-request], input[type=submit][data-request]',function documentOnClick(e){e.preventDefault()
|
||||
$(this).request()
|
||||
|
@ -14,6 +14,8 @@ useFiles=false}
|
||||
if($.type(loading)=='string'){loading=$(loading)}
|
||||
var requestHeaders={'X-OCTOBER-REQUEST-HANDLER':handler,'X-OCTOBER-REQUEST-PARTIALS':this.extractPartials(options.update)}
|
||||
if(useFlash){requestHeaders['X-OCTOBER-REQUEST-FLASH']=1}
|
||||
var csrfToken=getXSRFToken()
|
||||
if(csrfToken){requestHeaders['X-XSRF-TOKEN']=csrfToken}
|
||||
var requestData,inputName,data={}
|
||||
$.each($el.parents('[data-request-data]').toArray().reverse(),function extendRequest(){$.extend(data,paramToObj('data-request-data',$(this).data('request-data')))})
|
||||
if($el.is(':input')&&!$form.length){inputName=$el.attr('name')
|
||||
@ -112,6 +114,12 @@ function paramToObj(name,value){if(value===undefined)value=''
|
||||
if(typeof value=='object')return value
|
||||
try{return ocJSON("{"+value+"}")}
|
||||
catch(e){throw new Error('Error parsing the '+name+' attribute value. '+e)}}
|
||||
function getXSRFToken(){var cookieValue=null
|
||||
if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';')
|
||||
for(var i=0;i<cookies.length;i++){var cookie=jQuery.trim(cookies[i])
|
||||
if(cookie.substring(0,11)==('XSRF-TOKEN'+'=')){cookieValue=decodeURIComponent(cookie.substring(11))
|
||||
break}}}
|
||||
return cookieValue}
|
||||
$(document).on('change','select[data-request], input[type=radio][data-request], input[type=checkbox][data-request], input[type=file][data-request]',function documentOnChange(){$(this).request()})
|
||||
$(document).on('click','a[data-request], button[data-request], input[type=button][data-request], input[type=submit][data-request]',function documentOnClick(e){e.preventDefault()
|
||||
$(this).request()
|
||||
|
@ -68,6 +68,11 @@ if (window.jQuery.request !== undefined) {
|
||||
requestHeaders['X-OCTOBER-REQUEST-FLASH'] = 1
|
||||
}
|
||||
|
||||
var csrfToken = getXSRFToken()
|
||||
if (csrfToken) {
|
||||
requestHeaders['X-XSRF-TOKEN'] = csrfToken
|
||||
}
|
||||
|
||||
/*
|
||||
* Request data
|
||||
*/
|
||||
@ -465,6 +470,21 @@ if (window.jQuery.request !== undefined) {
|
||||
}
|
||||
}
|
||||
|
||||
function getXSRFToken() {
|
||||
var cookieValue = null
|
||||
if (document.cookie && document.cookie != '') {
|
||||
var cookies = document.cookie.split(';')
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = jQuery.trim(cookies[i])
|
||||
if (cookie.substring(0, 11) == ('XSRF-TOKEN' + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(11))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue
|
||||
}
|
||||
|
||||
$(document).on('change', 'select[data-request], input[type=radio][data-request], input[type=checkbox][data-request], input[type=file][data-request]', function documentOnChange() {
|
||||
$(this).request()
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user