@@ -2764,18 +2764,36 @@ function allowOrigin($allowAll = false)
27642764
27652765 // Public resources (e.g. VAST/VMAP ad XML) should be readable by any
27662766 // origin. Pass $allowAll = true for permissive CORS.
2767- // When the browser sends a credentialed request (credentials:'include', e.g.
2768- // the IMA SDK), it rejects the wildcard '*' – the spec requires echoing the
2769- // exact origin in that case. We reflect whatever origin is in the request
2770- // and add Allow-Credentials:true so credentialed fetches also succeed.
2771- // These endpoints return public ad XML and carry no session-sensitive data,
2772- // so reflecting any origin is safe here.
2767+ // SECURITY: even in $allowAll mode we must NOT reflect an arbitrary third-party
2768+ // Origin together with Access-Control-Allow-Credentials:true — that lets any
2769+ // attacker page make credentialed cross-origin requests and read session-
2770+ // authenticated API responses (user PII, stream keys, admin flags).
2771+ // We therefore validate the origin the same way as the $allowAll=false path:
2772+ // - Same-origin requests get reflected + credentials (logged-in browser calls)
2773+ // - All other origins get wildcard without credentials (public/ad-tech reads)
2774+ // Mobile apps use APISecret token auth, not session cookies, so they are
2775+ // unaffected by removing Allow-Credentials for third-party origins.
27732776 if ($ allowAll ) {
27742777 $ requestOrigin = $ _SERVER ['HTTP_ORIGIN ' ] ?? '' ;
2775- if (!empty ($ requestOrigin )) {
2778+
2779+ $ siteOriginForAllowAll = '' ;
2780+ if (!empty ($ global ['webSiteRootURL ' ])) {
2781+ $ parsedForAllowAll = parse_url ($ global ['webSiteRootURL ' ]);
2782+ if (!empty ($ parsedForAllowAll ['scheme ' ]) && !empty ($ parsedForAllowAll ['host ' ])) {
2783+ $ siteOriginForAllowAll = $ parsedForAllowAll ['scheme ' ] . ':// ' . $ parsedForAllowAll ['host ' ];
2784+ if (!empty ($ parsedForAllowAll ['port ' ])) {
2785+ $ siteOriginForAllowAll .= ': ' . $ parsedForAllowAll ['port ' ];
2786+ }
2787+ }
2788+ }
2789+
2790+ if (!empty ($ requestOrigin ) && !empty ($ siteOriginForAllowAll ) && $ requestOrigin === $ siteOriginForAllowAll ) {
2791+ // Verified same-origin request — reflect with credentials
27762792 header ('Access-Control-Allow-Origin: ' . $ requestOrigin );
27772793 header ('Access-Control-Allow-Credentials: true ' );
27782794 } else {
2795+ // Third-party or no origin: allow non-credentialed reads only.
2796+ // This covers IMA/ad-tech fetches (VAST/VMAP) which never send credentials.
27792797 header ('Access-Control-Allow-Origin: * ' );
27802798 }
27812799 header ('Access-Control-Allow-Private-Network: true ' );
0 commit comments