Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Html Sanitizer Laravel Package

symfony/html-sanitizer

Symfony HtmlSanitizer provides an object-oriented API to sanitize untrusted HTML before inserting it into the DOM. Configure allowed/blocked tags and attributes, drop or keep children, force attribute values, enforce HTTPS, and restrict link schemes/hosts to prevent XSS and unsafe behavior.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**:
   ```bash
   composer require symfony/html-sanitizer:^8.1

No additional configuration is required beyond autoloading.

  1. First Use Case: Sanitize user-generated HTML (e.g., from a WYSIWYG editor or comment field) before rendering it in a Laravel Blade view:

    use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
    
    $sanitizer = new HtmlSanitizer();
    $cleanHtml = $sanitizer->sanitize($userInputHtml);
    return view('post.show', ['content' => $cleanHtml]);
    
  2. Where to Look First:


Implementation Patterns

Core Workflows

  1. Basic Sanitization:

    $sanitizer = new HtmlSanitizer();
    $safeHtml = $sanitizer->sanitize($rawHtml);
    
    • New in v8.1: Automatically sanitizes URLs in specific attributes (e.g., action, formaction) to mitigate XSS via malicious URLs.
  2. Config-Driven Sanitization with Security Hardening:

    $config = (new HtmlSanitizerConfig())
        ->allowSafeElements()
        ->allowElement('img', ['src' => ['allowLinkHosts' => ['cdn.example.com']]])
        ->allowElement('a', ['href' => ['allowLinkHosts' => ['trusted.example.com']]])
        ->rejectBidirectionalChars(); // New in v8.1: Blocks BiDi override characters
    
    • Critical: Use allowLinkHosts/allowMediaHosts to restrict URLs in attributes like href, src, action, etc. The new release fixes bypasses in these rules.
  3. Dynamic Configuration for Admin/User Roles:

    $config = Auth::user()->isAdmin()
        ? (new HtmlSanitizerConfig())->allowStaticElements()
        : (new HtmlSanitizerConfig())
            ->allowSafeElements()
            ->rejectBidirectionalChars()
            ->allowElement('a', ['href' => ['allowLinkHosts' => ['trusted.example.com']]]);
    
  4. Laravel Service Provider with Updated Security: Bind the sanitizer to the container with a pre-configured secure config:

    // app/Providers/AppServiceProvider.php
    public function register()
    {
        $this->app->singleton(HtmlSanitizer::class, function ($app) {
            $config = (new HtmlSanitizerConfig())
                ->allowSafeElements()
                ->rejectBidirectionalChars()
                ->allowElement('a', ['href' => ['allowLinkHosts' => ['trusted.example.com']]])
                ->allowElement('img', ['src' => ['allowLinkHosts' => ['cdn.example.com']]]);
            return new HtmlSanitizer($config);
        });
    }
    
  5. Form Request Validation with URL Sanitization: Combine with Laravel’s validation to sanitize after validation, leveraging new URL sanitization:

    public function rules()
    {
        return ['content' => 'required|string'];
    }
    
    protected function sanitize($attribute, $value, $fail)
    {
        $sanitizer = app(HtmlSanitizer::class);
        $this->{$attribute} = $sanitizer->sanitize($value);
    }
    
  6. Blade Directives with Security: Create a custom Blade directive for inline sanitization, ensuring URL safety:

    // app/Providers/BladeServiceProvider.php
    Blade::directive('sanitize', function ($expression) {
        return "<?php echo app(\Symfony\Component\HtmlSanitizer\HtmlSanitizer::class)->sanitize({$expression}); ?>";
    });
    

    Usage:

    {!! sanitize($userHtml) !!}
    

Integration Tips

  • Database Storage: Sanitize before storing user-generated HTML to prevent malicious payloads, especially URLs in attributes like action or formaction.
  • API Responses: Sanitize HTML in API responses (e.g., rich-text fields) to prevent XSS in frontend frameworks. Use allowLinkHosts to restrict external URLs.
  • Testing: Verify URL sanitization in tests:
    $sanitizer = new HtmlSanitizer((new HtmlSanitizerConfig())
        ->allowElement('a', ['href' => ['allowLinkHosts' => ['trusted.example.com']]]));
    $this->assertEquals(
        '<a href="https://trusted.example.com">Safe</a>',
        $sanitizer->sanitize('<a href="javascript:alert(1)">Click</a>')
    );
    

Gotchas and Tips

Pitfalls

  1. URL-Based Attacks:

    • New in v8.1: The package now sanitizes URLs in action, formaction, poster, and cite attributes by default. However, always explicitly whitelist allowed hosts using allowLinkHosts/allowMediaHosts to avoid residual risks.
    • Example of a bypass (now fixed):
      <form action="javascript:alert(1)"> <!-- Previously allowed; now blocked -->
      
    • Mitigation: Use allowElement('form', ['action' => ['allowLinkHosts' => ['trusted.example.com']]]).
  2. BiDi (Bidirectional Text) Overrides:

    • New in v8.1: The package now rejects BiDi override characters (e.g., &#x202E;) by default to prevent UI redressing attacks.
    • Debugging: If legitimate content uses these characters, explicitly allow them (not recommended) or pre-process the input to remove them.
  3. Attribute Wildcard (*):

    • allowElement('span', '*') still permits all attributes, including dangerous ones like onclick. Prefer explicit whitelists:
      ->allowElement('span', ['class', 'style'])
      
  4. Nested Elements:

    • Blocking a parent element (e.g., blockElement('div')) retains its children. Use dropElement() to remove the entire subtree:
      ->dropElement('div'); // Deletes <div> and all descendants
      
  5. Performance:

    • Repeatedly creating HtmlSanitizer instances is expensive. Reuse a singleton or configure once per request lifecycle.
  6. Laravel Caching:

    • If using a configured HtmlSanitizer instance across requests, ensure thread safety (Symfony components are stateless, so this is rarely an issue).

Debugging

  1. Inspect Sanitized Output: Compare input/output to identify unintended drops/retentions, especially for URLs:

    $raw = '<form action="javascript:alert(1)">Submit</form>';
    $clean = $sanitizer->sanitize($raw);
    dd($raw, $clean); // Should show the form action is stripped
    
  2. Log Configuration: Temporarily log the config to verify rules, including URL hosts:

    $config = (new HtmlSanitizerConfig())
        ->allowElement('a', ['href' => ['allowLinkHosts' => ['trusted.example.com']]]);
    \Log::debug('Allowed hosts:', $config->getAllowedHosts('href'));
    
  3. Test Edge Cases:

    • URLs: Test malicious URLs in all relevant attributes (href, src, action, formaction, poster, cite):
      $sanitizer->sanitize('<a href="data:text/html,<script>alert(1)</script>">Click</a>');
      
    • BiDi Characters: Verify they are rejected by default:
      $sanitizer->sanitize('<div>&#x202E;Text</div>'); // Should be stripped
      
    • Unicode Spaces: Ensure spaces in URLs are percent-encoded:
      $sanitizer->sanitize('<a href="https://example.com/path with space">Link</a>');
      

Extension Points

  1. Custom URL Sanitization: Extend URL validation logic for specific use cases (e.g., internal paths
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai