src/Subscriber/SecurityHeaderSubscriber.php line 47

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Subscriber;
  4. use App\Store\StoreContext;
  5. use Psr\Log\LoggerInterface;
  6. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  7. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  8. use Symfony\Component\HttpKernel\KernelEvents;
  9. /**
  10. * Setting various security headers
  11. *
  12. * @package App\Subscriber
  13. */
  14. class SecurityHeaderSubscriber implements EventSubscriberInterface
  15. {
  16. private StoreContext $storeContext;
  17. private LoggerInterface $logger;
  18. public function __construct(StoreContext $storeContext, LoggerInterface $logger)
  19. {
  20. $this->storeContext = $storeContext;
  21. $this->logger = $logger;
  22. }
  23. /**
  24. * Returns an array of event names this subscriber wants to listen to.
  25. *
  26. * @return array The event names to listen to
  27. */
  28. public static function getSubscribedEvents(): array
  29. {
  30. // run after EventListener\DisallowRobotsIndexingListener
  31. return [KernelEvents::RESPONSE => ['onKernelResponse', -256]];
  32. }
  33. /**
  34. * Conservative add headers (don't replace exiting ones)
  35. *
  36. * @param ResponseEvent $event
  37. *
  38. * @return void
  39. */
  40. public function onKernelResponse(ResponseEvent $event): void
  41. {
  42. $headers = $event->getResponse()->headers;
  43. // Instructs browser to not send referer when downgrading from https to http
  44. if (!$headers->has('Referrer-Policy')) {
  45. $headers->set('Referrer-Policy', 'strict-origin-when-cross-origin', false);
  46. }
  47. // Prevents browser from trying to guess the mime-type of a response.
  48. // Reduces risk of execution of user uploaded content and exposure to drive-by downloads
  49. if (!$headers->has('X-Content-Type-Options')) {
  50. $headers->set('X-Content-Type-Options', 'nosniff', false);
  51. }
  52. // It would be better if Content-Security-Policy can be more strict. But that requires checking all used resources across the whole store
  53. if ($event->getRequest()->isSecure() && !$headers->has('Content-Security-Policy')) { // if this request is not secure, it makes little sense to force an upgrade for all used resources
  54. $headers->set('Content-Security-Policy', 'upgrade-insecure-requests', false);
  55. }
  56. // Use CSP to allow iframe embed for trusted resources (needed for SAP ARIBA integration)
  57. if ($headers->has('X-Frame-Options')) {
  58. $headers->remove('X-Frame-Options');
  59. }
  60. if ($this->storeContext->isPunchoutStore()) {
  61. $headers->set('Content-Security-Policy', "frame-ancestors *", false);
  62. } else {
  63. $headers->set('Content-Security-Policy', "frame-ancestors 'self' app.storyblok.com", false);
  64. }
  65. // Disable something that is very unlikely to be used, just to add this header
  66. if (!$headers->has('Feature-Policy')) {
  67. $headers->set('Feature-Policy', 'usb \'none\'', false);
  68. }
  69. }
  70. }