DEV Community

Cover image for Symfony XSSI Attack: Fixes + Free Security Check
Pentest Testing Corp
Pentest Testing Corp

Posted on

Symfony XSSI Attack: Fixes + Free Security Check

Cross-Site Script Inclusion (XSSI) is a sneaky data-leak where an attacker <script>-loads your API and reads secrets if your endpoint returns executable JavaScript or unsafe JSON. Below is a concise, Symfony-focused guide with copy-paste fixes—perfect for dev readers and teams.

Symfony XSSI Attack: Fixes + Free Security Check

👉 Try our free website security scanner to spot XSSI and other issues.
Also see our blog: Pentest Testing Corp.


How XSSI happens (Symfony example)

Vulnerable: returning executable JS (not JSON). If an attacker includes it, your data becomes a global variable on their page.

// src/Controller/ProfileController.php #[Route('/api/profile.js', name: 'api_profile_js', methods: ['GET'])] public function profileAsJs(): Response { $data = ['email' => 'alice@example.com', 'role' => 'admin']; // ❌ Executable JavaScript — XSSI-prone $body = 'window.__profile = ' . json_encode($data) . ';'; return new Response($body, 200, ['Content-Type' => 'application/javascript']); } 
Enter fullscreen mode Exit fullscreen mode

Attacker page:

<script src="https://victim.example.com/api/profile.js"></script> <script> // now readable cross-site — data leaked console.log(window.__profile); </script> 
Enter fullscreen mode Exit fullscreen mode

📸 Screenshot of the Website Vulnerability Scanner tool homepage:

Screenshot of the free tools webpage where you can access security assessment tools.Screenshot of the free tools webpage where you can access security assessment tools.


Safer pattern #1: Strict JSON + anti-XSSI prefix

Return pure JSON (not JS) and add a harmless prefix that breaks execution if included via <script>.

// src/Controller/ProfileController.php use Symfony\Component\HttpFoundation\Response; #[Route('/api/profile', name: 'api_profile', methods: ['GET'])] public function profile(): Response { $data = ['email' => 'alice@example.com', 'role' => 'admin']; $prefix = ")]}',\n"; // breaks script execution; clients strip it before JSON.parse $json = $prefix . json_encode($data, JSON_UNESCAPED_SLASHES); return new Response($json, 200, [ 'Content-Type' => 'application/json; charset=utf-8', 'X-Content-Type-Options' => 'nosniff', 'Cross-Origin-Resource-Policy' => 'same-site', ]); } 
Enter fullscreen mode Exit fullscreen mode

Client parsing tip:

// strip prefix before JSON.parse fetch('/api/profile').then(r => r.text()).then(t => { const clean = t.replace(/^\)\]\}',\n?/, ''); return JSON.parse(clean); }); 
Enter fullscreen mode Exit fullscreen mode

Safer pattern #2: Block cross-origin inclusion at the edge

Add security headers globally via an event subscriber.

// src/EventSubscriber/SecurityHeadersSubscriber.php use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ResponseEvent; final class SecurityHeadersSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return ['kernel.response' => 'onKernelResponse']; } public function onKernelResponse(ResponseEvent $event): void { $r = $event->getResponse(); $r->headers->set('X-Content-Type-Options', 'nosniff'); $r->headers->set('Cross-Origin-Resource-Policy', 'same-site'); // Optional: CORP/CORB hardening $r->headers->set('Content-Type', $r->headers->get('Content-Type') ?: 'application/json; charset=utf-8'); } } 
Enter fullscreen mode Exit fullscreen mode

Safer pattern #3: Avoid GET for sensitive data + CSRF

Return sensitive data via POST and require a CSRF token.

# config/routes.yaml api_secure_profile: path: /api/secure/profile controller: App\Controller\SecureController::profile methods: [POST] 
Enter fullscreen mode Exit fullscreen mode
// src/Controller/SecureController.php use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; public function __construct(private CsrfTokenManagerInterface $csrf) {} public function profile(Request $req): JsonResponse { $token = $req->request->get('_token'); if (!$this->csrf->isTokenValid(new CsrfToken('api_profile', $token))) { return $this->json(['error' => 'invalid_csrf'], 403); } return $this->json(['ok' => true]); } 
Enter fullscreen mode Exit fullscreen mode

📸 Sample assessment report from our tool to check Website Vulnerability:

Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.


Bonus hardening checklist

  • Disable JSONP and never return executable JS from APIs.
  • Cookies: use SameSite=Lax or Strict, HttpOnly, Secure.
  • CORS: only allow trusted origins; avoid Access-Control-Allow-Origin: * with credentials.
  • CSP on your pages: script-src 'self' (reduces risky third-party scripts).
  • Audit endpoints with our free tool for a website security test.

Services from Pentest Testing Corp.


Newsletter: Subscribe on LinkedIn

Read more on our blog → https://www.pentesttesting.com/blog/.

Top comments (0)