Security headers e CSP no Laravel
1. Fundamentos de Security Headers em Aplicações Web
Security headers são cabeçalhos HTTP que instruem o navegador sobre como se comportar ao carregar e processar o conteúdo de uma aplicação web. Em aplicações PHP, especialmente aquelas construídas com Laravel, esses headers são essenciais para mitigar ataques como XSS (Cross-Site Scripting), clickjacking e MIME-type sniffing.
Os principais security headers que toda aplicação Laravel deveria implementar são:
- X-Content-Type-Options: Impede que navegadores interpretem arquivos como um tipo MIME diferente do declarado. Valor recomendado:
nosniff - X-Frame-Options: Protege contra clickjacking ao controlar se a página pode ser exibida em iframes. Valor recomendado:
DENYouSAMEORIGIN - X-XSS-Protection: Ativa o filtro XSS embutido nos navegadores (embora muitos navegadores modernos estejam depreciando este header). Valor recomendado:
1; mode=block
Para inspecionar headers no navegador, utilize as DevTools (aba Network) ou ferramentas como securityheaders.com e Mozilla Observatory.
2. Implementando Security Headers no Laravel
A forma mais eficiente de aplicar security headers globalmente no Laravel é através de um middleware customizado. Vamos criar um middleware que adiciona os principais headers de segurança:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class SecureHeaders
{
private array $headers = [
'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'DENY',
'X-XSS-Protection' => '1; mode=block',
'Referrer-Policy' => 'strict-origin-when-cross-origin',
'Permissions-Policy' => 'geolocation=(), microphone=(), camera=()',
];
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
foreach ($this->headers as $key => $value) {
$response->headers->set($key, $value);
}
return $response;
}
}
Registre o middleware no App\Http\Kernel:
// App\Http\Kernel.php
protected $middleware = [
// ...
\App\Http\Middleware\SecureHeaders::class,
];
Para aplicar apenas em rotas específicas, adicione ao array $routeMiddleware:
protected $routeMiddleware = [
'secure.headers' => \App\Http\Middleware\SecureHeaders::class,
];
E use no arquivo de rotas:
Route::middleware('secure.headers')->group(function () {
// Rotas protegidas
});
3. Content Security Policy (CSP): Conceitos e Diretivas Essenciais
CSP é uma camada adicional de segurança que ajuda a detectar e mitigar ataques XSS e injeção de dados. Funciona através de um header HTTP (Content-Security-Policy) que define fontes confiáveis para diferentes tipos de recursos.
Diretivas principais:
- default-src: Serve como fallback para todas as diretivas não especificadas
- script-src: Controla fontes permitidas para scripts JavaScript
- style-src: Controla fontes permitidas para folhas de estilo
- img-src: Controla fontes permitidas para imagens
- connect-src: Controla fontes para conexões via fetch, XMLHttpRequest, WebSocket
Exemplo de política restritiva:
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'");
Para testar violações sem bloquear recursos, use o modo report-only:
header("Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violation");
4. Configurando CSP no Laravel com Pacotes Especializados
O pacote spatie/laravel-csp simplifica a implementação de CSP no Laravel. Instalação:
composer require spatie/laravel-csp
Publicar configuração:
php artisan vendor:publish --provider="Spatie\Csp\CspServiceProvider"
Crie uma política CSP personalizada:
<?php
namespace App\Csp;
use Spatie\Csp\Directive;
use Spatie\Csp\Keyword;
use Spatie\Csp\Policy;
use Spatie\Csp\Value;
class AppPolicy extends Policy
{
public function configure()
{
$this
->addDirective(Directive::BASE, Keyword::SELF)
->addDirective(Directive::DEFAULT, Keyword::SELF)
->addDirective(Directive::SCRIPT, [
Keyword::SELF,
'https://cdnjs.cloudflare.com',
'https://code.jquery.com',
])
->addDirective(Directive::STYLE, [
Keyword::SELF,
'https://fonts.googleapis.com',
Keyword::UNSAFE_INLINE, // Evite em produção
])
->addDirective(Directive::IMG, [
Keyword::SELF,
'data:',
'https://*.cloudfront.net',
])
->addDirective(Directive::CONNECT, Keyword::SELF)
->addDirective(Directive::FONT, [
'https://fonts.gstatic.com',
])
->addDirective(Directive::OBJECT, Keyword::NONE)
->addDirective(Directive::FRAME, Keyword::SELF);
}
}
Para usar nonces com scripts inline:
// No controller ou view
$nonce = csp_nonce();
// Na view Blade
<script nonce="{{ $nonce }}">
console.log('Script seguro com nonce');
</script>
Configure no arquivo config/csp.php:
return [
'policy' => App\Csp\AppPolicy::class,
'report_only' => env('CSP_REPORT_ONLY', true),
'report_uri' => env('CSP_REPORT_URI', '/csp-report'),
];
5. Lidando com Recursos Externos e Frameworks Front-end
Ao integrar CDNs, Google Fonts ou APIs externas, é necessário ajustar a política CSP. Exemplo para Google Fonts e Analytics:
$this
->addDirective(Directive::FONT, [
'https://fonts.gstatic.com',
])
->addDirective(Directive::STYLE, [
'https://fonts.googleapis.com',
])
->addDirective(Directive::SCRIPT, [
'https://www.googletagmanager.com',
'https://www.google-analytics.com',
])
->addDirective(Directive::IMG, [
'https://www.google-analytics.com',
]);
Para Laravel Mix/Vite, gere hashes automáticos para assets compilados:
// No arquivo webpack.mix.js
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.options({
cspHash: true
});
Para Alpine.js ou Livewire com nonces:
// No layout Blade
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"
nonce="{{ csp_nonce() }}" defer></script>
// Ou com Livewire
@livewireScripts
<script nonce="{{ csp_nonce() }}">
document.addEventListener('livewire:load', function () {
// Código Livewire
});
</script>
6. Monitoramento e Debug de Violações de CSP
Configure o report-uri para coletar relatórios de violação:
$this->addDirective(Directive::REPORT_URI, '/csp-report');
Crie uma rota dedicada no Laravel para processar os relatórios:
// routes/web.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\CspReportController;
Route::post('/csp-report', [CspReportController::class, 'store'])
->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);
Controller para processar relatórios:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class CspReportController extends Controller
{
public function store(Request $request)
{
$report = $request->input('csp-report');
Log::channel('csp')->warning('CSP Violation', [
'document_uri' => $report['document-uri'] ?? null,
'blocked_uri' => $report['blocked-uri'] ?? null,
'violated_directive' => $report['violated-directive'] ?? null,
'effective_directive' => $report['effective-directive'] ?? null,
'original_policy' => $report['original-policy'] ?? null,
'source_file' => $report['source-file'] ?? null,
'line_number' => $report['line-number'] ?? null,
'column_number' => $report['column-number'] ?? null,
'user_agent' => $request->userAgent(),
'ip' => $request->ip(),
]);
return response()->json(['status' => 'ok'], 204);
}
}
Configure o canal de log no config/logging.php:
'channels' => [
'csp' => [
'driver' => 'daily',
'path' => storage_path('logs/csp.log'),
'level' => 'warning',
'days' => 30,
],
],
Ferramentas de terceiros como Report URI e Sentry oferecem integração com Laravel para análise avançada de violações CSP.
7. Boas Práticas e Testes de Segurança
Combine security headers com outras camadas de segurança:
// Middleware SecureHeaders completo
private array $headers = [
'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'DENY',
'X-XSS-Protection' => '1; mode=block',
'Referrer-Policy' => 'strict-origin-when-cross-origin',
'Permissions-Policy' => 'geolocation=(), microphone=(), camera=()',
'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains',
'Cross-Origin-Embedder-Policy' => 'require-corp',
'Cross-Origin-Opener-Policy' => 'same-origin',
'Cross-Origin-Resource-Policy' => 'same-origin',
];
Testes com PHPUnit para validar headers:
<?php
namespace Tests\Feature;
use Tests\TestCase;
class SecurityHeadersTest extends TestCase
{
public function test_security_headers_are_present()
{
$response = $this->get('/');
$response->assertHeader('X-Content-Type-Options', 'nosniff');
$response->assertHeader('X-Frame-Options', 'DENY');
$response->assertHeader('X-XSS-Protection', '1; mode=block');
$response->assertHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->assertHeader('Strict-Transport-Security');
}
public function test_csp_header_is_present()
{
$response = $this->get('/');
$response->assertHeader('Content-Security-Policy');
}
}
Checklist final para validação:
- Teste com
securityheaders.com— obtenha nota A+ - Verifique no Mozilla Observatory — alvo: nota A
- Execute testes automatizados no pipeline CI/CD
- Monitore logs de violação CSP regularmente
- Revise e ajuste políticas periodicamente
Referências
- Documentação Oficial do Laravel sobre Middleware — Guia completo sobre criação e registro de middlewares no Laravel
- Pacote Spatie Laravel CSP — Pacote oficial para implementação de Content Security Policy no Laravel
- Mozilla Observatory — Ferramenta gratuita para testar e avaliar headers de segurança do seu site
- Content Security Policy (CSP) - MDN Web Docs — Documentação completa sobre CSP, diretivas e exemplos práticos
- Security Headers - Scott Helme — Artigo técnico detalhado sobre todos os security headers e suas implementações
- Report URI — Serviço de monitoramento e análise de violações CSP
- OWASP Content Security Policy Cheat Sheet — Guia de referência rápida da OWASP sobre CSP