XML Sitemap [and XML RSS feed] breaks CSP, rest of website has no problems

46 Views Asked by At

I used the Spatie CSP package to add CSP to a website and all is good, no issues. But I discovered there is a problem with the XML sitemaps. Same with the XML RSS feed, both generate an error:

Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' 'nonce-7k7gFQ6FMMcVLsjCqciDMUiVD7LKVEvp' fonts.googleapis.com". Either the 'unsafe-inline' keyword, a hash ('sha256-p08VBe6m5i8+qtXWjnH/AN3klt1l4uoOLsjNn8BjdQo='), or a nonce ('nonce-...') is required to enable inline execution.

I'm not sure how to fix the problem. Has anyone else ran into this problem and could shed some light on the matter? The policy is:

<meta http-equiv="Content-Security-Policy" content="default-src 'self';style-src 'self' 'nonce-OSEVE4oRn4HgQdDkQPVf1LXfCfYfN2f5' fonts.googleapis.com;script-src 'self' 'nonce-OSEVE4oRn4HgQdDkQPVf1LXfCfYfN2f5' fonts.googleapis.com *.googletagmanager.com *.google-analytics.com;font-src 'self' fonts.gstatic.com fonts.googleapis.com data:;base-uri 'self';connect-src 'self' *.google-analytics.com;form-action 'self';media-src 'none';object-src 'none';img-src 'self' *.amazonaws.com 'unsafe-inline' *.google-analytics.com data:">

All the appropriate style and javascript tags have the nonce. Must I add the nonce to the XML declaration and if so, how? Right clicking on the sitemap page reveals the XML as expected.

The Spatie code is:

class Policy extends BasePolicy 
{
    /**
     * @see     https://github.com/spatie/laravel-csp
     * @see     https://freek.dev/982-using-content-security-policy-headers-in-a-laravel-app
     * @see     https://content-security-policy.com/
     * @see     https://csp-evaluator.withgoogle.com/
     * @see     https://christoph-rumpel.com/2018/03/content-security-policy-101
     * @see     https://glennsantos.com/implementing-a-content-security-policy-csp-in-laravel/
     */

    public function configure()
    {
        $this->addGenericDirectives();
        $this->addFontsDirectives();
        $this->addGoogleDirectives();
        $this->addImagesDirectives();
    }

    protected function addGenericDirectives() 
    {
        /**
         * @note    cover everything that is self hosted
         */

        $this->addDirective(Directive::DEFAULT, Keyword::SELF);
        $this->addDirective(Directive::STYLE, Keyword::SELF);
        $this->addDirective(Directive::SCRIPT, Keyword::SELF);
        $this->addDirective(Directive::FONT, Keyword::SELF);

        $this->addDirective(Directive::BASE, Keyword::SELF);
        $this->addDirective(Directive::CONNECT, Keyword::SELF);             
        $this->addDirective(Directive::FORM_ACTION, Keyword::SELF);
    
        $this->addDirective(Directive::MEDIA, Keyword::NONE);
        $this->addDirective(Directive::OBJECT, Keyword::NONE);

        $this->addNonceForDirective(Directive::SCRIPT);
        $this->addNonceForDirective(Directive::STYLE);
    }

    protected function addImagesDirectives()
    {
        /**
         * @note    add data: to the policy for bootstrap:
         * 
         *          - https://stackoverflow.com/a/70138826/17343181
         */
        
        $this->addDirective(Directive::IMG, [
            Keyword::SELF,
            '*.amazonaws.com',
            'unsafe-inline',
            '*.google-analytics.com',
            'data:',
        ]); 
    }

    protected function addFontsDirectives() 
    {
        $this->addDirective(Directive::FONT, [
            'fonts.gstatic.com',
            'fonts.googleapis.com',
            'data:'
        ]);
        $this->addDirective(Directive::SCRIPT, 'fonts.googleapis.com');
        $this->addDirective(Directive::STYLE, 'fonts.googleapis.com');
    }

    protected function addGoogleDirectives() 
    {
        $this->addDirective(Directive::SCRIPT, [
            '*.googletagmanager.com',
            '*.google-analytics.com',
        ]);

        /**
         * @note    required, for those scripts dynamically loaded
         */
        
        $this->addDirective(Directive::CONNECT, [
            '*.google-analytics.com',
        ]); 
    }

    protected function addDirectivesForYouTube()
    {
        return $this->addDirective(Directive::FRAME, '*.youtube.com');
    }

}

The blade file is:

<?php
echo '<?xml version="1.0" encoding="UTF-8" ?>';
?>
<urlset 
    xmlns="http://www.google.com/schemas/sitemap/0.84" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.google.com/schemas/sitemap/0.84 http://www.google.com/schemas/sitemap/0.84/sitemap.xsd">

    @foreach($posts as $post)
        <url>
            <loc>{{ config("app.url") }}/{{ $post->slug }}</loc>
            <lastmod>{{ $post->created_at->tz('UTC')->toAtomString() }}</lastmod>
        </url>
    @endforeach
</urlset> 

I would appreciate any help on these thanks.

I am not sure what changes to make to the CSP to add XML compliance, if any. Nothing in the Spatie documentation mentions anything about XML or extensible HTML either.

1

There are 1 best solutions below

0
Les On

The cause of the issue are the browsers themselves. In the meantime, one solution I believe to work is to put those public routes under a middleware and those few XML related routes outside of said middleware which uses the Spatie's CSP package.