diff --git a/README.md b/README.md index 057fd71..7451bca 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![run-tests](https://github.com/protonemedia/laravel-xss-protection/actions/workflows/run-tests.yml/badge.svg)](https://github.com/protonemedia/laravel-xss-protection/actions/workflows/run-tests.yml) [![Total Downloads](https://img.shields.io/packagist/dt/protonemedia/laravel-xss-protection.svg?style=flat-square)](https://packagist.org/packages/protonemedia/laravel-xss-protection) -Laravel Middleware to protect your app against Cross-site scripting (XSS). It sanitizes request input by utilising the [Security Core](https://github.com/GrahamCampbell/Security-Core) package, and it can sanatize [Blade echo statements](https://laravel.com/docs/8.x/blade#displaying-data) as well. +Laravel Middleware to protect your app against Cross-site scripting (XSS). It sanitizes request input by utilising the [voku/anti-xss](https://github.com/voku/anti-xss) package, and it can sanatize [Blade echo statements](https://laravel.com/docs/8.x/blade#displaying-data) as well. This package was inspired by the [Security Core](https://github.com/GrahamCampbell/Security-Core) package. * PHP 8.2 and higher * Laravel 10 and higher @@ -106,7 +106,7 @@ Event::listen(function (MaliciousInputFound $event) { ### Additional configuration for `voku/anti-xss` -As of version 1.6.0, you may provide additional configuration for the `voku/anti-xss` package. You may do this by filling the `middleware.anti_xss` key. This is similar to the [Laravel Security](https://github.com/GrahamCampbell/Laravel-Security) package, which this package used to rely on. +As of version 1.6.0, you may provide additional configuration for the `voku/anti-xss` package. You may do this by filling the `middleware.anti_xss` key. ```php 'anti_xss' => [ diff --git a/composer.json b/composer.json index a9cedda..5c4d5dd 100644 --- a/composer.json +++ b/composer.json @@ -17,9 +17,10 @@ ], "require": { "php": "^8.2|^8.3|^8.4", - "graham-campbell/security-core": "^4.0", "illuminate/contracts": "^10.0|^11.0|^12.0", - "spatie/laravel-package-tools": "^1.9.2" + "spatie/laravel-package-tools": "^1.9.2", + "voku/anti-xss": "~4.1.42", + "voku/portable-utf8": "^6.0.13" }, "require-dev": { "laravel/pint": "^1.14", diff --git a/src/Middleware/XssCleanInput.php b/src/Middleware/XssCleanInput.php index e8b81a8..741cc34 100644 --- a/src/Middleware/XssCleanInput.php +++ b/src/Middleware/XssCleanInput.php @@ -3,11 +3,12 @@ namespace ProtoneMedia\LaravelXssProtection\Middleware; use Closure; -use GrahamCampbell\SecurityCore\Security; use Illuminate\Foundation\Http\Middleware\TransformsRequest; use ProtoneMedia\LaravelXssProtection\Cleaners\BladeEchoes; use ProtoneMedia\LaravelXssProtection\Events\MaliciousInputFound; use Symfony\Component\HttpFoundation\File\UploadedFile; +use voku\helper\AntiXSS; +use voku\helper\UTF8; class XssCleanInput extends TransformsRequest { @@ -53,7 +54,7 @@ class XssCleanInput extends TransformsRequest * @return void */ public function __construct( - protected Security $security, + protected AntiXSS $antiXss, protected BladeEchoes $bladeEchoCleaner ) { // @@ -131,7 +132,11 @@ protected function transform($key, $value) return null; } - $output = $this->security->clean((string) $value); + $output = $this->antiXss->xss_clean((string) $value); + + if ($this->antiXss->isXssFound() === false) { + $output = $this->cleanInvisibleCharacters($output); + } if (! $this->enabledInConfig('allow_blade_echoes')) { $output = $this->bladeEchoCleaner->clean((string) $output); @@ -187,4 +192,22 @@ public static function clearCallbacks() static::$skipKeyCallbacks = []; } + + /** + * Clean invisible characters from the input. + * + * @param string|array $input + */ + private function cleanInvisibleCharacters($input): string|array + { + if (is_array($input)) { + foreach ($input as $key => &$value) { + $value = $this->cleanInvisibleCharacters($value); + } + + return $input; + } + + return UTF8::remove_invisible_characters($input, true); + } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 96a36b5..b3ea3b0 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -2,9 +2,10 @@ namespace ProtoneMedia\LaravelXssProtection; -use GrahamCampbell\SecurityCore\Security; +use ProtoneMedia\LaravelXssProtection\Middleware\XssCleanInput; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; +use voku\helper\AntiXSS; class ServiceProvider extends PackageServiceProvider { @@ -22,9 +23,29 @@ public function configurePackage(Package $package): void public function packageBooted() { - $this->app->singleton(Security::class, fn () => Security::create( - config('xss-protection.anti_xss.evil'), - config('xss-protection.anti_xss.replacement') - )); + $this->app->when(XssCleanInput::class) + ->needs(AntiXSS::class) + ->give(function () { + $antiXss = new AntiXSS; + + $replacement = config('xss-protection.anti_xss.replacement'); + + if ($replacement !== null) { + $antiXss->setReplacement($replacement); + } + + $evil = config('xss-protection.anti_xss.evil'); + + if ($evil !== null) { + if (isset($evil['attributes']) || isset($evil['tags'])) { + $antiXss->addEvilAttributes($evil['attributes'] ?? []); + $antiXss->addEvilHtmlTags($evil['tags'] ?? []); + } else { + $antiXss->addEvilAttributes($evil); + } + } + + return $antiXss; + }); } }