Skip to content

Commit

Permalink
Add CSRF Middleware
Browse files Browse the repository at this point in the history
Add csrf inputs on all forms
Use an exception rather than default blank page
Add CSRF check on ajax post requests
Make CSRF token persistent to ease with ajax calls
  • Loading branch information
trasher committed Nov 9, 2021
1 parent afd3c59 commit a5602bc
Show file tree
Hide file tree
Showing 49 changed files with 299 additions and 6 deletions.
3 changes: 2 additions & 1 deletion galette/composer.json
Expand Up @@ -50,7 +50,8 @@
"doctrine/annotations": "^1.8",
"laminas/laminas-servicemanager": "3.7",
"symfony/polyfill-php80": "^1.23",
"ezyang/htmlpurifier": "^4.13"
"ezyang/htmlpurifier": "^4.13",
"slim/csrf": "0.8.3"
},
"require-dev": {
"atoum/atoum": "dev-master",
Expand Down
106 changes: 105 additions & 1 deletion galette/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions galette/includes/dependencies.php
Expand Up @@ -432,6 +432,31 @@
)
);

$container->set(
'csrf',
function (ContainerInterface $c) {
$storage = null;
$guard = new \Slim\Csrf\Guard(
'csrf',
$storage,
null,
200,
16,
true
);

$guard->setFailureCallable(function ($request, $response, $next) {
Analog::log(
'CSRF check has failed',
Analog::CRITICAL
);
throw new \RuntimeException(_T('Failed CSRF check!'));
});

return $guard;
}
);

