Skip to content

Instantly share code, notes, and snippets.

@haruyama
Created June 6, 2014 09:10
Show Gist options
  • Save haruyama/3b429ef86d3a74ece040 to your computer and use it in GitHub Desktop.
Save haruyama/3b429ef86d3a74ece040 to your computer and use it in GitHub Desktop.
Unixuser\Slim\CsrfGuard
<?php
// Copyright (c) HARUYAMA Seigo
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished
// to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Original:
// https://github.com/codeguy/Slim-Extras/blob/a831f4e0a7e52ea7b982edaa9fe0e5b1640412d6/Middleware/CsrfGuard.php
//
// Copyright (c) 2012 Josh Lockhart
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished
// to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
namespace Unixuser\Slim;
class CsrfGuard extends \Slim\Middleware
{
protected $key;
protected $cookie;
protected $header;
protected $cookie_secure;
protected $csrf_error_template;
public function __construct($key = 'csrf_token', $cookie = 'XSRF-TOKEN', $header = 'X-XSRF-TOKEN')
{
$this->key = $key;
$this->cookie = $cookie;
$this->header = $header;
$this->cookie_secure = ini_get('session.cookie_secure');
}
public function setCsrfErrorTemplate($filename)
{
$this->csrf_error_template = $filename;
}
private function renderCsrfErrorPage($message)
{
if (empty($this->csrf_error_template)) {
$this->app->halt(400, $message);
} else {
$this->app->render(
$this->csrf_error_template,
['message' => $message],
400
);
$this->app->stop();
}
}
public function call()
{
$this->app->hook('slim.before', [$this, 'check']);
$this->next->call();
}
public function check()
{
if (session_id() === '') {
throw new \Exception('Sessions are required to use the CSRF Guard middleware.');
}
$app = $this->app;
if (!isset($_SESSION[$this->key])) {
$_SESSION[$this->key] = base64_encode(hash('sha256', openssl_random_pseudo_bytes(32), true));
}
$token = $_SESSION[$this->key];
if ($this->app->request->isXhr()) {
if (! $this->secureEquals($token, $app->request()->headers->get($this->header))) {
$this->renderCsrfErrorPage('Invalid or missing CSRF token(XHR).');
}
} elseif (in_array($app->request()->getMethod(), ['POST', 'PUT', 'DELETE'])) {
if (! $this->secureEquals($token, $app->request()->post($this->key))) {
$this->renderCsrfErrorPage('Invalid or missing CSRF token.');
}
}
$app->setCookie($this->cookie, $token, 0, '/', '', $this->cookie_secure, false);
$app->view()->appendData([
'csrf_key' => $this->key,
'csrf_token' => $token,
]);
}
// Original
// https://github.com/symfony/security-core/blob/e76be03b0a56d41c20b9027219593e31e0faa571/Util/StringUtils.php#L36
//
// Copyright (c) 2004-2014 Fabien Potencier
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished
// to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
private static function secureEquals($knownString, $userInput)
{
$knownString .= chr(0);
$userInput .= chr(0);
$knownLen = strlen($knownString);
$userLen = strlen($userInput);
$result = $knownLen - $userLen;
for ($i = 0; $i < $userLen; ++$i) {
$result |= (ord($knownString[$i % $knownLen]) ^ ord($userInput[$i]));
}
return 0 === $result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment