> ## Content Index
> Fetch the complete content index at: https://securinglaravel.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Security Tip: When Is XSS Not Strictly XSS? (But Still Bad!)
- URL: https://securinglaravel.com/security-tip-when-is-xss-not-strictly-xss-but-still-bad/
- Published: 2025-09-08T09:22:44.000Z
- Updated: 2025-09-08T09:22:44.000Z
- Description: [Tip #121] Technically, XSS involves injecting malicious Javascript, but sometimes you don't need any JS to get up to mischief! 😈
- Author: Stephen Rees-Carter
- Tags: Security Tips, XSS, Alpine, Livewire, Injection, Content Security Policy

Great news! Alpine and Livewire are getting a CSP safe mode! Soon (v4!) you'll be able to run Livewire with your CSP and get rid of those pesky `unsafe-*`directives! 🎉 (I'm excited, can you tell??) 

If you want to check it out early, here are the PRs: 

- [https://github.com/alpinejs/alpine/pull/4671](https://github.com/alpinejs/alpine/pull/4671?ref=securinglaravel.com)
- [https://github.com/livewire/livewire/pull/9461](https://github.com/livewire/livewire/pull/9461?ref=securinglaravel.com)

Setting aside my excitement for a bit, when I was reviewing the Alpine PR, I was thinking about an interesting XSS attack vector:

**If an attacker can inject HTML onto the page, can trigger existing javascript functions and perform unauthorised actions, without submitting any actual javascript!**

It's XSS without the Scripting... or the Cross-Site... it's just an... attack? 🤷  
Let's stick with calling it XSS for now, as far as I am aware there isn't a better name for it.

💡

This isn't a new idea, it's been around since frontend enhancements were a thing, and is definitely not unique to Alpine! Alpine just makes this easy to demonstrate.

Let's see this attack in action!

### Traditional XSS

We'll start by first looking at how traditional XSS may look:

![](https://storage.ghost.io/c/d9/0d/d90de76f-6031-4e2c-85b8-3447a38c4992/content/images/2025/09/image-2.png)

Injected inline Javascript

And if I click on `Click me`, I get this:

## Want to Learn More?

Sign up for ****Securing Laravel**, the essential security resource for Laravel developers. You'll receive [weekly security tips](https://securinglaravel.com/tag/tips/) (like this one!), and upgrade to receive [monthly In Depth articles](https://securinglaravel.com/tag/in-depth/) that go even deeper and to support my security work within the Laravel & PHP Community!

Subscribe 

Email sent! Check your inbox to complete your signup. 

No spam. Unsubscribe anytime.

![](https://storage.ghost.io/c/d9/0d/d90de76f-6031-4e2c-85b8-3447a38c4992/content/images/2025/09/image-3.png)

Boom! XSS fired.

This is easily blocked by a CSP that does not include `unsafe-inline` directive. 

```
Content-Security-Policy: script-src 'self' 'strict-dynamic' 'nonce-*';
```

Clicking on `Click me!` gives us a violation and blocks the Alert:

![](https://storage.ghost.io/c/d9/0d/d90de76f-6031-4e2c-85b8-3447a38c4992/content/images/2025/09/image-4.png)

"Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self' 'strict-dynamic' 'nonce-Y\_nzHVk5pxi8Evk4scqSBA'"."

### Alpine Directives

Let's switch from raw Javascript to an Alpine directive:

```HTML
<div x-on:click="alert('Boom!')">Click me!</div>
```

I've also switched my CSP over to `Content-Security-Policy-Report-Only` (because the CSP-friendly version isn't installed), so it'll still let javascript execute - but we will see all violations in the console.

Here's what happens when I click on `Click me!`:

![](https://storage.ghost.io/c/d9/0d/d90de76f-6031-4e2c-85b8-3447a38c4992/content/images/2025/09/image-5.png)

No CSP violations thrown!

Nothing. 🧐

The CSP didn't detect the injected directive, and didn't report it. 😱

We've completely bypassed the CSP by using Alpine, instead of raw javascript, and Alpine & Livewire's new CSP safe mode **isn't going to stop this attack.**

This example is trivial, but consider what functions could be available in the frontend - admin controls come to mind! There was a fun attack in the WordPress world that abused an *Add User* javascript function to create a new admin account via an XSS payload on a public form. 😈

### How do you stop this?

Don't render untrusted user input unescaped - [always escape or sanitise that output](https://securinglaravel.com/tag/xss/). You can never trust user input, to make sure you always render it with as much paranoia as you can muster. 

This attack works because HTML can do a lot of things in a lot of ways. If you need to let users submit HTML, run it through a parser and strip out ***everything*** that isn't essential. [This includes the style directive!](https://securinglaravel.com/in-depth-css-clickjacking/) 

Sometimes you do need to output user input where a frontend parser may consume it and perform actions. In this case, some frontend frameworks usually directives that can help: [Alpine has x-ignore](https://alpinejs.dev/directives/ignore?ref=securinglaravel.com) and [Vue has v-pre](https://vuejs.org/api/built-in-directives.html?ref=securinglaravel.com#v-pre) \- both of which instruct the framework to leave the HTML element untouched.

For example, if I add `x-ignore` to my page, the `Click me!` stops working entirely:

```HTML
<div class="mt-6" x-ignore>
    <h2 class="text-lg font-medium">Stored value</h2>
    <div class="mt-2 p-3 rounded bg-neutral-100 dark:bg-neutral-800">{{ $stored }}</div>
    <h2 class="text-lg font-medium">Raw HTML</h2>
    <div class="mt-2 p-3 rounded bg-neutral-100 dark:bg-neutral-800">{!! $stored !!}</div>
</div>
```

x-ignore directive added on the top line

---

***If you found this security tip useful?* 👍**  
[*Subscribe now*](#/portal/signup) *to get weekly* [***Security Tips***](https://securinglaravel.com/tag/tips/) *straight to your inbox, filled with practical, actionable advice to help you build safer apps.*

***Want to learn more?* 🤓**  
*Upgrade to a* [*Premium Subscription*](#/portal/signup) *for exclusive monthly* [**In Depth* articles*](https://securinglaravel.com/tag/in-depth/)*, or support my work with a* [*one-off tip*](#/portal/support) *or* [*recurring Sponsorship*](https://securinglaravel.com/sponsor/)*! Your support directly funds my security work in the Laravel community.* 🥰

**Need a second set of eyes on your code?** 
*Book in a* [*Laravel Security Audit and Penetration Test*](https://stephenreescarter.net/laravel-security-audits-and-pentesting/?utm%5Fsource=securinglaravel.com) *today! I also offer budget-friendly* [*Security Reviews*](https://stephenreescarter.net/laravel-security-reviews/?utm%5Fsource=securinglaravel.com) *too.*

*Finally, connect with me on* [*Bluesky*](https://bsky.app/profile/valorin.bsky.social?ref=securinglaravel.com)*, or* [*other socials*](https://pinkary.com/@valorin?ref=securinglaravel.com)*, and check out* [*Practical Laravel Security*](https://practicallaravelsecurity.com/?utm%5Fsource=securinglaravel.com)*, my interactive course designed to boost your Laravel security skills.*