Middleware essencial para aplicar headers de segurança e proteger aplicações contra ataques comuns como XSS, CSRF, clickjacking e outros vetores de ataque.
- Visão Geral
- Instalação e Uso Básico
- Configurações Disponíveis
- Headers de Segurança
- Proteções Implementadas
- Configurações por Ambiente
- Integração com Outros Middlewares
- Testing
- Exemplos Práticos
- Boas Práticas
O SecurityMiddleware é um middleware fundamental que aplica múltiplas camadas de proteção de segurança web, incluindo headers HTTP de segurança padrão da indústria e proteções contra vulnerabilidades OWASP Top 10.
- XSS Protection - Prevenção de Cross-Site Scripting
- Content Type Options - Prevenção de MIME sniffing
- Frame Options - Proteção contra clickjacking
- Referrer Policy - Controle de informações de referência
- Custom Security Headers - Headers personalizados
<?php
use PivotPHP\Core\Core\Application;
use PivotPHP\Core\Middleware\Security\SecurityMiddleware;
$app = new Application();
// Aplicação básica (recomendado para a maioria dos casos)
$app->use(new SecurityMiddleware());
// Suas rotas aqui
$app->get('/', function($req, $res) {
return $res->json(['status' => 'secure']);
});
$app->run();// Método de criação padrão
$app->use(SecurityMiddleware::create());
// Configuração para desenvolvimento (mais permissiva)
$app->use(SecurityMiddleware::development());
// Configuração para produção (mais restritiva)
$app->use(SecurityMiddleware::production());
// Configuração estrita (máxima segurança)
$app->use(SecurityMiddleware::strict());$app->use(new SecurityMiddleware([
// Headers básicos
'xContentTypeOptions' => true, // X-Content-Type-Options: nosniff
'referrerPolicy' => 'strict-origin-when-cross-origin',
// Headers customizados
'customHeaders' => [
'X-Custom-Security' => 'enabled',
'X-API-Version' => '2.1',
'X-Powered-By' => 'PivotPHP'
],
// Configurações avançadas
'frameOptions' => 'DENY', // X-Frame-Options
'contentSecurityPolicy' => null, // CSP (se configurado)
'strictTransportSecurity' => null, // HSTS (se configurado)
// Configurações de debug
'logSecurityEvents' => false,
'debugMode' => false
]));$config = [
// X-Content-Type-Options
'xContentTypeOptions' => true, // Impede MIME sniffing
// Referrer Policy
'referrerPolicy' => 'strict-origin-when-cross-origin', // Controla referrer
// X-XSS-Protection (sempre ativo)
// Automaticamente definido como "1; mode=block"
// Headers customizados
'customHeaders' => [
'X-Content-Security' => 'protected',
'X-Request-ID' => function($request) {
return $request->getAttribute('request_id') ?: uniqid();
}
]
];$advancedConfig = [
// Content Security Policy
'contentSecurityPolicy' => [
'default-src' => "'self'",
'script-src' => "'self' 'unsafe-inline'",
'style-src' => "'self' 'unsafe-inline'",
'img-src' => "'self' data: https:",
'font-src' => "'self'",
'connect-src' => "'self'",
'frame-ancestors' => "'none'"
],
// HTTP Strict Transport Security
'strictTransportSecurity' => [
'max-age' => 31536000, // 1 ano
'includeSubDomains' => true,
'preload' => false
],
// X-Frame-Options
'frameOptions' => 'DENY', // ou 'SAMEORIGIN' ou 'ALLOW-FROM uri'
// Logging de eventos de segurança
'logSecurityEvents' => true,
'logLevel' => 'info'
];X-XSS-Protection: 1; mode=block- Propósito: Ativa proteção XSS do navegador
- Valor: Sempre
1; mode=block - Compatibilidade: Navegadores legacy
X-Content-Type-Options: nosniff- Propósito: Impede MIME type sniffing
- Configurável:
xContentTypeOptions => true/false - Recomendação: Sempre ativado
Referrer-Policy: strict-origin-when-cross-origin- Propósito: Controla informações de referência
- Configurável:
referrerPolicy => 'valor' - Opções:
no-referrer,strict-origin,same-origin, etc.
$app->use(new SecurityMiddleware([
'contentSecurityPolicy' => [
'default-src' => "'self'",
'script-src' => "'self' https://cdnjs.cloudflare.com",
'style-src' => "'self' 'unsafe-inline'",
'img-src' => "'self' data: https:",
'font-src' => "'self' https://fonts.gstatic.com",
'connect-src' => "'self' https://api.exemplo.com",
'frame-ancestors' => "'none'",
'base-uri' => "'self'",
'object-src' => "'none'"
]
]));Resultado:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.exemplo.com; frame-ancestors 'none'; base-uri 'self'; object-src 'none'$app->use(new SecurityMiddleware([
'strictTransportSecurity' => [
'max-age' => 31536000, // 1 ano em segundos
'includeSubDomains' => true,
'preload' => false // Para inclusion no HSTS preload list
]
]));Resultado:
Strict-Transport-Security: max-age=31536000; includeSubDomains// O middleware automaticamente adiciona:
// X-XSS-Protection: 1; mode=block
// Para CSP mais rigoroso contra XSS:
$app->use(new SecurityMiddleware([
'contentSecurityPolicy' => [
'default-src' => "'self'",
'script-src' => "'self'", // Sem 'unsafe-inline'
'object-src' => "'none'",
'base-uri' => "'self'"
]
]));// Automaticamente adicionado:
// X-Content-Type-Options: nosniff
// Impede que navegadores "adivinhem" o tipo de conteúdo
// e executem scripts maliciosos$app->use(new SecurityMiddleware([
'frameOptions' => 'DENY' // Impede embedding em frames/iframes
]));
// Ou permitir apenas same-origin
$app->use(new SecurityMiddleware([
'frameOptions' => 'SAMEORIGIN'
]));
// Resultado:
// X-Frame-Options: DENY$app->use(new SecurityMiddleware([
'referrerPolicy' => 'strict-origin-when-cross-origin'
]));
// Opções disponíveis:
// - 'no-referrer' - Nunca envia referrer
// - 'no-referrer-when-downgrade' - Padrão do navegador
// - 'origin' - Apenas origem
// - 'origin-when-cross-origin' - Origem para cross-origin
// - 'same-origin' - Apenas same-origin
// - 'strict-origin' - Origem, mas não para HTTPS→HTTP
// - 'strict-origin-when-cross-origin' - Recomendado
// - 'unsafe-url' - URL completa sempreclass SecurityMiddleware
{
public static function development(): self
{
return new self([
'xContentTypeOptions' => true,
'referrerPolicy' => 'origin-when-cross-origin',
'frameOptions' => 'SAMEORIGIN',
'logSecurityEvents' => true,
'debugMode' => true,
'customHeaders' => [
'X-Environment' => 'development',
'X-Debug-Mode' => 'enabled'
]
]);
}
}
// Uso
if ($_ENV['APP_ENV'] === 'development') {
$app->use(SecurityMiddleware::development());
}class SecurityMiddleware
{
public static function production(): self
{
return new self([
'xContentTypeOptions' => true,
'referrerPolicy' => 'strict-origin-when-cross-origin',
'frameOptions' => 'DENY',
'strictTransportSecurity' => [
'max-age' => 31536000,
'includeSubDomains' => true
],
'contentSecurityPolicy' => [
'default-src' => "'self'",
'script-src' => "'self'",
'style-src' => "'self' 'unsafe-inline'",
'img-src' => "'self' data:",
'font-src' => "'self'",
'connect-src' => "'self'",
'frame-ancestors' => "'none'",
'base-uri' => "'self'",
'object-src' => "'none'"
],
'customHeaders' => [
'X-Environment' => 'production',
'Server' => 'PivotPHP' // Ocultar versão do servidor
]
]);
}
}class SecurityMiddleware
{
public static function strict(): self
{
return new self([
'xContentTypeOptions' => true,
'referrerPolicy' => 'no-referrer',
'frameOptions' => 'DENY',
'strictTransportSecurity' => [
'max-age' => 63072000, // 2 anos
'includeSubDomains' => true,
'preload' => true
],
'contentSecurityPolicy' => [
'default-src' => "'none'",
'script-src' => "'self'",
'style-src' => "'self'",
'img-src' => "'self'",
'font-src' => "'self'",
'connect-src' => "'self'",
'media-src' => "'none'",
'object-src' => "'none'",
'child-src' => "'none'",
'frame-ancestors' => "'none'",
'base-uri' => "'none'",
'form-action' => "'self'"
],
'customHeaders' => [
'X-Security-Level' => 'strict',
'X-Permitted-Cross-Domain-Policies' => 'none'
]
]);
}
}// Ordem correta: Security primeiro, depois CORS
$app->use(new SecurityMiddleware());
$app->use(new CorsMiddleware([
'origins' => ['https://app.exemplo.com'],
'credentials' => true
]));// Headers de segurança antes da autenticação
$app->use(new SecurityMiddleware());
$app->use(new AuthMiddleware(['authMethods' => ['jwt']]));// Error handling primeiro para capturar todos os erros
$app->use(new ErrorMiddleware());
$app->use(new SecurityMiddleware());
// Outros middlewares...$app->use(new ErrorMiddleware()); // 1. Captura de erros
$app->use(new SecurityMiddleware()); // 2. Headers de segurança
$app->use(new CorsMiddleware()); // 3. CORS
$app->use(new RateLimitMiddleware()); // 4. Rate limiting
$app->use(new AuthMiddleware()); // 5. Autenticação
$app->use(new ValidationMiddleware()); // 6. Validação<?php
namespace Tests\Middleware;
use PHPUnit\Framework\TestCase;
use PivotPHP\Core\Middleware\Security\SecurityMiddleware;
class SecurityMiddlewareTest extends TestCase
{
public function testAddsSecurityHeaders(): void
{
$middleware = new SecurityMiddleware();
$request = $this->createMockRequest();
$response = $this->createMockResponse();
$next = function($req, $res) { return $res; };
$result = $middleware->handle($request, $response, $next);
// Verificar headers obrigatórios
$this->assertTrue($response->hasHeader('X-XSS-Protection'));
$this->assertEquals('1; mode=block', $response->getHeader('X-XSS-Protection'));
$this->assertTrue($response->hasHeader('X-Content-Type-Options'));
$this->assertEquals('nosniff', $response->getHeader('X-Content-Type-Options'));
}
public function testCustomHeaders(): void
{
$middleware = new SecurityMiddleware([
'customHeaders' => [
'X-Custom-Security' => 'test-value'
]
]);
$request = $this->createMockRequest();
$response = $this->createMockResponse();
$next = function($req, $res) { return $res; };
$middleware->handle($request, $response, $next);
$this->assertTrue($response->hasHeader('X-Custom-Security'));
$this->assertEquals('test-value', $response->getHeader('X-Custom-Security'));
}
public function testContentSecurityPolicy(): void
{
$middleware = new SecurityMiddleware([
'contentSecurityPolicy' => [
'default-src' => "'self'",
'script-src' => "'self' 'unsafe-inline'"
]
]);
$request = $this->createMockRequest();
$response = $this->createMockResponse();
$next = function($req, $res) { return $res; };
$middleware->handle($request, $response, $next);
$this->assertTrue($response->hasHeader('Content-Security-Policy'));
$csp = $response->getHeader('Content-Security-Policy');
$this->assertStringContainsString("default-src 'self'", $csp);
$this->assertStringContainsString("script-src 'self' 'unsafe-inline'", $csp);
}
}<?php
namespace Tests\Integration;
use Tests\TestCase;
use PivotPHP\Core\Middleware\Security\SecurityMiddleware;
class SecurityMiddlewareIntegrationTest extends TestCase
{
public function testSecurityHeadersInResponse(): void
{
$this->app->use(new SecurityMiddleware());
$this->app->get('/test', function($req, $res) {
return $res->json(['message' => 'test']);
});
$response = $this->get('/test');
$response->assertStatus(200);
$response->assertHeader('X-XSS-Protection', '1; mode=block');
$response->assertHeader('X-Content-Type-Options', 'nosniff');
$response->assertHeaderContains('Referrer-Policy', 'strict-origin');
}
public function testCspViolationReporting(): void
{
$this->app->use(new SecurityMiddleware([
'contentSecurityPolicy' => [
'default-src' => "'self'",
'report-uri' => '/csp-report'
]
]));
$response = $this->get('/');
$csp = $response->headers->get('Content-Security-Policy');
$this->assertStringContainsString('report-uri /csp-report', $csp);
}
}<?php
use PivotPHP\Core\Core\Application;
use PivotPHP\Core\Middleware\Security\SecurityMiddleware;
use PivotPHP\Core\Http\Psr15\Middleware\CorsMiddleware;
$app = new Application();
// Configuração de segurança para API
$app->use(new SecurityMiddleware([
'xContentTypeOptions' => true,
'referrerPolicy' => 'strict-origin-when-cross-origin',
'frameOptions' => 'DENY',
'contentSecurityPolicy' => [
'default-src' => "'none'",
'connect-src' => "'self'" // Apenas requisições AJAX para mesma origem
],
'customHeaders' => [
'X-API-Version' => '2.1',
'X-Content-Security' => 'protected'
]
]));
$app->use(new CorsMiddleware([
'origins' => ['https://app.exemplo.com'],
'methods' => ['GET', 'POST', 'PUT', 'DELETE'],
'headers' => ['Content-Type', 'Authorization'],
'credentials' => true
]));
$app->get('/api/status', function($req, $res) {
return $res->json(['status' => 'secure', 'timestamp' => time()]);
});<?php
$app = new Application();
// Configuração para aplicação web com assets externos
$app->use(new SecurityMiddleware([
'contentSecurityPolicy' => [
'default-src' => "'self'",
'script-src' => [
"'self'",
"'unsafe-inline'", // Para scripts inline (use com cuidado)
'https://cdnjs.cloudflare.com',
'https://code.jquery.com'
],
'style-src' => [
"'self'",
"'unsafe-inline'", // Para estilos inline
'https://fonts.googleapis.com'
],
'font-src' => [
"'self'",
'https://fonts.gstatic.com'
],
'img-src' => [
"'self'",
'data:', // Para imagens base64
'https:' // Qualquer imagem HTTPS
],
'connect-src' => [
"'self'",
'https://api.exemplo.com'
]
],
'strictTransportSecurity' => [
'max-age' => 31536000,
'includeSubDomains' => true
]
]));<?php
class SecurityLoggingMiddleware extends SecurityMiddleware
{
protected function logSecurityEvent(string $event, array $context = []): void
{
$logger = $this->container->get('logger');
$logger->info("Security Event: {$event}", array_merge([
'timestamp' => date('c'),
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
], $context));
}
public function handle($request, $response, callable $next)
{
// Log da aplicação de headers de segurança
$this->logSecurityEvent('security_headers_applied', [
'headers' => $this->getAppliedHeaders()
]);
return parent::handle($request, $response, $next);
}
}
$app->use(new SecurityLoggingMiddleware([
'logSecurityEvents' => true
]));// ✅ Bom - configuração específica por ambiente
$securityConfig = match($_ENV['APP_ENV']) {
'development' => SecurityMiddleware::development(),
'testing' => SecurityMiddleware::development(),
'staging' => SecurityMiddleware::production(),
'production' => SecurityMiddleware::strict(),
default => SecurityMiddleware::create()
};
$app->use($securityConfig);// ✅ Bom - headers que adicionam valor
$app->use(new SecurityMiddleware([
'customHeaders' => [
'X-API-Version' => '2.1',
'X-Rate-Limit-Policy' => 'standard',
'X-Security-Level' => 'high'
]
]));
// ❌ Evitar - headers que expõem informações desnecessárias
$app->use(new SecurityMiddleware([
'customHeaders' => [
'X-Powered-By' => 'PHP/8.1.0', // Exposição de versão
'X-Server-Name' => 'web-01' // Informação interna
]
]));// ✅ Comece restritivo e ajuste conforme necessário
$baseCsp = [
'default-src' => "'self'",
'script-src' => "'self'",
'style-src' => "'self'",
'img-src' => "'self' data:",
'font-src' => "'self'",
'connect-src' => "'self'",
'frame-ancestors' => "'none'",
'base-uri' => "'self'",
'object-src' => "'none'"
];
// Adicione origens conforme necessário
if ($needsCdn) {
$baseCsp['script-src'][] = 'https://cdnjs.cloudflare.com';
$baseCsp['style-src'][] = 'https://fonts.googleapis.com';
}
$app->use(new SecurityMiddleware([
'contentSecurityPolicy' => $baseCsp
]));// ✅ Implementar reporting para CSP
$app->use(new SecurityMiddleware([
'contentSecurityPolicy' => [
'default-src' => "'self'",
'report-uri' => '/security/csp-report',
'report-to' => 'security-reports'
]
]));
// Endpoint para receber relatórios
$app->post('/security/csp-report', function($req, $res) {
$report = $req->json();
// Log violation
error_log("CSP Violation: " . json_encode($report));
// Opcionalmente, enviar para serviço de monitoramento
$this->securityMonitor->reportViolation('csp', $report);
return $res->status(204)->send();
});// Função para verificar headers de segurança
function checkSecurityHeaders(string $url): array
{
$headers = get_headers($url, true);
$checks = [
'X-XSS-Protection' => isset($headers['X-XSS-Protection']),
'X-Content-Type-Options' => isset($headers['X-Content-Type-Options']),
'X-Frame-Options' => isset($headers['X-Frame-Options']),
'Strict-Transport-Security' => isset($headers['Strict-Transport-Security']),
'Content-Security-Policy' => isset($headers['Content-Security-Policy'])
];
return $checks;
}
// Usar em testes automatizados
public function testSecurityHeadersPresent(): void
{
$response = $this->get('/');
$headers = $response->headers->all();
$this->assertArrayHasKey('x-xss-protection', $headers);
$this->assertArrayHasKey('x-content-type-options', $headers);
$this->assertArrayHasKey('x-frame-options', $headers);
}- CorsMiddleware - Configuração CORS
- AuthMiddleware - Sistema de autenticação
- Middleware Overview - Visão geral de middlewares
- Security Best Practices - Práticas de segurança
- OWASP Security Headers: Implementação baseada nas recomendações OWASP
- CSP Generator: Ferramentas online para gerar Content Security Policy
- Security Testing: Ferramentas como securityheaders.com para validação
- Monitoring: Integração com serviços de monitoramento de segurança
Para dúvidas ou contribuições, consulte o guia de contribuição.