Skip to content
>_ITDITDWeb Security Platform

By framework

Laravel security — .env exposure, APP_DEBUG, and authorization

Laravel's big three security pitfalls — .env exposed from the public directory, APP_DEBUG on in production, and missing authorization (IDOR / Mass Assignment) — and how to close each. Defensive, no attack steps.

Published 2026-07-02 Updated 2026-07-02 4 min read

For: anyone running a Laravel app. No attack steps here — just the big three operational incidents and how to close them. For the full picture, see the security-by-framework hub.

The big three pitfalls (where defaults won't save you)

Laravel's escaping and authentication are excellent, but these three are yours to close.

① Exposed secrets

.env, backups, or keys reachable by URL from public/. A placement slip = instant leak.

② Debug on in production

APP_DEBUG=true exposes env vars and connection info on the error page. Extracted via deliberate errors.

③ Missing authorization

Authenticated but no owner scope (IDOR) / Mass Assignment overwriting is_admin, etc.

The three incidents most common when running Laravel — all outside the defaults.

How to close them (3 steps)

1

Move secrets outside the public directory (perms 600)

Laravel already keeps .env outside the document root, but don't accidentally drop backups, exports, or key files into public/. Keep secrets outside the app root at perms 600 (owner-only). (→ keep secrets out of public directories · a full .env exposure case)
2

Disable debug + cache config in production

Ensure APP_DEBUG=false and APP_ENV=production. Pin it with a config cache and stop detailed errors from showing externally. Bake it into the deploy steps and verify every time.
3

Build authorization (Policy/Gate + $fillable)

Don't stop at "logged in means allowed." Implement ownership checks with Policy/Gate and scope every read/update/delete by user_id, etc. For Mass Assignment, declare $fillable to prevent unintended fields from being overwritten. (→ what IDOR is)

Common (dangerous)

  • backups or keys placed in public/, reachable by URL
  • production left at APP_DEBUG=true
  • no authorization — "logged in = can view"
  • Model::create($request->all()) accepting every field

Correct

  • secrets outside the document root at perms 600
  • production debug off + config cache
  • Policy/Gate owner scoping
  • declare $fillable to block Mass Assignment

This site's view: solid up to the defaults; authorization is on you

Laravel has many good defaults, but authorization — who may do what — is app-specific, so no framework can guard it automatically. The incidents we keep seeing are less SQLi or XSS than the "authenticated, but no ownership check" type. So the center of gravity isn't flashy config but the unglamorous work of scoping every read/update by owner. Add keeping secrets off the public surface and turning off production debug, and you prevent most real-world Laravel incidents with these three.

FAQ

QIs Laravel a secure framework?
A

Laravel ships many safe defaults (escaping, authentication) and is fairly solid out of the box. But 'safe defaults' and 'safe operations' are different things, and real incidents come not from the core but from configuration and operations. Exposed .env/secrets, debug left on in production, and thin authorization are areas the framework won't cover for you. You can't rely on defaults alone — you have to close these three yourself.

QWhat's dangerous about shipping with APP_DEBUG=true?
A

With debug on, an error page can show not just a stack trace but internal secrets like environment variables and connection info. An attacker can trigger errors on purpose to extract them. In production, always set APP_DEBUG=false and cache the config (config cache) to make it stick, and stop detailed errors from being shown externally.

QWhy is 'if you're logged in, you can see it' dangerous?
A

That's authentication without authorization. Even when logged in, if the data isn't scoped to the user (by user_id, etc.), swapping the ID in the URL can reveal someone else's data (IDOR). In Laravel, implement ownership checks with Policy/Gate, and declare $fillable for Mass Assignment to prevent unintended fields from being overwritten.