alexsoft / cross-origin-protection
Cross Origin Protection middleware that uses Sec-Fetch-Site and Origin headers
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 1
pkg:composer/alexsoft/cross-origin-protection
Requires
- php: ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0
- php-http/discovery: ^1.17
- psr/http-factory: ^1.1
- psr/http-factory-implementation: *
- psr/http-message: ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.48.2
- guzzlehttp/psr7: ^2.8
- phpstan/phpstan: ^2.1.32
- phpstan/phpstan-strict-rules: ^2.0.7
- phpunit/phpunit: ^10.5.58
Provides
README
This library is a port of CrossOriginProtection component from Go.
You can read an introduction to the mentioned Go component here: alexedwards.net/blog/preventing-csrf-in-go.
Overview
This is a small PSR-15/PSR-7-compatible middleware library for PHP which validates fetch metadata (Sec-Fetch-Site) and Origin headers. It follows the approach popularised by the Go CrossOriginProtection component.
This middleware is focused on rejecting unsafe cross-origin requests (a practical defense against certain CSRF/forged-request scenarios).
Why use this
Protect state-changing endpoints from cross-site requests when browser fetch metadata is available.
Lightweight, framework-agnostic middleware for PSR-compatible stacks.
Installation
Install with Composer:
composer require alexsoft/cross-origin-protection:^1.0
Usage
use Alexsoft\CrossOriginProtection\CrossOriginProtection; use GuzzleHttp\Psr7\Request; $result = (new CrossOriginProtection())->check(Request::fromGlobals()); // accepts instance of ServerRequestInterface if ($result === null) { // request is considered safe } else { // request is NOT considered safe }
If $result is not null, it will be an instance of CrossOriginRequestError which has public property $message which can be used for logging. Usually it is discouraged to show this message to the user, it is preferable to show some more generic server error message.
PSR-15 middleware usage
Get instance of Psr\Http\Server\MiddlewareInterface by calling getMiddleware() method. Then you can plug it into your stack.
use Alexsoft\CrossOriginProtection\CrossOriginProtection; $psr15Middleware = (new CrossOriginProtection())->getMiddleware();
Configuration
CrossOriginProtection has 2 method that provide extension:
addInsecureBypassPattern(string $regex): void– can be used to add case-insensitive regex for the URLs that need to be bypassed. All added regexes are combined with|, wrapped with leading and trailing slashes and havei(ignore case) flag added.addTrustedOrigin(string|UriInterface $uri): void– can be used to add trusted origins for which requests should be bypassed.
use Alexsoft\CrossOriginProtection\CrossOriginProtection; use GuzzleHttp\Psr7\Request; $crossOriginProtection = new CrossOriginProtection(); $crossOriginProtection->addInsecureBypassPattern('\/internal\/'); // will bypass URLS with '/internal/' section in it $crossOriginProtection->addTrustedOrigin('https://example.com'); // can be used directly $crossOriginProtection->check(Request::fromGlobals()); // accepts instance of ServerRequestInterface // or as PSR-15 middleware $middleware = $crossOriginProtection->getMiddleware();
Important notes
Because the middleware relies on browser-provided Sec-Fetch-Site and Origin headers, consider following:
Safe methods
You typically only need to apply strict checks to state-changing methods (e.g. POST, PUT, DELETE, PATCH). GET, HEAD are usually safe but evaluate per-API.
Fetch metadata support
Sec-Fetch-Site is not present in all browsers/clients. Falling back on comparing Origin and Host when Sec-Fetch-Site is missing can be risky: Host lacks scheme and that fallback has edge cases (e.g. http://example.com vs https://example.com). This should be mitigated with HTTP Strict Transport Security (HSTS). See the article for nuances.
Not a complete protection
This middleware helps reject unsafe cross-origin requests but is not a complete CSRF protection on its own. Use it alongside other controls (CSRF tokens for browser forms, SameSite cookies, strong authentication).
When Sec-Fetch-Site is absent, implementation falls back to Origin vs Host checks; this fallback may introduce false-positives/negatives in mixed-scheme deployments (HTTP ↔ HTTPS). Test carefully.
Do not depend on header values from non-browser clients — spoilable by attackers. The middleware is primarily to harden browser-based attack surfaces.