Filament security: checklist voor productie (2026)
Terug naar blog

Filament security: checklist voor productie (2026)

10 min leestijd

Filament beveiligen begint met één methode: canAccessPanel(). Zonder die check kan in productie niemand inloggen. Mét een slordige return true kan iedereen met een account erin. Daarna volgen policies, MFA, file uploads en updates. In 2026 publiceerde Filament al elf security advisories. Updaten...

Filament beveiligen begint met één methode: canAccessPanel(). Zonder die check kan in productie niemand inloggen. Mét een slordige return true kan iedereen met een account erin. Daarna volgen policies, MFA, file uploads en updates. In 2026 publiceerde Filament al elf security advisories. Updaten is dus geen bijzaak.

Onze eigen backoffice draait op Filament: blog, leads, afspraken, cases en een database met Magento-shops. Deze checklist loop je af vóór elke livegang. Hij geldt voor Filament v4 en v5. Nieuw met Filament? Begin dan bij wat Filament is en wanneer je het inzet.

Waarom is canAccessPanel verplicht in productie?

Buiten de local-omgeving weigert Filament elke gebruiker die het FilamentUser-contract niet implementeert. Volgens de deployment-docs kan dan "niemand inloggen in productie". De echte valkuil is de snelle fix: return true. Dan krijgt elk account in je users-tabel toegang tot het panel.

Dat is extra gevaarlijk als dezelfde tabel ook klanten bevat. Denk aan een webshop-backoffice of een klantportaal. Een klant die zich registreert, logt dan zo in op je admin.

De correcte opzet, volgens de Filament-docs:

php
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements FilamentUser
{
    public function canAccessPanel(Panel $panel): bool
    {
        if ($panel->getId() === 'admin') {
            return $this->is_admin && $this->hasVerifiedEmail();
        }

        return false;
    }
}

Drie regels die wij hanteren:

  • Check altijd $panel. Bij meerdere panels (admin, klantportaal) moet elk panel een eigen regel hebben.
  • Default false. Een nieuw panel is dan dicht tot je het bewust opent.
  • Geen e-mail-domeincheck alleen. str_ends_with($email, '@bedrijf.nl') werkt alleen als registratie uit staat of e-mail geverifieerd is.

Hoe stel je MFA in Filament in?

Filament heeft sinds v4 ingebouwde multi-factor authentication. Je kiest uit een authenticator-app (TOTP, zoals Google Authenticator of Microsoft Authenticator) of een code per e-mail. Er is geen extra package nodig.

php
use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Auth\MultiFactor\Email\EmailAuthentication;

->multiFactorAuthentication([
    AppAuthentication::make()->recoverable(),
    EmailAuthentication::make(),
], isRequired: true)

Wat je nog meer nodig hebt:

  • App-MFA: HasAppAuthentication interface, InteractsWithAppAuthentication trait en een kolom app_authentication_secret.
  • Recovery codes: HasAppAuthenticationRecovery, de bijbehorende trait en een kolom app_authentication_recovery_codes.
  • E-mail-MFA: HasEmailAuthentication, de trait en een boolean has_email_authentication.

Zet isRequired: true aan voor elk panel met klantdata of orders. Optionele MFA slaan gebruikers in de praktijk vaak over.

De eerlijke versie: Filament zegt zelf dat MFA alleen binnen de Filament-loginflow geldt. Heb je ook een API of andere login-routes? Die vallen er buiten. Daar regel je MFA of token-beveiliging apart, bijvoorbeeld via Sanctum of Passport.

Belangrijk: juist de MFA-code had in 2025-2026 de meeste advisories. Daarover meer bij updates.

Werkt Filament automatisch met Laravel policies?

Ja, voor resources. Filament leest je model policies voor viewAny, view, create, update, delete, deleteAny, restore, forceDelete en reorder. Maar ontbreekt een policy, dan geeft Filament standaard gewoon toegang.

Dat staat letterlijk in de panel-docs: zonder policy "gaat Filament ervan uit dat je authorization nog niet hebt ingesteld" en laat de gebruiker door. Eén vergeten make:policy en een resource staat open voor elke panel-gebruiker.

De oplossing is één regel in je panel provider:

php
->strictAuthorization()

Daarmee gooit Filament een exception als een policy of policy-methode ontbreekt. Je merkt het dus in development, niet in een incident.

Wat strictAuthorization() níet afdekt, volgens de Filament security-docs:

  • Custom pages en widgets. Die gebruiken canAccess() of canView(). Die moet je zelf implementeren.
  • Custom actions. Gebruik authorize(), visible() of hidden(). Een action zonder check is voor iedereen uitvoerbaar.
  • Inline-editable kolommen. ToggleColumn, TextInputColumn en SelectColumn checken géén policy. Alleen de disabled()-state telt.
  • Bulk actions. Die gebruiken standaard deleteAny() en niet per record delete(). Wil je per record checken? Gebruik authorizeIndividualRecords().

Werk je met rollen en permissies? Dan is Filament Shield de gangbare route. We schreven er een apart stuk over: rollen en rechten in Filament met Shield.

Hoe zit het met mass assignment en verborgen velden?

Filament slaat alleen velden op die in je form-schema staan. Dat beperkt mass assignment, maar het lost niet alles op. Het grotere risico zit in data die je per ongeluk naar de browser stuurt.

Livewire stuurt alle niet-$hidden model-attributen mee naar de frontend. Volgens de Filament-docs kunnen gebruikers die niet via mass assignment wijzigen. Maar ze kunnen ze wel lezen in de browser. Denk aan API-keys, interne notities of marge-velden.

Concreet:

  • Zet gevoelige kolommen in $hidden op het model, of filter ze in mutateFormDataBeforeFill().
  • Gebruik geen globale Model::unguard(). Veel Filament-tutorials doen dit voor gemak. Het schakelt $fillable voor je hele app uit, ook buiten Filament.
  • Vertrouw niet op hidden() of disabled() in een form als beveiliging. Het is UI. Echte afscherming zit in policies en query scoping.
  • Scope je queries. Filament scoped alleen automatisch bij ingebouwde tenancy. Custom pages, actions en relation queries scope je zelf via getEloquentQuery() of modifyQueryUsing().

Hoe beveilig je file uploads in Filament?

Het FileUpload-veld accepteert standaard elk bestandstype. De Filament-docs waarschuwen expliciet: op een local of public disk kan een aanvaller een .php-bestand uploaden en uitvoeren. Dat is remote code execution.

Een veilige basis:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('invoice')
    ->disk('s3')
    ->visibility('private')
    ->acceptedFileTypes(['application/pdf'])
    ->maxSize(5120)
    ->preventFilePathTampering()
    ->storeFileNamesIn('invoice_original_name');

Waarom elke regel:

  • acceptedFileTypes() activeert de mimetypes-validatie en blokkeert PHP-extensies. Voor afbeeldingen volstaat ->image().
  • maxSize() in kilobytes. Stem dit af op upload_max_filesize en post_max_size in php.ini.
  • preventFilePathTampering() omdat de waarde van het veld een client-controlled string is. Zonder deze check kan een gebruiker naar andere bestanden op dezelfde disk verwijzen.
  • storeFileNamesIn() in plaats van preserveFilenames(). Originele bestandsnamen op disk zijn op local of public een RCE-risico.
  • S3 of een private disk. Bestanden op S3 worden niet door PHP uitgevoerd.

Heb je eigen Livewire-pages met schemas? Voeg dan de trait RestrictsFileUploadsToSchemaComponents toe. Anders kan een aanvaller uploads doen naar willekeurige properties. Voor de bredere Laravel-kant van uploads zie beveiligde file uploads in Laravel.

Moet je het panel-pad /admin veranderen?

Een ander pad via ->path('beheer-x7') stopt geautomatiseerde scans. Het stopt geen gerichte aanval. Zie het als ruisreductie, niet als beveiliging.

Wat wel echt helpt:

  • ->domain('admin.jouwbedrijf.nl') zet het panel op een eigen subdomein. Daarop kun je aparte firewall-regels of een IP-allowlist zetten.
  • IP-restrictie op webserver- of Cloudflare-niveau voor interne panels. Een backoffice die alleen vanaf kantoor en VPN bereikbaar is, heeft een veel kleiner aanvalsoppervlak.
  • Eigen middleware via ->middleware() of ->authMiddleware() op het panel, bijvoorbeeld voor een CSP-header.

Heeft Filament rate limiting op de login?

Ja. De loginpagina heeft ingebouwde rate limiting: 5 pogingen per minuut per IP-adres. Het wachtwoord-reset-formulier staat op 2 per minuut. Dat zie je in de broncode van Filament\Auth\Pages\Login en RequestPasswordReset.

