Slim Framework CSRF protection middleware

Related tags

Frameworks Slim-Csrf
Overview

Slim Framework CSRF Protection

Build Status Coverage Status

This repository contains a Slim Framework CSRF protection PSR-15 middleware. CSRF protection applies to all unsafe HTTP requests (POST, PUT, DELETE, PATCH).

You can fetch the latest CSRF token's name and value from the Request object with its getAttribute() method. By default, the CSRF token's name is stored in the csrf_name attribute, and the CSRF token's value is stored in the csrf_value attribute.

Install

Via Composer

$ composer require slim/csrf

Requires Slim 4.0.0 or newer.

Usage

In most cases you want to register Slim\Csrf for all routes, however, as it is middleware, you can also register it for a subset of routes.

Register for all routes

use DI\Container;
use Slim\Csrf\Guard;
use Slim\Factory\AppFactory;

require __DIR__ . '/vendor/autoload.php';

// Start PHP session
session_start();

// Create Container
$container = new Container();
AppFactory::setContainer($container);

// Create App
$app = AppFactory::create();
$responseFactory = $app->getResponseFactory();

// Register Middleware On Container
$container->set('csrf', function () use ($responseFactory) {
    return new Guard($responseFactory);
});

// Register Middleware To Be Executed On All Routes
$app->add('csrf');

$app->get('/foo', function ($request, $response, $args) {
    // CSRF token name and value
    $csrf = $this->get('csrf');
    $nameKey = $csrf->getTokenNameKey();
    $valueKey = $csrf->getTokenValueKey();
    $name = $request->getAttribute($nameKey);
    $value = $request->getAttribute($valueKey);

    /*
       Render HTML form which POSTs to /bar with two hidden input fields for the
       name and value:
       <input type="hidden" name="<?= $nameKey ?>" value="<?= $name ?>">
       <input type="hidden" name="<?= $valueKey ?>" value="<?= $value ?>">
     */
});

$app->post('/bar', function ($request, $response, $args) {
    // CSRF protection successful if you reached
    // this far.
});

$app->run();

Register per route

use DI\Container;
use Slim\Csrf\Guard;
use Slim\Factory\AppFactory;

require __DIR__ . '/vendor/autoload.php';

// Start PHP session
session_start();

// Create Container
$container = new Container();
AppFactory::setContainer($container);

// Create App
$app = AppFactory::create();
$responseFactory = $app->getResponseFactory();

// Register Middleware On Container
$container->set('csrf', function () use ($responseFactory) {
    return new Guard($responseFactory);
});

$app->get('/api/route',function ($request, $response, $args) {
    $csrf = $this->get('csrf');
    $nameKey = $csrf->getTokenNameKey();
    $valueKey = $csrf->getTokenValueKey();
    $name = $request->getAttribute($nameKey);
    $value = $request->getAttribute($valueKey);

    $tokenArray = [
        $nameKey => $name,
        $valueKey => $value
    ];
    
    return $response->write(json_encode($tokenArray));
})->add('csrf');

$app->post('/api/myEndPoint',function ($request, $response, $args) {
    //Do my Things Securely!
})->add('csrf');

$app->run();

Manual usage

If you are willing to use Slim\Csrf\Guard outside a Slim\App or not as a middleware, be careful to validate the storage:

use Slim\Csrf\Guard;
use Slim\Psr7\Factory\ResponseFactory;

// Start PHP session
session_start();

// Create Middleware
$responseFactory = new ResponseFactory(); // Note that you will need to import
$guard = new Guard($responseFactory);

// Generate new tokens
$csrfNameKey = $guard->getTokenNameKey();
$csrfValueKey = $guard->getTokenValueKey();
$keyPair = $guard->generateToken();

// Validate retrieved tokens
$guard->validateToken($_POST[$csrfNameKey], $_POST[$csrfValueKey]);

Token persistence

By default, Slim\Csrf\Guard will generate a fresh name/value pair after each request. This is an important security measure for certain situations. However, in many cases this is unnecessary, and a single token throughout the user's session will suffice. By using per-session requests it becomes easier, for example, to process AJAX requests without having to retrieve a new CSRF token (by reloading the page or making a separate request) after each request. See issue #49.

To use persistent tokens, set the sixth parameter of the constructor to true. No matter what, the token will be regenerated after a failed CSRF check. In this case, you will probably want to detect this condition and instruct your users to reload the page in their legitimate browser tab (or automatically reload on the next failed request).

Accessing the token pair in templates (Twig, etc)

In many situations, you will want to access the token pair without needing to go through the request object. In these cases, you can use getTokenName() and getTokenValue() directly on the Guard middleware instance. This can be useful, for example in a Twig extension:

