Security-Header-Architektur für Shopware 6
Alle Security Headers zentral konfigurieren — shopware.yaml für das native CSP-System, Symfony EventSubscriber für weitere Header und die richtige Plugin-Struktur.
Security Headers in Shopware 6 — zwei Konfigurationswege
Shopware 6.5+ basiert auf Symfony und bietet zwei architektonisch verschiedene Wege zur Header-Konfiguration. Die native shopware.yaml steuert die Content Security Policy über csp_templates mit vier Kontexten: default, storefront, administration und store-api. Für alle weiteren Security Headers erstellen Sie einen Symfony EventSubscriber in einem Custom Plugin.
Diese zweigeteilte Architektur ist Shopware-spezifisch: CSP wird nativ verwaltet, weil Shopware den %nonce%-Platzhalter unterstützt und verschiedene Kontexte (Storefront vs. Administration) unterschiedliche Policies benötigen. Alle anderen Header setzen Sie über einen EventSubscriber, der auf kernel.response Events hört.
Implementierung: shopware.yaml und EventSubscriber
Die Konfiguration teilt sich in zwei Bereiche: shopware.yaml für die Content Security Policy (Variante A) und ein EventSubscriber-Plugin für alle weiteren Header (Variante B). Beide Wege funktionieren sowohl in Shopware Cloud als auch Self-Hosted.
# config/packages/shopware.yaml — CSP-Konfiguration
shopware:
security:
csp_templates:
# Standard für alle Kontexte
default:
default-src: "'self'"
script-src: "'self' '%nonce%' 'strict-dynamic'"
style-src: "'self' 'unsafe-inline'"
img-src: "'self' data: https:"
font-src: "'self'"
object-src: "'none'"
base-uri: "'self'"
# Storefront — mit Payment-Providern
storefront:
script-src: "'self' '%nonce%' *.paypal.com js.stripe.com *.klarna.com"
frame-src: "'self' *.paypal.com *.stripe.com *.klarna.com"
connect-src: "'self' *.paypal.com *.stripe.com *.klarna.com"
# Administration — großzügiger
administration:
script-src: "'self' 'unsafe-eval' 'unsafe-inline'"
style-src: "'self' 'unsafe-inline'" <?php
// custom/plugins/SecurityHeaders/src/Subscriber/SecurityHeaderSubscriber.php
namespace SecurityHeaders\Subscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class SecurityHeaderSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
KernelEvents::RESPONSE => ['onResponse', -256],
];
}
public function onResponse(ResponseEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$response = $event->getResponse();
// HSTS — nur über HTTPS senden
$response->headers->set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
// Basis-Header
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Permissions-Policy',
'camera=(), microphone=(), geolocation=()');
// Cross-Origin Headers
$response->headers->set('Cross-Origin-Opener-Policy', 'same-origin');
$response->headers->set('Cross-Origin-Resource-Policy', 'same-origin');
$response->headers->set('Origin-Agent-Cluster', '?1');
}
} <!-- custom/plugins/SecurityHeaders/src/Resources/config/services.xml -->
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services">
<services>
<service id="SecurityHeaders\Subscriber\SecurityHeaderSubscriber">
<tag name="kernel.event_subscriber"/>
</service>
</services>
</container> Shopware unterscheidet default (Fallback), storefront (Kundenseite), administration (Backend) und store-api (Headless-API). Die Storefront braucht Payment-Provider-Domains, die Administration braucht 'unsafe-eval' für das Vue.js-Backend. Mischen Sie diese Kontexte nicht.
Verifizierung
Nach jeder Konfigurationsänderung muss der Shopware-Cache geleert werden. Ohne bin/console cache:clear werden Änderungen in shopware.yaml und neue EventSubscriber nicht wirksam.
# Cache leeren (WICHTIG nach jeder Konfigurationsänderung)
bin/console cache:clear
# Security Headers prüfen
curl -sI https://ihr-shop.de | grep -iE "content-security|strict-transport|x-frame|referrer|x-content-type|permissions-policy"
# Erwartete Ausgabe (alle Header vorhanden):
# content-security-policy: default-src 'self'; ...
# strict-transport-security: max-age=31536000; ...
# x-frame-options: SAMEORIGIN
# referrer-policy: strict-origin-when-cross-origin
# x-content-type-options: nosniff
# permissions-policy: camera=(), microphone=(), ... Häufige Fehler
bin/console cache:clear vergessen
Die häufigste Fehlerquelle: Shopware cached Konfigurationen aggressiv. Änderungen in shopware.yaml und neue EventSubscriber werden erst nach bin/console cache:clear wirksam. Im Produktivbetrieb nutzen Sie bin/console cache:warmup nach dem Leeren.
Administration-CSP vs Storefront-CSP verwechselt
Die Administration benötigt 'unsafe-eval' für das Vue.js-Backend. Die Storefront braucht Payment-Provider-Domains. Wenn Sie die Kontexte verwechseln, funktioniert entweder das Admin-Panel oder der Checkout nicht mehr.
Plugin setzt eigene Header die überschrieben werden
Shopware-Plugins können eigene EventSubscriber mit höherer Priorität registrieren. Prüfen Sie die Priorität Ihres Subscribers (negative Werte = späte Ausführung). Mit -256 stellen Sie sicher, dass Ihr Subscriber nach den meisten Plugins läuft.
Shopware Cloud: Kein Server-Config-Zugriff
In Shopware Cloud können Sie weder Nginx noch Apache konfigurieren. Setzen Sie alle Header über shopware.yaml (CSP) und EventSubscriber (Rest). Server-Level-Header wie bei Self-Hosted sind nicht möglich.
Compliance-Relevanz
Eine konsolidierte Security-Header-Architektur in Shopware unterstützt direkt die Anforderungen mehrerer Compliance-Frameworks — besonders relevant für E-Commerce mit Zahlungsverkehr.
Wie steht Ihre Domain bei Implementierungs-Architektur?
Prüfen Sie es jetzt — kostenlos, ohne Registrierung, mit 166 Prüfpunkte.