De valkuil zit in "per IP". Draai je achter Cloudflare, een load balancer of een andere proxy? Dan moet Laravel's TrustProxies goed staan. Anders ziet Filament alle bezoekers als hetzelfde IP-adres. Gevolg: één aanvaller blokkeert iedereen, of de limiet werkt helemaal niet.

Bovenop de ingebouwde limiet adviseren wij:

  • MFA verplicht, zodat een gelekt wachtwoord niet genoeg is.
  • Registratie uit (->registration() niet aanroepen) op interne panels.
  • Een WAF-regel op het login-pad bij verdachte volumes.

Heeft Filament audit logging?

Nee. Filament logt niet standaard wie wat heeft gewijzigd. Voor elk panel met orders, prijzen of klantdata wil je dat wel. Bij een incident is "wie heeft dit veranderd?" de eerste vraag.

De gangbare route is spatie/laravel-activitylog op je modellen, plus een Filament-plugin of eigen resource om de log te tonen. Log minimaal:

  • Create, update en delete op gevoelige modellen, met oude en nieuwe waarden.
  • Login-pogingen en MFA-wijzigingen.
  • Bulk actions en exports. Een export van je hele klantenbestand is een datalek-risico.

Exports verdienen extra aandacht. In Filament v3 (vóór 3.2.123) kwamen geëxporteerde bestanden standaard op de public disk terecht (advisory GHSA-4hxw-gc2q-f6f3). Controleer waar jouw exports landen. Meer over exports in import en export in Filament.

Welke Filament security advisories zijn er bekend?

Filament publiceert advisories via GitHub Security Advisories op de filamentphp/filament-repository. Wij telden er per 6 oktober 2026 veertien. Elf daarvan verschenen in 2026, de meeste rond MFA en XSS.

AdvisoryDatumErnstWatOpgelost in
GHSA-7m6h-rg42-m44923-09-2026MediumMFA-beheer vraagt geen wachtwoord opnieuw4.13.3 / 5.8.3
GHSA-52xp-w8hr-xv3c17-08-2026HighMFA (app) te omzeilen met recovery codes aan4.12.0 / 5.7.0
GHSA-r3j6-gpjw-qfjr17-08-2026MediumOude MFA-code bruikbaar na nieuwere code4.12.6 / 5.7.6
GHSA-xwpv-pqxp-5v3617-08-2026LowWachtwoord-validiteit lekt bij geweigerde panel-toegang4.12.5 / 5.7.5
GHSA-m9cv-24rx-8mv717-06-2026HighXSS via disabled RichEditor (v3)forms 3.3.53
GHSA-mc5j-f6wx-h9qh23-05-2026HighRecovery codes meermaals bruikbaar via gelijktijdige requests4.11.5 / 5.6.5
GHSA-44wp-g8f4-f4v523-05-2026MediumOngeauthenticeerde tijdelijke upload op auth-pagina's3.3.52 / 4.11.5 / 5.6.5
GHSA-5w46-g9pq-wh6f23-05-2026MediumUser enumeration via timing op loginpagina4.11.5 / 5.6.5
GHSA-vv3x-j2x5-36jc18-03-2026HighXSS via Range/Values summarizers4.8.5 / 5.3.5
GHSA-9h9q-qhxg-89xr27-09-2024CriticalXSS via ColorColumn/ColorEntry (v3)3.2.115

Niet alle veertien staan in de tabel. De volledige lijst staat op github.com/filamentphp/filament/security/advisories.

De rekenstap is simpel. Zit je op v5 en lager dan 5.8.3, of op v4 lager dan 4.13.3? Dan heb je minstens één bekende kwetsbaarheid. De nieuwste release op Packagist is 5.9.0 (26 september 2026). Draai je nog v3? Lees dan Filament 3 upgraden naar 5.

Zo blijf je bij:

  • composer audit in je CI-pipeline. Die checkt je lockfile tegen bekende advisories.
  • Dependabot of Renovate op de repository.
  • "Watch → Security alerts" op de Filament GitHub-repo.
  • php artisan filament:upgrade in je composer.json post-update scripts laten staan, zoals de docs voorschrijven.

Filament security checklist voor productie