//For bad existing globals can be used...
global $translator, $i18n;
if (
Expand Down
2 changes: 2 additions & 0 deletions galette/includes/main.inc.php
Expand Up @@ -92,6 +92,8 @@

// Set up dependencies
require GALETTE_ROOT . '/includes/dependencies.php';
$app->add(new \Galette\Middleware\SmartyCsrf($app->getContainer()));
$app->add($app->getContainer()->get('csrf'));

if ($needs_update) {
$app->add(
Expand Down
96 changes: 96 additions & 0 deletions galette/lib/Galette/Middleware/SmartyCsrf.php
@@ -0,0 +1,96 @@
<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
* Galette CSRF middleware
*
* PHP version 5
*
* Copyright © 2021 The Galette Team
*
* This file is part of Galette (http://galette.tuxfamily.org).
*
* Galette is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Galette is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Galette. If not, see <http://www.gnu.org/licenses/>.
*
* @category Core
* @package Galette
*
* @author Johan Cwiklinski <johan@x-tnd.be>
* @copyright 2021 The Galette Team
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
* @link http://galette.tuxfamily.org
* @since Available since 0.9.6dev - 2021-11-08
*/

namespace Galette\Middleware;

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Analog\Analog;
use DI\Container;

/**
* Galette CSRF middleware
*
* @category Middleware
* @name SmartyCsrf
* @package Galette
* @author Johan Cwiklinski <johan@x-tnd.be>
* @copyright 2020 The Galette Team
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
* @link http://galette.tuxfamily.org
* @since Available since 0.9.4dev - 2020-05-06
*/
class SmartyCsrf
{
private $smarty;
private $csrf;

/**
* Constructor
*
* @param Container $container Container instance
*/
public function __construct(Container $container)
{
$view = $container->get('Slim\Views\Smarty');
$this->smarty = $view->getSmarty();
$this->csrf = $container->get('csrf');
}

/**
* Middleware invokable class
*
* @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
* @param \Psr\Http\Message\ResponseInterface $response PSR7 response
* @param callable $next Next middleware
*
* @return \Psr\Http\Message\ResponseInterface
*/
public function __invoke(Request $request, Response $response, $next): Response
{
$nameKey = $this->csrf->getTokenNameKey();
$valueKey = $this->csrf->getTokenValueKey();
$name = $request->getAttribute($nameKey);
$value = $request->getAttribute($valueKey);

$this->smarty->assign('csrf_name_key', $nameKey);
$this->smarty->assign('csrf_value_key', $valueKey);
$this->smarty->assign('csrf_name', $name);
$this->smarty->assign('csrf_value', $value);

return $next($request, $response);
}
}
3 changes: 2 additions & 1 deletion galette/templates/default/admintools.tpl
Expand Up @@ -31,10 +31,11 @@
</p>
</fieldset>
<div class="button-container">
<button ype="submit" class="action">
<button type="submit" class="action">
<i class="fas fa-database" aria-hidden="true"></i>
{_T string="Go"}
</button>
{include file="forms_types/csrf.tpl"}
</div>
</form>
{/block}
1 change: 1 addition & 0 deletions galette/templates/default/advanced_search.tpl
Expand Up @@ -312,6 +312,7 @@
<input type="hidden" name="advanced_filtering" value="true" />
<input type="submit" class="inline" value="{_T string="Filter"}"/>
<input type="submit" name="clear_adv_filter" class="inline" value="{_T string="Clear filter"}"/>
{include file="forms_types/csrf.tpl"}
</div>
</form>
{/block}
Expand Down
1 change: 1 addition & 0 deletions galette/templates/default/ajouter_contribution.tpl
Expand Up @@ -184,6 +184,7 @@
<input type="hidden" name="trans_id" value="{if $contribution->transaction neq NULL}{$contribution->transaction->id}{/if}"/>
</div>
{/if}
{include file="forms_types/csrf.tpl"}
</form>
{else} {* No members *}
<div class="center" id="warningbox">
Expand Down
1 change: 1 addition & 0 deletions galette/templates/default/ajouter_transaction.tpl
Expand Up @@ -46,6 +46,7 @@
</button>
<input type="hidden" name="trans_id" value="{$transaction->id}"/>
<input type="hidden" name="valid" value="1"/>
{include file="forms_types/csrf.tpl"}
</div>
<p>{_T string="NB : The mandatory fields are in"} <span class="required">{_T string="red"}</span></p>
</form>
Expand Down
1 change: 1 addition & 0 deletions galette/templates/default/attendance_sheet_details.tpl
Expand Up @@ -31,6 +31,7 @@
{foreach $selection as $member}
<input type="hidden" name="selection[]" value="{$member}"/>
{/foreach}
{include file="forms_types/csrf.tpl"}
</p>
</fieldset>
{if not $ajax}
Expand Down
1 change: 1 addition & 0 deletions galette/templates/default/change_passwd.tpl
Expand Up @@ -16,5 +16,6 @@
</table>
<input type="submit" name="change_passwd" value="{_T string="Change my password"}"/>
<input type="hidden" name="hash" value="{$hash}"/>
{include file="forms_types/csrf.tpl"}
</form>
{/block}
18 changes: 18 additions & 0 deletions galette/templates/default/common_scripts.tpl
@@ -1,5 +1,23 @@
<script type="text/javascript">
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$(function(){
$.ajaxPrefilter(function(options, originalOptions, jqXHR){
if (options.type.toLowerCase() === "post") {
// initialize `data` to empty string if it does not exist
options.data = options.data || "";
// add leading ampersand if `data` is non-empty
options.data += options.data?"&":"";
// add csrf
options.data += encodeURIComponent("{$csrf_name_key}") + "=" + encodeURIComponent("{$csrf_name}") + "&" + encodeURIComponent("{$csrf_value_key}") + "=" + encodeURIComponent("{$csrf_value}")
}
});
$.datepicker.setDefaults($.datepicker.regional['{$galette_lang}']);
{if $galette_lang eq 'en'}
$.datepicker.setDefaults({
Expand Down
1 change: 1 addition & 0 deletions galette/templates/default/config_fields.tpl
Expand Up @@ -55,6 +55,7 @@
<button type="submit" class="action">
<i class="fas fa-save fa-fw"></i> {_T string="Save"}
</button>
{include file="forms_types/csrf.tpl"}
</div>
</form>
{/block}
Expand Down
1 change: 1 addition & 0 deletions galette/templates/default/config_lists.tpl
Expand Up @@ -57,6 +57,7 @@
<i class="fas fa-save fa-fw"></i> {_T string="Save"}
</button>
</div>
{include file="forms_types/csrf.tpl"}
</form>
{/block}

Expand Down
1 change: 1 addition & 0 deletions galette/templates/default/confirm_removal.tpl
Expand Up @@ -30,6 +30,7 @@
<input type="hidden" name="{$key}" value="{$value}"/>
{/if}
{/foreach}
{include file="forms_types/csrf.tpl"}
</div>
</form>
</div>
Expand Down
1 change: 1 addition & 0 deletions galette/templates/default/directlink.tpl
Expand Up @@ -9,6 +9,7 @@
<input type="submit" name="directlink" value="{_T string="Get my document"}" />
<input type="hidden" name="valid" value="1"/>
<input type="hidden" name="hash" value="{$hash}"/>
{include file="forms_types/csrf.tpl"}
</section>
</form>
{/block}
1 change: 1 addition & 0 deletions galette/templates/default/edit_paymenttype.tpl
Expand Up @@ -16,6 +16,7 @@
</button>
<input type="submit" name="cancel" value="{_T string="Cancel"}"/>
<input type="hidden" name="id" id="id" value="{$ptype->id}"/>
{include file="forms_types/csrf.tpl"}
</div>
</form>
{/block}
1 change: 1 addition & 0 deletions galette/templates/default/edit_title.tpl
Expand Up @@ -20,6 +20,7 @@
</button>
<input type="submit" name="cancel" value="{_T string="Cancel"}"/>
<input type="hidden" name="id" id="id" value="{$title->id}"/>
{include file="forms_types/csrf.tpl"}
</div>
</form>
{/block}

0 comments on commit a5602bc

Please sign in to comment.