0

Dependency vulnerability scanning — composer audit

Beginner5 min read·php-16-009
security

Concept

Security headers are HTTP response headers that instruct browsers to enable security features. They're a critical defense-in-depth layer that can prevent or mitigate XSS, clickjacking, MIME sniffing, and information leakage.

Key security headers:

  • Content-Security-Policy (CSP): The most powerful header. Defines allowed sources for scripts, styles, images, fonts, etc. default-src 'self' allows only same-origin resources. script-src 'self' https://cdn.example.com — only these script sources. nonce-{RANDOM} — allows specific inline scripts. Prevents XSS by blocking inline scripts and external script loading from attacker-controlled domains.

  • Strict-Transport-Security (HSTS): max-age=31536000; includeSubDomains; preload — tells browsers to always use HTTPS. Prevents SSL stripping attacks. preload submits the domain to browser preload lists.

  • X-Content-Type-Options: nosniff: Prevents browsers from MIME-sniffing response content type — the browser must use the declared Content-Type.

  • X-Frame-Options: DENY|SAMEORIGIN: Prevents your page from being loaded in iframes — blocks clickjacking. Superseded by CSP's frame-ancestors directive but still widely needed.

  • Referrer-Policy: Controls what's in the Referer header. strict-origin-when-cross-origin is a good default — sends origin only for cross-site requests.

  • Permissions-Policy: Formerly Feature-Policy. Controls which browser features the page can use (camera, microphone, geolocation).

Code Example

php
<?php
// Setting security headers in plain PHP
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'");
header("Strict-Transport-Security: max-age=31536000; includeSubDomains; preload");
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: SAMEORIGIN");
header("Referrer-Policy: strict-origin-when-cross-origin");
header("Permissions-Policy: camera=(), microphone=(), geolocation=()");

// Remove information-leaking headers
header_remove("X-Powered-By"); // removes "PHP/8.4.0"
// Also set: expose_php = Off in php.ini

// Laravel — middleware for security headers
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class SecurityHeaders
{
    public function handle(Request $request, Closure $next): mixed
    {
        $response = $next($request);

        $nonce = base64_encode(random_bytes(16)); // CSP nonce for inline scripts
        $response->headers->set('Content-Security-Policy',
            "default-src 'self'; script-src 'self' 'nonce-$nonce'; style-src 'self' 'unsafe-inline'"
        );
        $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('X-Frame-Options', 'SAMEORIGIN');
        $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
        $response->headers->remove('X-Powered-By');

        return $response;
    }
}

// Nginx config — set security headers at web server level (preferred)
// add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
// add_header X-Content-Type-Options "nosniff" always;
// add_header X-Frame-Options "SAMEORIGIN" always;
// add_header Referrer-Policy "strict-origin-when-cross-origin" always;

// Test your headers: securityheaders.com