#CheckHoePrioriteit
1canAccessPanel() met echte logica per panelFilamentUser-contract, default falseMust
2Geen return true in canAccessPanel()Code reviewMust
3strictAuthorization() aanPanel providerMust
4Policy voor elk resource-modelmake:policy, test met PestMust
5Custom actions, pages en widgets geautoriseerdauthorize(), canAccess()Must
6MFA verplichtmultiFactorAuthentication([...], isRequired: true)Must
7Filament op laatste patch-versiecomposer audit, DependabotMust
8acceptedFileTypes() op elke FileUploadForm-schemaMust
9preventFilePathTampering() en geen preserveFilenames()Form-schemaMust
10Gevoelige attributen in $hiddenModelShould
11Geen globale Model::unguard()AppServiceProviderShould
12Queries gescoped buiten tenancygetEloquentQuery()Should
13TrustProxies correct achter proxy/CDNLaravel configShould
14Registratie uit op interne panelsPanel providerShould
15Audit logging op gevoelige modellenActivitylogShould
16Exports op private diskExport-configShould
17Panel op eigen (sub)domein of IP-allowlistdomain(), firewallCould
18CSP-header via panel-middleware->middleware()Could
19Ander panel-pad dan /adminpath()Could

Policies test je het best automatisch. Een test die per rol checkt wie welke resource mag openen, vangt regressies na elke update. Zie Filament testen met Pest.

Samengevat

  • canAccessPanel() is verplicht en moet per panel echte logica hebben.
  • Zonder strictAuthorization() staat een resource zonder policy open.
  • Inline-editable kolommen en custom actions checken geen policies vanzelf.
  • MFA is ingebouwd sinds v4. Zet hem verplicht.
  • FileUpload accepteert standaard alles. Beperk types, grootte en paden.
  • Rate limiting op login is er al (5 per minuut per IP), mits je proxy-config klopt.
  • Audit logging moet je zelf toevoegen.
  • Er zijn 11 advisories in 2026. Patch naar minimaal 5.8.3 of 4.13.3.

Bouw je een backoffice of portaal op Laravel en wil je dat iemand meekijkt? Dit is precies het werk dat we voor onze eigen Filament-omgeving doen.

Veelgestelde vragen

Is Filament veilig voor productie?

Ja, mits je het goed configureert en bijwerkt. Filament controleert policies, heeft rate limiting op de login en ingebouwde MFA. De meeste risico's zitten in configuratie: een open canAccessPanel(), ontbrekende policies of onbeperkte file uploads. Filament patcht gemelde kwetsbaarheden snel, dus updaten is het tweede halve werk.

Wat gebeurt er als ik canAccessPanel niet implementeer?

Lokaal mag elke gebruiker inloggen. In elke andere omgeving kan niemand inloggen, ook jij niet. Dat is veilig, maar leidt vaak tot een snelle return true. Die fix opent het panel voor elk account in je users-tabel.

Heeft Filament ingebouwde twee-factor-authenticatie?

Ja, sinds Filament v4. Je kiest uit een authenticator-app (TOTP) met optionele recovery codes, of een code per e-mail. Met isRequired: true maak je MFA verplicht voor alle gebruikers. Let op: het geldt alleen voor de Filament-login, niet voor je API.

Zijn er bekende CVE's of kwetsbaarheden in Filament?

Ja. Per 6 oktober 2026 staan er veertien advisories op GitHub, waarvan elf uit 2026. Ze gaan vooral over MFA-omzeiling en XSS. Voor v4 en v5 is alles opgelost vanaf 4.13.3 en 5.8.3; draai composer audit om je eigen project te checken.

Hoe log ik wie wat wijzigt in Filament?

Filament heeft geen ingebouwde audit log. De gangbare oplossing is spatie/laravel-activitylog op je modellen, met een Filament-resource of plugin om de log te bekijken. Log in elk geval wijzigingen op gevoelige data, bulk actions en exports.

Is het veranderen van /admin naar een ander pad zinvol?

Beperkt. Het filtert geautomatiseerde scans uit je logs, maar houdt geen gerichte aanvaller tegen. Een eigen subdomein met IP-allowlist of VPN-toegang levert veel meer op.

Twijfel je of jouw Filament-panel aan deze checklist voldoet? Neem contact op, dan lopen we hem samen door.

Ruthger Idema

Geschreven door Ruthger Idema

15+ jaar ervaring in e-commerce development. Gespecialiseerd in Magento, Shopify en Laravel maatwerk.

Meer over ons team →
Deel dit artikel:

Wil je jouw e-commerce naar het volgende niveau?

Plan een vrijblijvend gesprek met onze experts over Magento, Shopify of Laravel maatwerk.

Plan een Tech Check