فهرست منبع

Fix R01-N25: X-Permitted-Cross-Domain-Policies: none

Adobe Flash and Acrobat are dead, but the cross-domain-policies header
is a single-line, defence-in-depth lockout against any tooling that
still inspects the file. Added to the FatalErrorHandler::securityHeaders
single source of truth so both the happy path and the 500 fallback get
it for free, on HTTP and HTTPS alike. FatalErrorHandlerTest gains the
drift fence (header in the production-500 emit + present in the helper
on both HTTPS and plain HTTP).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chiappa 2 روز پیش
والد
کامیت
f6ce13f5c7
2فایلهای تغییر یافته به همراه16 افزوده شده و 4 حذف شده
  1. 10 4
      src/Http/FatalErrorHandler.php
  2. 6 0
      tests/Http/FatalErrorHandlerTest.php

+ 10 - 4
src/Http/FatalErrorHandler.php

@@ -169,10 +169,16 @@ final class FatalErrorHandler
     public static function securityHeaders(bool $isHttps): array
     {
         $h = [
-            'X-Content-Type-Options'  => 'nosniff',
-            'X-Frame-Options'         => 'DENY',
-            'Referrer-Policy'         => 'strict-origin-when-cross-origin',
-            'Content-Security-Policy' => self::CSP,
+            'X-Content-Type-Options'              => 'nosniff',
+            'X-Frame-Options'                     => 'DENY',
+            'Referrer-Policy'                     => 'strict-origin-when-cross-origin',
+            // R01-N25: defence-in-depth for legacy Adobe Flash / Acrobat
+            // cross-origin policy lookups. Cheap header; tells those clients
+            // (and any tooling that still inspects the file) that no
+            // crossdomain.xml / clientaccesspolicy.xml is honoured under this
+            // origin.
+            'X-Permitted-Cross-Domain-Policies'   => 'none',
+            'Content-Security-Policy'             => self::CSP,
         ];
         if ($isHttps) {
             $h['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains';

+ 6 - 0
tests/Http/FatalErrorHandlerTest.php

@@ -50,6 +50,8 @@ final class FatalErrorHandlerTest extends TestCase
         self::assertContains('X-Content-Type-Options: nosniff', $this->headers);
         self::assertContains('X-Frame-Options: DENY', $this->headers);
         self::assertContains('Referrer-Policy: strict-origin-when-cross-origin', $this->headers);
+        // R01-N25: Flash / Acrobat cross-domain policy lockout.
+        self::assertContains('X-Permitted-Cross-Domain-Policies: none', $this->headers);
         self::assertContains(
             'Strict-Transport-Security: max-age=31536000; includeSubDomains',
             $this->headers,
@@ -196,11 +198,15 @@ final class FatalErrorHandlerTest extends TestCase
         self::assertSame('nosniff',                                $h['X-Content-Type-Options']);
         self::assertSame('DENY',                                   $h['X-Frame-Options']);
         self::assertSame('strict-origin-when-cross-origin',        $h['Referrer-Policy']);
+        self::assertSame('none',                                   $h['X-Permitted-Cross-Domain-Policies']);
         self::assertSame('max-age=31536000; includeSubDomains',    $h['Strict-Transport-Security']);
         self::assertArrayHasKey('Content-Security-Policy', $h);
 
         $hPlain = FatalErrorHandler::securityHeaders(false);
         self::assertArrayNotHasKey('Strict-Transport-Security', $hPlain);
+        // R01-N25: the new header is unconditional — must ride along on
+        // both HTTP and HTTPS responses, unlike HSTS.
+        self::assertSame('none', $hPlain['X-Permitted-Cross-Domain-Policies']);
     }
 
     /** @return callable(string,bool):void */