DEV Community

Kamruzzaman Kamrul
Kamruzzaman Kamrul

Posted on

How to Escape XSS in Laravel Like a Pro (And Still Let Users Submit Rich Text)

How to Escape XSS in Laravel Like a Pro (And Still Let Users Submit Rich Text)

Cross-Site Scripting (XSS) remains one of the most common and dangerous web vulnerabilities out there.
As Laravel developers, escaping XSS properly is non-negotiable.

But what if you want to allow users to submit rich text content — like blog posts, comments with formatting, or product descriptions — without breaking security?

In this post, I’ll show you how to escape XSS in Laravel the right way while still letting your users submit safe rich text.


⚠️ What Is XSS and Why Should You Care?

XSS occurs when an attacker injects malicious JavaScript into your site, which then runs in other users’ browsers. The consequences can range from stealing cookies and session hijacking to full account takeover.

Laravel’s Blade templating engine escapes output by default:

{{ $userInput }}
Enter fullscreen mode Exit fullscreen mode

This means it converts special characters like <, >, & into HTML entities — preventing malicious scripts from running.


🚫 Common Blade Mistake: Using {!! !!} Without Care

To output raw HTML, many developers use Blade’s {!! $htmlContent !!} directive. But if $htmlContent contains untrusted user input, you open the door for XSS.

Never blindly trust raw HTML from users!


✅ Strategy 1: Use Laravel’s Built-in Escaping Wherever Possible

The best way to avoid XSS is to never output raw user input without escaping. That means:

{{ $user->name }}
Enter fullscreen mode Exit fullscreen mode

will always be safe.


🖋️ Strategy 2: Let Users Submit Rich Text Safely Using Sanitizers

If your app requires rich text input (bold, italics, links, images), you must sanitize that input before saving or rendering.

Popular Sanitization Tools for Laravel

  • mewebstudio/purifier
    A Laravel wrapper around HTMLPurifier, one of the best HTML sanitizers out there.

  • HTMLPurifier
    Removes malicious code while keeping allowed HTML tags intact.

Example usage with mewebstudio/purifier:

composer require mewebstudio/purifier
Enter fullscreen mode Exit fullscreen mode

Then, in your controller or model:

use Purifier;

$cleanHtml = Purifier::clean($request->input('content'));
Enter fullscreen mode Exit fullscreen mode

Now, you can safely store and output $cleanHtml with:

{!! $cleanHtml !!}
Enter fullscreen mode Exit fullscreen mode

Because it’s been sanitized, the risk of XSS is greatly reduced.


🎯 Configure Allowed Tags and Attributes

You don’t want users to upload <script> tags or inline event handlers (onclick, onerror).

Configure Purifier in config/purifier.php or publish its config:

'HTML.SafeIframe' => true,
'HTML.Allowed' => 'p,b,strong,i,em,u,a[href|title],ul,ol,li,br,img[src|alt|width|height],blockquote',
'Attr.AllowedFrameTargets' => ['_blank'],
Enter fullscreen mode Exit fullscreen mode

This allows only safe tags and attributes.


🧪 Strategy 3: Validate Inputs, Then Sanitize

Don’t skip validation before sanitization.

Example:

$request->validate([
    'content' => 'required|string|max:5000',
]);

$cleanHtml = Purifier::clean($request->input('content'));
Enter fullscreen mode Exit fullscreen mode

🚨 Bonus: Don’t Forget Other XSS Vectors

  • JavaScript in URLs: Sanitize or validate href attributes to avoid javascript: URLs.
  • CSS Injection: Restrict inline styles unless absolutely necessary.
  • Attribute Injection: Avoid allowing dangerous attributes like onload, style, or onerror.

🛠️ Strategy 4: Use Content Security Policy (CSP) Headers

Even with sanitization, adding CSP headers is a powerful additional layer.

Example middleware snippet:

public function handle($request, Closure $next)
{
    $response = $next($request);
    $response->headers->set('Content-Security-Policy', "default-src 'self'; script-src 'self';");
    return $response;
}
Enter fullscreen mode Exit fullscreen mode

Packages like spatie/laravel-csp help manage this easily.


🧩 TL;DR Best Practices for Escaping XSS & Handling Rich Text

  • Always escape output by default ({{ }})
  • Only use {!! !!} after sanitizing input
  • Use trusted sanitizers like mewebstudio/purifier
  • Validate input size and type before sanitizing
  • Configure allowed HTML tags and attributes carefully
  • Add CSP headers for extra defense
  • Regularly audit and test your app for XSS

📘 Want to Learn More?

XSS is just one piece of the Laravel security puzzle.

In my book Bulletproof Laravel: Write Code That Hackers Hate, I dive deep into:

  • Preventing XSS, CSRF, SQL injection
  • Secure authentication and authorization
  • Protecting file uploads and APIs
  • Real-world security scenarios and checklists

If you want to truly master Laravel security, grab your copy here:
https://www.amazon.com/dp/B0FFNT7BMQ


👇 Your Turn

How do you handle rich text safely in Laravel?
Have you faced tricky XSS bugs? Share your tips or horror stories below!

Top comments (0)