fix: allow same-origin fetch in CSP when inline login script is used
When subdomain redirection is off, the login form is served inline on the protected host and submission happens via a same-origin fetch() call in _script.html.twig. The CSP default-src 'none' was blocking that fetch (connect-src falls back to default-src). Add connect-src 'self' to the CSP only when the request is not on the auth subdomain (i.e. when the inline script is present). On the auth subdomain the form POSTs normally with no inline script, so the stricter policy still applies there. This is the least-privilege relaxation: only same-origin connections, only on pages that need them.
This commit was merged in pull request #9.
This commit is contained in:
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Listener;
|
||||
|
||||
use App\Service\DomainInterface;
|
||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
@@ -15,6 +16,11 @@ use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
*/
|
||||
final readonly class SecurityHeadersListener
|
||||
{
|
||||
public function __construct(
|
||||
private DomainInterface $domainManager,
|
||||
) {
|
||||
}
|
||||
|
||||
#[AsEventListener(priority: 0)]
|
||||
public function onKernelResponse(ResponseEvent $event): void
|
||||
{
|
||||
@@ -36,11 +42,24 @@ final readonly class SecurityHeadersListener
|
||||
|
||||
/* Content-Security-Policy — the login page uses inline styles
|
||||
* and scripts (via Twig includes), so we allow 'unsafe-inline'
|
||||
* for those. No external resources are loaded. */
|
||||
$headers->set(
|
||||
'Content-Security-Policy',
|
||||
"default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline';"
|
||||
);
|
||||
* for those. No external resources are loaded.
|
||||
*
|
||||
* When subdomain redirection is off (or the request is not on
|
||||
* the auth subdomain), the login form is served inline on the
|
||||
* protected host and submission is performed via a same-origin
|
||||
* fetch() call in _script.html.twig. That fetch is blocked by
|
||||
* the default 'none' policy, so we add connect-src 'self' only
|
||||
* in that case — the least privilege needed to make the form
|
||||
* work. On the auth subdomain the form POSTs normally and no
|
||||
* inline script is included, so the stricter policy applies. */
|
||||
$inlineScript = $this->domainManager->getAuthSubdomain() !== $event->getRequest()->getHost();
|
||||
$csp = "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline';";
|
||||
|
||||
if ($inlineScript) {
|
||||
$csp .= " connect-src 'self';";
|
||||
}
|
||||
|
||||
$headers->set('Content-Security-Policy', $csp);
|
||||
|
||||
/* HSTS — enforce HTTPS for one year (app is designed for HTTPS behind a proxy) */
|
||||
$headers->set('Strict-Transport-Security', 'max-age=31536000');
|
||||
|
||||
Reference in New Issue
Block a user