In my case I am saving a URL into my database via Eloquent Model:
namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; class PageLink extends Model { use HasFactory; protected $table='links'; public function setUrlAttribute(string $url):void { $saneUrl = trim($url); $saneUrl = filter_var($url, FILTER_SANITIZE_URL); $saneUrl = preg_replace("/javascript:.*/","",$saneUrl); $saneUrl = htmlspecialchars($url); $saneUrl = Str::of($url)->stripTags()->toString(); /** * Regex was found into https://stackoverflow.com/a/36564776 * The idea is to extract the url from attack vectors such as: * * "http://example.com\"/> <IMG SRC= onmouseover=\"alert('xxs')\"/> * * This allows me to make the entry safe */ preg_match_all('/https?\:\/\/[^\" ]+/i', $saneUrl, $match); if(is_array($match[0]) && isset($match[0][0])){ $saneUrl=$match[0][0]; }elseif (is_string($match[0])){ $saneUrl=$match[0]; }else { $saneUrl=""; } $this->attributes['url']=$saneUrl; } }
And in my blade view it is rendered as:
<a href="{{$link->url}}">{{$link->url}}</a>
But also will be provided via AJAX API as well (that is under development). Therefore I want to sanitize my input upon a valid URL. I do not validate it here because my controller does this for me:
use Illuminate\Contracts\Validation\Validator; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; Route::post("/url",function(Request $request){ $validator = Validator::create($request->all(),[ 'url'=>"required|url" ],[ 'url'=>"Δεν δώσατε έναν έγγυρο σύνδεσμο" ]); if($validator->fails()){ return new JsonResponse(['msg'=>$validator->error()],400); } $link = new Link(); $link->url = $request->get('url'); try{ $link->save(); }catch(\Exception $e){ report($e); return new JsonResponse(["msg"=>"Link fails to be saved"],500); } return new JsonResponse($link,201); });
And here is a unit test for the model:
namespace Tests\Feature\Unit\Model; use App\Models\Link; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithFaker; use Tests\TestCase; class UserPageLinkTest extends TestCase { use RefreshDatabase; public static function urlProvider(): array { return [ ['http://example.com<script>alert("XSS");</script>'], ["http://example.com\" onfocus=\"alert(\"Hello\")\""], ["https://example.com\' onfocus='alert('Hello')'"], ["<script>alert(\"XSS\")</script>"], ["http://example.com\"/> <IMG SRC= onmouseover=\"alert('xxs')\"/>"], ["javascript:alert(String.fromCharCode(88,83,83))"] ]; } /** * @dataProvider urlProvider */ public function testUrlSanitizedFromXss($input): void { $model = new Link(); $model->url = $input; $saneUrl = $model->url; $this->assertNotEquals($saneUrl,$input); } }
The idea behind the code is to make a minimal/lightweight CMS that manages some fixed pages.
In my case could I omit any other filter and just use a regular expression pattern and still be safe from XSS attacks in the input?
JaVaScRiPt:
orjavajavascriptscript:
orjava<new line>script:
This is why @YourCommonSense's answer is so important. Otherwise you'll be playing an endless cat-and-mouse game.\$\endgroup\$