laravel/laravel
Security
The skeleton ships several security-minded defaults. Most of them are inherited from laravel/framework, but a few decisions live here in this repo's config files. This page catalogues what's already protecting you and what you have to do yourself.
Reporting vulnerabilities
The repo's README.md Security Vulnerabilities section is unambiguous:
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via taylor@laravel.com. All security vulnerabilities will be promptly addressed.
Don't open public issues or PRs for vulnerabilities — email first.
Defaults baked into the skeleton
Encryption key separation
config/app.php splits the encryption key from the rest of the config:
'cipher' => 'AES-256-CBC',
'key' => env('APP_KEY'),
'previous_keys' => [
...array_filter(
explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
),
],previous_keys (an array_filter(explode(','...))) lets you rotate APP_KEY while still decrypting payloads encrypted with previous keys. Without this, key rotation breaks existing sessions, queued jobs, and signed URLs.
Password hashing cast
app/Models/User.php declares:
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}The hashed cast (introduced in Laravel 10) ensures any value assigned to $user->password is automatically passed through Hash::make(). You can't accidentally store a plaintext password — even User::factory()->create(['password' => 'secret']) produces a bcrypt hash.
BCRYPT_ROUNDS=12 in .env.example is the default cost factor. The CI test config (phpunit.xml) lowers it to 4 so tests run fast.
Session serialization defaults to JSON, not PHP
config/session.php ends with:
'serialization' => 'json',Setting this to 'php' allows storing PHP objects in the session — which sounds convenient but opens a serialization-gadget attack surface if APP_KEY ever leaks. The default is JSON, and the file's comment block explicitly calls out the gadget-chain risk.
Cache deserialization defaults to no classes
config/cache.php:
'serializable_classes' => false,Same story — a 2026-02-18 hardening (commit 8a924774) that prevents the database cache store from rebuilding arbitrary objects out of cached payloads. Setting this to a list of class names is opt-in.
CSRF protection (framework default)
The web middleware group automatically applies VerifyCsrfToken. Every Blade form template should include @csrf, which emits a hidden _token input.
The skeleton ships no App\Http\Middleware\VerifyCsrfToken override — the framework's default applies, which validates POST/PUT/PATCH/DELETE requests against the session token.
To disable CSRF for specific routes (e.g. webhook endpoints), use the validateCsrfTokens configuration inside bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: [
'webhooks/*',
]);
})Same-site cookie defaults to lax
config/session.php:
'same_site' => env('SESSION_SAME_SITE', 'lax'),lax mitigates CSRF on top-level navigations while still allowing the session cookie to flow on same-site form submits and standard navigations. strict is the most paranoid; none is required for embedded cross-site contexts (and must be paired with secure).
Partitioned cookies (CHIPS)
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),Off by default. Set SESSION_PARTITIONED_COOKIE=true if your app embeds inside another site and needs Cookies-Having-Independent-Partitioned-State semantics.
HTTPS-only cookies
'secure' => env('SESSION_SECURE_COOKIE'),The default is null — i.e. the framework uses the request scheme to decide. In production you should explicitly set SESSION_SECURE_COOKIE=true so browsers refuse to send the session cookie over HTTP.
HTTP_ONLY for the session cookie
'http_only' => env('SESSION_HTTP_ONLY', true),On by default. JavaScript can't read the session cookie. Don't disable this.
Supply-chain hardening (2026)
Three commits in March-April 2026 tightened the JS toolchain after a supply-chain incident:
- 2026-03-31
02ec8155 [security] pin axios version(#6776) — pinnedaxiosto a specific patch version after a malicious release was published. - 2026-04-01
371cc0b8 Remove axios and enable ignore-scripts(#6778) — removedaxiosfrom the skeleton entirely.package.jsonnow ships zero runtime npm dependencies. - 2026-04-15
9d195133 enable npm audit by default(#6788) —.npmrcaddsaudit=true, sonpm installwarns loudly about vulnerable transitive deps.
.npmrc ends up at:
ignore-scripts=true
audit=trueignore-scripts=true prevents lifecycle scripts (postinstall, preinstall, etc.) from any installed package from executing on your machine. This is the single biggest npm-supply-chain hardening you can apply.
Things you must do yourself
Set a real APP_KEY
.env.example ships with APP_KEY= (empty). The composer setup script and post-create-project-cmd hook run php artisan key:generate to fill it in. Verify before deploying — the framework will throw at runtime if the key is missing.
Use HTTPS
The skeleton doesn't force HTTPS. To redirect HTTP → HTTPS in production, set APP_URL=https://... in .env and ensure your web server / load balancer terminates TLS and forwards X-Forwarded-Proto. The framework's TrustProxies middleware (auto-applied by the web group in Laravel 11+) reads that header and treats the request as secure.
For belt-and-braces, force HTTPS in the app via URL::forceScheme('https') inside App\Providers\AppServiceProvider::boot() when app()->environment('production').
Validate input
Every controller method that handles user input should validate. The framework provides $request->validate(...) and form requests (php artisan make:request StoreUserRequest). The skeleton doesn't ship any controllers, so there's no example here — but it's the most consequential security responsibility you'll inherit.
Configure dontFlash for sensitive fields
Inside bootstrap/app.php's withExceptions():
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->dontFlash([
'current_password',
'password',
'password_confirmation',
'token',
'secret',
]);
})Without this, validation-failure redirects can flash these fields back into the session and expose them in URLs / debug output. The framework handles the standard password/password_confirmation pair by default but you should add anything else sensitive your app uses.
Lock down debug surfaces in production
| Surface | Production action |
|---|---|
APP_DEBUG |
Set to false |
php artisan tinker |
Don't expose; it's a dev tool |
| Telescope / Pulse | If installed, gate behind authentication and IP allow-listing in production |
APP_URL |
Set correctly so generated URLs and signed routes are stable |
phpunit.xml proactively sets PULSE_ENABLED=false, TELESCOPE_ENABLED=false, and NIGHTWATCH_ENABLED=false so installing those packages doesn't break tests — but they're still on by default in production once installed. Read the relevant package docs before turning them on.
Rate-limit your routes
Laravel's ThrottleRequests middleware (alias throttle) is the standard control. The default api middleware group applies throttle:api (60/minute per user). The skeleton has no routes/api.php so this is moot until you add one — but throttle is also valid on web routes:
Route::post('/login', [LoginController::class, 'store'])
->middleware('throttle:5,1'); // 5 attempts per minuteStore secrets out of source control
.gitignore already excludes .env, .env.backup, .env.production, and auth.json. Never override that. Use your deployment platform's secret store.
Trust boundaries
The skeleton's only trust boundary out of the box is authenticated user vs. unauthenticated request, mediated by:
App\Models\User(the principal)config/auth.phpwebguard (the session-based authenticator)password_reset_tokenstable (the password reset issuance/validation surface)sessionstable (where authenticated session state persists whenSESSION_DRIVER=database)
There is no role/permission system, no policy classes, no audit log. As soon as you add multi-tenant logic, role-based authorization, or sensitive data, you're stepping outside what this skeleton ships and need to build the controls yourself.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.