use Slim\Csrf\Guard;

class CsrfExtension extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface
{
    /**
     * @var Guard
     */
    protected $csrf;
    
    public function __construct(Guard $csrf)
    {
        $this->csrf = $csrf;
    }

    public function getGlobals()
    {
        // CSRF token name and value
        $csrfNameKey = $this->csrf->getTokenNameKey();
        $csrfValueKey = $this->csrf->getTokenValueKey();
        $csrfName = $this->csrf->getTokenName();
        $csrfValue = $this->csrf->getTokenValue();
        
        return [
            'csrf'   => [
                'keys' => [
                    'name'  => $csrfNameKey,
                    'value' => $csrfValueKey
                ],
                'name'  => $csrfName,
                'value' => $csrfValue
            ]
        ];
    }
}

Once you have registered your extension, you may access the token pair in any template:

<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">

Handling validation failure

By default, Slim\Csrf\Guard will return a Response with a 400 status code and a simple plain text error message.

To override this, provide a callable as the third parameter to the constructor or via setFailureHandler(). This callable has the same signature as middleware: function($request, $handler) and must return a Response.

For example:

use Slim\Csrf\Guard;
use Slim\Psr7\Factory\ResponseFactory;

$responseFactory = new ResponseFactory();
$guard = new Guard($responseFactory);
$guard->setFailureHandler(function (ServerRequestInterface $request, RequestHandlerInterface $handler) {
    $request = $request->withAttribute("csrf_status", false);
    return $handler->handle($request);
});

In this example, an attribute is set on the request object that can then be checked in subsequent middleware or the route callable using:

if (false === $request->getAttribute('csrf_status')) {
    // display suitable error here
} else {
    // successfully passed CSRF check
}

Testing

$ phpunit

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

