├── .gitattributes ├── LICENSE.md ├── README.md ├── composer.json └── src ├── CsrfTokenToHeaders.php └── CsrfTokenToView.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Aurélien Millet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slim 3 CSRF middleware utilities 2 | 3 | Requires [Slim 3 CSRF component](https://github.com/slimphp/Slim-Csrf) 4 | 5 | Basically, this package passes CSRF token to view (currently, official Slim Twig and PHP renderers are supported) or in response headers (for AJAX calls). 6 | 7 | ## Installation 8 | 9 | Requires [Composer](https://getcomposer.org/doc/00-intro.md) 10 | 11 | ```sh 12 | composer require aurmil/slim3-csrf-utilities 13 | ``` 14 | 15 | Then require Composer autoload file 16 | 17 | ```php 18 | require 'vendor/autoload.php'; 19 | ``` 20 | 21 | ## Usage 22 | 23 | For an action that needs to display CSRF token in a view, add __Aurmil\Slim\CsrfTokenToView__ middleware before __Slim\Csrf\Guard__. 24 | 25 | For an AJAX called action that needs to return new token to the caller in response headers, add __Aurmil\Slim\CsrfTokenToHeaders__ middleware before __Slim\Csrf\Guard__. 26 | 27 | Let's consider a really light Slim app: 28 | 29 | index.php 30 | 31 | ```php 32 | getContainer(); 46 | 47 | // If a route needs a view renderer 48 | $container['renderer'] = function ($c) { 49 | return new \Slim\Views\Twig(__DIR__, ['cache' => false]); // Twig 50 | return new \Slim\Views\PhpRenderer(__DIR__.'/'); // Or PHP 51 | }; 52 | 53 | // CSRF component 54 | $container['csrf'] = function ($c) { 55 | return new \Slim\Csrf\Guard; 56 | }; 57 | 58 | // HTML form including fields for CSRF token 59 | $app->get('/', function ($request, $response) { 60 | return $this->renderer->render($response, 'view.twig'); // Twig 61 | return $this->renderer->render($response, 'view.php'); // Or PHP 62 | })->add(new CsrfTokenToView($container->csrf, $container->renderer)) 63 | ->add($container->csrf); 64 | 65 | // CSRF protected action, can be called by AJAX 66 | $app->post('/submit', function ($request, $response) { 67 | if ($request->isXhr()) { 68 | return $response->withJson(['success' => true]); 69 | } else { 70 | return $response->withRedirect('/'); 71 | } 72 | })->add(new CsrfTokenToHeaders($container->csrf)) 73 | ->add($container->csrf); 74 | 75 | // Slim dispatching 76 | $app->run(); 77 | ``` 78 | 79 | Twig view 80 | 81 | ```twig 82 | 83 | 84 | 85 | 86 | CSRF 87 | 88 | 89 |
90 | {% if csrf_token is defined and csrf_token %} 91 | {% for key, value in csrf_token %} 92 | 93 | {% endfor %} 94 | {% endif %} 95 | 96 | 97 |
98 | 99 | 100 | 101 | 102 | 103 | 104 | ``` 105 | 106 | Or PHP view 107 | 108 | ```php 109 | 110 | 111 | 112 | 113 | CSRF 114 | 115 | 116 |
117 | 118 | $value): ?> 119 | 120 | 121 | 122 | 123 | 124 |
125 | 126 | 127 | 128 | 129 | 130 | 131 | ``` 132 | 133 | JS file (fox AJAX calls) using jQuery 134 | 135 | ```js 136 | $(function () { 137 | var form = $('form'); 138 | form.on('submit', function () { 139 | $.ajax({ 140 | url: form.attr('action'), 141 | method: form.attr('method'), 142 | data: form.serialize(), 143 | cache: false, 144 | dataType: 'json', 145 | success: function (data) { 146 | console.log('OK'); 147 | }, 148 | error: function () { 149 | console.log('error') 150 | }, 151 | complete: function (jqXHR) { 152 | var csrfToken = jqXHR.getResponseHeader('X-CSRF-Token'); 153 | 154 | if (csrfToken) { 155 | try { 156 | csrfToken = $.parseJSON(csrfToken); 157 | var csrfTokenKeys = Object.keys(csrfToken); 158 | var hiddenFields = form.find('input.csrf[type="hidden"]'); 159 | 160 | if (csrfTokenKeys.length === hiddenFields.length) { 161 | hiddenFields.each(function(i) { 162 | $(this).attr('name', csrfTokenKeys[i]); 163 | $(this).val(csrfToken[csrfTokenKeys[i]]); 164 | }); 165 | } 166 | } catch (e) { 167 | 168 | } 169 | } 170 | } 171 | }); 172 | 173 | return false; 174 | }); 175 | }); 176 | ``` 177 | 178 | And .htaccess 179 | 180 | ```apache_conf 181 | 182 | RewriteEngine On 183 | RewriteCond %{REQUEST_FILENAME} !-f 184 | RewriteRule ^ index.php [QSA,L] 185 | 186 | ``` 187 | 188 | ## License 189 | 190 | The MIT License (MIT). Please see [License File](https://github.com/aurmil/slim3-csrf-utilities/blob/master/LICENSE.md) for more information. 191 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aurmil/slim3-csrf-utilities", 3 | "description": "Slim Framework 3 CSRF protection middleware utilities", 4 | "keywords": ["slimphp", "slim", "framework", "middleware", "csrf"], 5 | "authors": [ 6 | { 7 | "name": "Aurélien Millet", 8 | "homepage": "http://www.aurelien-millet.fr/", 9 | "role": "Developer" 10 | } 11 | ], 12 | "homepage": "https://github.com/aurmil/slim3-csrf-utilities", 13 | "type": "library", 14 | "license": "MIT", 15 | "require": { 16 | "php": ">=5.5.0", 17 | "slim/csrf": "^0.8" 18 | }, 19 | "require-dev": { 20 | "slim/slim": "^3.3", 21 | "slim/php-view": "^2.1", 22 | "slim/twig-view": "^2.1" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Aurmil\\Slim\\": "src/" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/CsrfTokenToHeaders.php: -------------------------------------------------------------------------------- 1 | csrf = $csrf; 29 | } 30 | 31 | /** 32 | * @param Request $request 33 | * @param Response $response 34 | * @param callable $next 35 | * @return Response 36 | */ 37 | public function __invoke(Request $request, Response $response, callable $next) 38 | { 39 | $nameKey = $this->csrf->getTokenNameKey(); 40 | $valueKey = $this->csrf->getTokenValueKey(); 41 | $csrfToken = [ 42 | $nameKey => $request->getAttribute($nameKey), 43 | $valueKey => $request->getAttribute($valueKey) 44 | ]; 45 | 46 | if ($csrfToken[$nameKey] && $csrfToken[$valueKey]) { 47 | $response = $response->withHeader('X-CSRF-Token', json_encode($csrfToken)); 48 | } 49 | 50 | return $next($request, $response); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/CsrfTokenToView.php: -------------------------------------------------------------------------------- 1 | csrf = $csrf; 34 | $this->renderer = $renderer; 35 | } 36 | 37 | /** 38 | * @param Request $request 39 | * @param Response $response 40 | * @param callable $next 41 | * @return Response 42 | * @throws UnexpectedValueException 43 | */ 44 | public function __invoke(Request $request, Response $response, callable $next) 45 | { 46 | $nameKey = $this->csrf->getTokenNameKey(); 47 | $valueKey = $this->csrf->getTokenValueKey(); 48 | $csrfToken = [ 49 | $nameKey => $request->getAttribute($nameKey), 50 | $valueKey => $request->getAttribute($valueKey) 51 | ]; 52 | 53 | if ($csrfToken[$nameKey] && $csrfToken[$valueKey]) { 54 | // waiting for a possible Slim View Interface 55 | if ($this->renderer instanceof ArrayAccess) { 56 | $this->renderer['csrf_token'] = $csrfToken; 57 | } elseif (method_exists($this->renderer, 'addAttribute')) { 58 | $this->renderer->addAttribute('csrf_token', $csrfToken); 59 | } else { 60 | throw new UnexpectedValueException('Unsupported view renderer type.'); 61 | } 62 | } 63 | 64 | return $next($request, $response); 65 | } 66 | } 67 | --------------------------------------------------------------------------------