| title | description | updated |
|---|---|---|
Security |
Security considerations and audit notes for core-template |
2026-01-29 |
This document covers security considerations for applications built with core-template. It includes both framework-provided protections and recommendations for hardening your application.
Laravel's CSRF protection is enabled by default for all web routes. The template includes axios configuration that automatically attaches the CSRF token to AJAX requests:
// resources/js/bootstrap.js
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';For forms, use the @csrf Blade directive:
<form method="POST" action="/posts">
@csrf
<!-- form fields -->
</form>Blade's {{ }} syntax automatically escapes output. Use {!! !!} only for trusted HTML content.
Eloquent ORM and Query Builder use parameterised queries by default. Avoid raw queries where possible:
// Safe - parameterised
User::where('email', $email)->first();
// Dangerous - raw SQL
DB::select("SELECT * FROM users WHERE email = '$email'"); // Don't do thisModels should define $fillable or $guarded properties to prevent mass assignment vulnerabilities:
class Post extends Model
{
protected $fillable = ['title', 'content', 'slug'];
}The template configures bcrypt with 12 rounds by default (BCRYPT_ROUNDS=12 in .env.example). This is appropriate for production use.
Add security headers via middleware. Create app/Http/Middleware/SecurityHeaders.php:
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class SecurityHeaders
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
// Content Security Policy (adjust as needed)
$response->headers->set(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"
);
return $response;
}
}Register in bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
\Core\Front\Boot::middleware($middleware);
$middleware->web(append: [
\App\Http\Middleware\SecurityHeaders::class,
]);
})For production environments, update these settings in .env:
SESSION_SECURE_COOKIE=true # Only send cookies over HTTPS
SESSION_ENCRYPT=true # Encrypt session data
SESSION_HTTP_ONLY=true # Prevent JavaScript access to session cookie
SESSION_SAME_SITE=strict # Strict same-site policyForce HTTPS in production by adding to AppServiceProvider:
public function boot(): void
{
if ($this->app->environment('production')) {
URL::forceScheme('https');
}
}The default welcome route has no rate limiting. For production, add throttle middleware:
Route::middleware('throttle:60,1')->group(function () {
Route::get('/', function () {
return view('welcome');
});
});Configure custom rate limiters in AppServiceProvider:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)->by($request->ip());
});
}The APP_KEY is critical for encryption. Never:
- Commit it to version control
- Share it between environments
- Use predictable values
Rotate the key only when necessary, understanding that:
- Existing encrypted data becomes unreadable
- Active sessions are invalidated
- Signed URLs become invalid
Ensure APP_DEBUG=false in production. Debug mode exposes:
- Stack traces with file paths
- Environment variables
- Database queries
Never commit database credentials. Use environment variables:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=${DB_USERNAME}
DB_PASSWORD=${DB_PASSWORD}-
APP_DEBUG=false -
APP_ENV=production -
APP_KEYis set and unique - Session cookies are secure (
SESSION_SECURE_COOKIE=true) - HTTPS is enforced
- Security headers are configured
- Rate limiting is in place for sensitive endpoints
-
.envis not accessible via web (check withcurl https://yoursite.com/.env) - Storage directory is not web-accessible
- Error pages don't leak sensitive information
- Database credentials are environment-specific
- Third-party API keys are not exposed in client-side code
- Password reset tokens expire appropriately
- Login attempts are rate limited
- Account lockout is configured after failed attempts
- Two-factor authentication is available for sensitive accounts
- Session regeneration on login
- Session invalidation on logout
- API keys are properly scoped
- Rate limiting per API key
- Webhook signatures are verified
- CORS is configured appropriately
- Sensitive endpoints require authentication
Keep dependencies updated to receive security patches:
# Check for outdated packages
composer outdated
# Update dependencies
composer update
# Check for known vulnerabilities
composer auditThe template includes Dependabot configuration (.github/dependabot.yml) to automate security updates.
If you discover a security vulnerability in the Core PHP Framework:
- Do not create a public GitHub issue
- Email security concerns to the maintainers directly
- Include detailed steps to reproduce
- Allow reasonable time for a fix before public disclosure