Comments
  • Add Persistence Option / Guard for Tokens

    Add Persistence Option / Guard for Tokens

    It would be great to have a way to 'persist' tokens for subsequent attempts on requests. Please take a look at this issue which outlines a valid use case for such a scenario.

    Via @geggleto

    The fix is a guard around [ https://github.com/slimphp/Slim-Csrf/blob/master/src/Guard.php#L204 ], using what I think would be a controllable setting. We would keep the default currently which would be "strict" => true

    opened by ryanscherler 24
  • How to validate subsequent AJAX requests?

    How to validate subsequent AJAX requests?

    It seems to work on the first submit, but if submitted again, the tokens are invalid. Any strategies on how to refresh tokens for subsequent Ajax requests with the need for 'refreshed' tokens to pass along?

    opened by ryanscherler 16
  • Session Expires Quickly

    Session Expires Quickly

    Hello,

    I am using the Gaurd with the following arguments:

    new \Slim\Csrf\Guard('csrf', $_, function(Request $request, Response $response, $next) {
      return $response
        ->withStatus(400)
        ->withHeader('Content-type', 'text/plain')
        ->write('Security check failed. Please refresh the page.');
    }, 200, 16, true));
    

    I have users reporting that when they leave the page sit 30–60 minutes, the CSRF check fails on the next API request. I have been able to reproduce this on Mobile Safari on iOS. As far as I know, nothing is killing the session, as the user is still logged in. Any idea why this would be happening? I don't see anything in the code about how long to keep the tokens, so I would expect them to last as long as the session.

    opened by rafecolton 15
  • Option to whitelist routes

    Option to whitelist routes

    Right now, we can use the CSRF middleware in one of two ways: either applied to all routes, or on a route-by-route basis.

    It would be nice if there were an option to whitelist certain routes, so that CSRF checks are performed on all routes except those explicitly listed.

    opened by alexweissman 12
  • CSFR tokens for multiple forms\post per page (Ajax)

    CSFR tokens for multiple forms\post per page (Ajax)

    I have a Page that dynamically loads multiple forms via ajax let say each form is supposed to be a like button,

    so when CSFR is disabled forms works fine , and no exception is thrown ,but when enabled the problem appear the problem is the first request will work because the tokens will match but since ajax will not refresh the page the other forms will still have the old token so any subsquent request will not work and "Failed CSRF check!" exception will be thrown , i searched a lot for a solution and i find a tedious one which is to return the new csrf in the response to the AJAX post, then update the value of the csrf token field but it is not a fancy soulotion besides im using a CSRF middleware to append the values automaticly

    ` public function __invoke($request, $response, $next){

        $nameKey = $this->container->csrf->getTokenNameKey();
        $valueKey = $this->container->csrf->getTokenValueKey();
    
        $name = $this->container->csrf->getTokenName();
        $value = $this->container->csrf->getTokenValue();
    
        // Render HTML form which POSTs to /bar with two hidden input fields for the
        // name and value:
        $output  = '<input type="hidden" name="'.$nameKey .'" value="'.$name .'">';
        $output .= '<input type="hidden" name="'.$valueKey.'" value="'.$value.'">';
    
        // Append The CSRF Gards To The View
        $this->container->view->addAttribute('csrf_gards', $output );
    
        // Pass the Request to the next one
        $response = $next($request, $response);
        return $response;
    }`
    

    a nice approach like CodeIgniter would be to allow this kind of behavior $config['csrf_regenerate'] = FALSE;

    so any help would be appreciated

    opened by the-dijkstra 11
  • Selectable methods

    Selectable methods

    I think the ability to check against GET and other Methods is really nice if you wanna beef up security even more. So here a added a optional parameter array of which methods we wanna check in this function.

    To find there variables from other the post data field, we check Headers and query instead.

    opened by deployHuman 10
  • Review

    Review

    Hi Josh Lockhart,

    I don't believe mt_rand is a nice candidate. So the line

    $token = hash("sha512", mt_rand(0, mt_getrandmax()));
    

    seems not safe to me. I don't recall the discussion thread, but there was one.

    It was also in @auraphp Session . See commit https://github.com/auraphp/Aura.Session/commit/7e3c8360ff4f5f34d09d87a599a1ac527dbb054a

    Later iirc after reviewed by ircmaxwell , it was changed to make use of a RandomInterface, via which you can incorporate something like RandomLib or Random generators.

    It will be nice, if there is a way we can incorporate libraries. So only need to deal with one library for security.

    https://github.com/auraphp/Aura.Session#defending-against-csrf

    Thanks!

    opened by harikt 9
  • PHP8 Support

    PHP8 Support

    Hey, I ruined into the following error: Problem 1 - Root composer.json requires slim/csrf ^1.0.0 -> satisfiable by slim/csrf[1.0.0]. - slim/csrf 1.0.0 requires php ^7.1 -> your php version (8.0.0) does not satisfy that requirement.

    My 'testing' solution is removing the required for now and add it manually as remote repository in composer.

    Besides that PHP7.1 is (if im right) EOL.

    Thanks, Maxi

    opened by schiederme 8
  • Security Access - Token Generation

    Security Access - Token Generation

    I don't understand about generation of security tokens. Example:

    $app->get('/foo', function ($request, $response, $args) { // CSRF token name and value $nameKey = $this->csrf->getTokenNameKey(); $valueKey = $this->csrf->getTokenValueKey(); $name = $request->getAttribute($nameKey); $value = $request->getAttribute($valueKey); });

    $app->post('/bar', function ($request, $response, $args) { // CSRF protection successful if you reached // this far. });

    If /foo is public, anyone can generate a token and use to valid the access to another method (/bar). Any help about it?

    opened by ericciaparicio 8
  • How to validate AJAX with CSRF ?

    How to validate AJAX with CSRF ?

    Hello guys I have a little problem I am using slim 3 framework along with slim-csrf what I did is registering the csrf to the container I aslo created a csrfExtension that extends the twigExtension. in the csrfExtenstion class I created a method that return the csrf fields neede markup this way I don't need to retype the whole markup in my forms I just call the function that I created and it works fine.

    Now the problem is in my website admins-panel page there is a lot of ajax requests that I want to apply the csrf on here is where the problem appears the csrf only works for the first request but it will give an error on any other request cause the csrf_name and csrf_value that I am picking from my view has expired so how to get the new csrf_name and csrf_value so I can pass them along with my other ajax requests.

    hope you can help me Thanks in advance

    opened by Istanbouly 8
  • Token is not deleted after validation

    Token is not deleted after validation

    Until version 0.8.3 the used token is removed:

            // If we're not in persistent token mode, delete the token.
            if (!$this->persistentTokenMode) {
                $this->removeFromStorage($name);
            }
    

    (https://github.com/slimphp/Slim-Csrf/blob/0.8.3/src/Guard.php#L254-L257) and a new token is generated after validation:

    // Need to regenerate a new token, as the validateToken removed the current one.
    $request = $this->generateNewToken($request);
    

    (https://github.com/slimphp/Slim-Csrf/blob/0.8.3/src/Guard.php#L152-L153)

    Is there any particular reason why the token is no longer deleted in the latest version for slim 4?

    opened by Trekky12 7
  • CSRF always fails in 1.3.0

    CSRF always fails in 1.3.0

    https://github.com/slimphp/Slim-Csrf/blob/ebaaf295fd6d7224078d8ae3bba45329b31798c7/src/Guard.php#L240

    I'm not really that skilld with Xor operators, but the middleware used to work perfectly before

    PHP 8.1.4

    opened by mijorus 5
Releases(1.3.0)
Owner
Slim Framework
Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs.
Slim Framework
Juliangut Slim Framework Doctrine handler middleware

Juliangut Slim Framework Doctrine handler middleware Doctrine handler middleware for Slim Framework. Slim3 version Doctrine integration service for Sl

Julián Gutiérrez 6 Mar 23, 2021
This Slim Framework middleware will compile LESS CSS files on-the-fly using the Assetic library

This Slim Framework middleware will compile LESS CSS files on-the-fly using the Assetic library. It supports minification and caching, also via Asseti

Gerard Sychay 22 Mar 31, 2020
Access control middleware for Slim framework

Slim Access Access control middleware for Slim framework. Supported formats IPv4 and IPv6 addresses CIDR notation all keyword Installation composer re

Alexandre Bouvier 7 Oct 22, 2019
Redis cache middleware for Slim framework

RedisCache Redis cache middleware for Slim framework. Installation composer require abouvier/slim-redis-cache Usage Cache every successful HTTP respo

Alexandre Bouvier 16 Sep 20, 2021
Slim Framework HTTP cache middleware and service provider

Slim Framework HTTP Cache This repository contains a Slim Framework HTTP cache middleware and service provider. Install Via Composer $ composer requir

Slim Framework 107 Dec 15, 2022
CORS Middleware for PHP Slim Framework

CorsSlim Cross-origin resource sharing (CORS) Middleware for PHP Slim Framework. Usage Composer Autoloader Install with Composer Update your composer.

Palani Kumanan 92 Nov 30, 2021
PHP slim framework middleware to minify HTML output

slim-minify Slim middleware to minify HTML output generated by the slim PHP framework. It removes whitespaces, empty lines, tabs beetween html-tags an

Christian Klisch 35 Oct 31, 2021
Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs.

Slim Framework Slim is a PHP micro-framework that helps you quickly write simple yet powerful web applications and APIs. Installation It's recommended

Slim Framework 11.5k Jan 4, 2023
Slim Framework - Prerequisite Checker

Slim Framework - Server Configuration Checker Upload the file check.php to your webserver Browse to the file: https://example.com/check.php Check the

Daniel Opitz 6 Aug 30, 2022
REST APIs using Slim framework. Implemented all CRUD operations on the MySql database

PHP REST API using slim framework and CRUD operations ?? Hi there, this is a simple REST API built using the Slim framework. And this is for the folks

Hanoak 2 Jun 1, 2022
A Slim PHP MVC framework built just for fun!

Aura Framework A Slim PHP MVC framework built just for fun! en: Note: This repository only contains the core code of the Aura framework. If you want t

Murilo Magalhães Barreto 2 Dec 16, 2021
Slim Framework skeleton application with MVC Schema

Slim Framework skeleton application with MVC Schema

JingwenTian 9 Apr 29, 2021
A curated list of awesome tutorials and other resources for the Slim micro framework

Awesome Slim A curated list of awesome tutorials and other resources for the Slim micro framework Table of Contents Essentials Tutorials Packages and

Sawyer Charles 466 Dec 8, 2022
Slim Framework 3 Skeleton Application + PagSeguro Lib

Slim Framework 3 Skeleton Application + PagSeguro Lib Aplicação simples para geração do Token para pagamentos no PagSeguro (método transparente) e env

Raí Siqueira 1 Feb 26, 2018
My personal blog developed on the Slim Framework

nesbot.com I am making the source code of my personal site available publicly in case it helps anybody. It's developed using the Slim Framework. I am

Brian Nesbitt 56 Sep 14, 2022
Plates Template Integration for Slim micro framework 3

Plates Template Integration for Slim micro framework 3 Render your Slim 3 application views using Plates template engine. Install Via Composer $ compo

Projek XYZ 26 Feb 5, 2022
Strict PSR-7 implementation used by the Slim Framework

Strict PSR-7 implementation used by the Slim Framework, but you may use it separately with any framework compatible with the PSR-7 standard.

Slim Framework 96 Nov 14, 2022
Slim Framework custom views

Slim Views This repository contains custom View classes for the template frameworks listed below. You can use any of these custom View classes by eith

Slim Framework 302 Feb 21, 2022
Slim Framework using Jade for templates

Welcome to Slim-Jade What? This is a boilerplate. It's the Slim Framework with jade.php to render views Why? I like the Sinatra style MVC and love Jad

Joe Fleming 23 Oct 16, 2019