Jump to content
nbcommunication

MarkupContentSecurityPolicy

Recommended Posts

Wondering how to get that A+ rating on Mozilla Observatory? Now you can with MarkupContentSecurityPolicy

Of course, MarkupContentSecurityPolicy does not guarantee an A+ rating, but it does help you implement a Content Security Policy for your ProcessWire website.

Markup Content Security Policy

Configure and implement a Content Security Policy for all front-end HTML pages.

This module should only be used in production once it has been fully tested in development. Implementing a Content Security Policy on a site without testing will almost certainly break something!

Overview

Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement to distribution of malware.

... Configuring Content Security Policy involves adding the Content-Security-Policy HTTP header to a web page and giving it values to control resources the user agent is allowed to load for that page. For example, a page that uploads and displays images could allow images from anywhere, but restrict a form action to a specific endpoint. A properly designed Content Security Policy helps protect a page against a cross site scripting attack.

 https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

Website Security Auditing Tools such as Mozilla Observatory will only return a high score if a Content Security Policy is implemented. It is therefore desirable to implement one.

A common way of adding the Content-Security-Policy header would be to add it to the .htaccess file in the site's root directory. However, this means the policy would also cover the ProcessWire admin, and this limits the level of security policy you can add.

The solution is to use the <meta> element to configure a policy, for example: <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">. MarkupContentSecurityPolicy places this element with your configured policy at the beginning of the <head> element on each HTML page of your site.

There are some limitations to using the <meta> element:

  • Not all directives are allowed. These include frame-ancestors, report-uri, and sandbox.
  • The Content-Security-Policy-Report-Only header is not supported, so is not available for use by this module.

Configuration

To configure this module, go to Modules > Configure > MarkupContentSecurityPolicy.

Directives

The most commonly used directives are listed, with a field for each. The placeholder values given are examples, not suggestions, but they may provide a useful starting point.

You will almost certainly need to use 'unsafe-inline' in the style-src directive as this is required by some modules (e.g. TextformatterVideoEmbed) or frameworks such as UIkit.

Should you wish to add any other directives not listed, you can do so by adding them in Any other directives.

Please refer to these links for more information on how to configure your policy:

Violation Reporting

Because the report-uri directive is not available, when Violation Reporting is enabled a script is added to the <head>which listens for a SecurityPolicyViolationEvent. This script is based on https://developer.mozilla.org/en-US/docs/Web/API/SecurityPolicyViolationEvent and POSTs the generated report to ?csp-violations=1. The module then logs the violation report to csp-violations.

Unfortunately, most of the violations that are reported are false positives, and not actual attempts to violate the policy. These are most likely from browser extensions and are not easy to determine and filter.

For this reason, there is no option for the report to be emailed when a policy is violated. Instead, you can specify an endpoint for the report to be sent to. This allows you to handle additional reporting in a way that meets your needs. For example, you may want to log all reports in a central location and send out an email once a day to an administrator notifying them of all sites with violations since the last email.

Retrieving the Report

To retrieve the report at your endpoint, the following can be used:

$report = file_get_contents("php://input");
if(!empty($report)) {
	$report = json_decode($report, 1);
	if(isset($report) && is_array($report) && isset($report["documentURI"])) {
		// Do something
	}
}

Debug Mode

When this is enabled, a range of information is logged to markup-content-security-policy. This is probably most useful when debugging a reporting endpoint.

Additional .htaccess Rules

To get an A+ score on Mozilla Observatory, besides using HTTPS and enabling the HSTS header, you can also place the following prior to ProcessWire's htaccess directives:

Header set Content-Security-Policy "frame-ancestors 'self'"
Header set Referrer-Policy "no-referrer-when-downgrade"

Installation

  1. Download the zip file at Github or clone the repo into your site/modules directory.
  2. If you downloaded the zip file, extract it in your sites/modules directory.
  3. In your admin, go to Modules > Refresh, then Modules > New, then click on the Install button for this module.

ProcessWire >= 3.0.123 is required to use this module.

  • Like 13

Share this post


Link to post
Share on other sites

Fantastic timing - just about to look into this myself for a site. Thank you!

Share this post


Link to post
Share on other sites
27 minutes ago, netcarver said:

Fantastic timing - just about to look into this myself for a site. Thank you!

Please let me know how you find it - this module is really just a result of looking into it the past two weeks to try and get an A+ on Mozilla Observatory (did it😆) so I'd really appreciate any feedback!

  • Like 4

Share this post


Link to post
Share on other sites

Update to 0.0.2

  • Deploy mode (only enable for superuser when off)
  • Define params to exclude from reporting (e.g. originalPolicy)
  • Filtering of selected false positive patterns
  • Improvements to HTML page check
  • Like 1

Share this post


Link to post
Share on other sites

Hello,

I thought it might be useful to post a CSP I've recently deployed using this module. Every site is different - there's no prescriptive policy and that's the main caveat here.

This is for a site with an embedded Shopify store, an Issuu embed, a Google Tour embed and Google Maps implementation (JS API). It also uses Font Awesome 5 from their CDN, jQuery from CDNJS, and some Google Fonts. It also has TextformatterVideoEmbed installed alongside its extended options module.

default-src 'none'; 
script-src 'self' cdnjs.cloudflare.com *.google.com *.gstatic.com *.googleapis.com www.google-analytics.com www.googletagmanager.com e.issuu.com sdks.shopifycdn.com; 
style-src 'self' 'unsafe-inline' cdnjs.cloudflare.com *.googleapis.com use.fontawesome.com; 
img-src 'self' data: *.google.com *.googleapis.com *.gstatic.com *.ggpht.com www.google-analytics.com www.googletagmanager.com brand.nbcommunication.com *.shopify.com sdks.shopifycdn.com; 
connect-src 'self' www.google-analytics.com ocean-kinetics.myshopify.com; 
font-src 'self' fonts.gstatic.com use.fontawesome.com; 
object-src 'self'; 
media-src 'self' data:; 
manifest-src 'self'; 
frame-src www.google.com www.youtube.com www.youtube-nocookie.com player.vimeo.com w.soundcloud.com e.issuu.com; 
form-action 'self'; 
base-uri 'self'

The Shopify embed script and Google Analytics initialisation have been moved into script files so there are no inline scripts at all. The script-src 'unsafe-inline' directive is an obstacle to getting that A+ on Observatory. 

Google Analytics is also a bit of an impediment to getting a top-drawer score, as its script doesn't use SRI. However, there is a reason for that as I understand it - it is a script that just loads other scripts so SRI implementation would just be token, it wouldn't actually be improving security. Still, it is possible to get A+ without dealing with this.

It would be great to get some discussion going on CSP implementation - I'm only a few weeks in myself, so have much to learn!

Cheers,

Chris
NB

 

  • Like 2

Share this post


Link to post
Share on other sites

Hi Chris,

I had a chance to try this out today, and it's been pretty useful though I still have a lot to learn about CSPs. One quick observation is that it inserts at the start of the <head> block, before the charset and viewport meta tags. IIRC, the charset and viewport meta-tags are meant to be the first tags in the head block.

Will keep experimenting with the module.

  • Like 2

Share this post


Link to post
Share on other sites
36 minutes ago, nbcommunication said:

Thanks for the feedback - I'll have a look at this soon and see if I can get it placed better.

Hi and thanks for this useful module. I added to my list of things I need to explore soon. ;-)

A common usage for modules that need to inject content in the head or body output of pages, is to optionally serve a manual way per API call besides the automatic way. 

Share this post


Link to post
Share on other sites

@netcarver- I've got it automatically placed after the <title> element now, if that element exists (which it should!). 

@horst - I've added a renderMeta method to allow for manual outputting of the meta tag in the <head>. If this is used, it shouldn't output automatically.

  • Like 3

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By bernhard
      --- Please use RockFinder3 ---
    • By MoritzLost
      Cacheable Placeholders
      This module allows you to have pieces of dynamic content inside cached output. This aims to solve the common problem of having a mostly cacheable site, but with pieces of dynamic output here and there.  Consider this simple example, where you want to output a custom greeting to the current user:
      <h1>Good morning, <?= ucfirst($user->name) ?></h1> This snippet means you can't use the template cache (at least for logged-in users), because each user has a different name. Even if 99% of your output is static, you can only cache the pieces that you know won't include this personal greeting. A more common example would be CSRF tokens for HTML forms - those need to be unique by definition, so you can't cache the form wholesale.
      This module solves this problem by introducing cacheable placeholders - small placeholder tokens that get replaced during every request. The replacement is done inside a Page::render hook so it runs during every request, even if the response is served from the template cache. So you can use something like this:
      <h1>Good morning, {{{greeting}}}</h1> Replacement tokens are defined with a callback function that produces the appropriate output and added to the module through a simple hook:
      // site/ready.php wire()->addHookAfter('CachePlaceholders::getTokens', function (HookEvent $e) { $tokens = $e->return; $tokens['greeting'] = [ 'callback' => function (array $tokenData) { return ucfirst(wire('user')->name); } ]; $e->return = $tokens; }); Tokens can also include parameters that are parsed and passed to the callback function. There are more fully annotated examples and step-by-step instructions in the README on Github!
      Features
      A simple and fast token parser that calls the appropriate callback and runs automatically. Tokens may include multiple named or positional parameters, as well as multi-value parameters. A manual mode that allows you to replace tokens in custom pieces of cached content (useful if you're using the $cache API). Some built-in tokens for common use-cases: CSRF-Tokens, replacing values from superglobals and producing random hexadecimal strings. The token format is completely customizable, all delimiters can be changed to avoid collisions with existing tag parsers or template languages. Links
      Github Repository & documentation Module directory (pending approval) If you are interested in learning more, the README is very extensive, with more usage examples, code samples and usage instructions!
    • By Craig
      I've been using Fathom Analytics for a while now and on a growing number of sites, so thought it was about time there was a PW module for it.
      WayFathomAnalytics
      WayFathomAnalytics is a group of modules which will allow you to view your Fathom Analytics dashboard in the PW admin panel and (optionally) automatically add and configure the tracking code on front-end pages.
      Links
      GitHub Readme & documentation Download Zip Modules directory Module settings screenshot What is Fathom Analytics?
      Fathom Analytics is a simple, privacy-focused website analytics tool for bloggers and businesses.

      Stop scrolling through pages of reports and collecting gobs of personal data about your visitors, both of which you probably don't need. Fathom is a simple and private website analytics platform that lets you focus on what's important: your business.
      Privacy focused Fast-loading dashboards, all data is on a single screen Easy to get what you need, no training required Unlimited email reports Private or public dashboard sharing Cookie notices not required (it doesn't use cookies or collect personal data) Displays: top content, top referrers, top goals and more
    • By daniels
      This is a lightweight alternative to other newsletter & newsletter-subscription modules.
      You can find the Module in the Modules directory and on Github
      It can subscribe, update, unsubscribe & delete a user in a list in Mailchimp with MailChimp API 3.0. It does not provide any forms or validation, so you can feel free to use your own. To protect your users, it does not save any user data in logs or sends them to an admin.
      This module fits your needs if you...
      ...use Mailchimp as your newsletter / email-automation tool ...want to let users subscribe to your newsletter on your website ...want to use your own form, validation and messages (with or without the wire forms) ...don't want any personal user data saved in any way in your ProcessWire environment (cf. EU data regulation terms) ...like to subscribe, update, unsubscribe or delete users to/from different lists ...like the Mailchimp UI for creating / sending / reviewing email campaigns *I have only tested it with PHP 7.x so far, so use on owners risk
      EDIT:
      Since 0.0.4, instructions and changelog can be found in the README only. You can find it here  🙂
      If you have questions or like to contribute, just post a reply or create an issue or pr on github, thanks!
    • By MoritzLost
      Sorry for the convoluted title. I have a problem with Process modules that define a custom page using the page key through getModuleInfo (as demonstrated in this excellent tutorial by @bernhard). Those pages are created automatically when the module is installed. The problem is that the title of the page only gets set in the current language. That's not a problem if the current language (language of the superuser who is installing the module) is the default language; if it isn't, the Process page is missing a title in the default language. This has the very awkward effect that a user using the backend in the default language (or any other language) will see an empty entry in the setup menu:

      This screenshot comes from my Cache Control module which includes a Process page. Now I realize the description sounds obscure, but for us it's a common setup: We a multiple bilingual sites where the default language is German and the second language is English. While the clients use the CMS in German, as a developer I prefer the English interface, so whenever I install a Process module I get this problem.
      As a module author, is there a way to handle this situation? I guess it would be possible to use post-installation hooks or create the pages manually, but I very much prefer the declarative approach. The page title is already translatable (through the __ function), but of course at the time of installation there is no translation, and as far as I'm aware it's not possible to ship translations with a module so they are used automatically. Could this situation be handled better in the core? I would prefer if the module installation process would always set the title of the Process page in the default language, instead of the language of the current user.
×
×
  • Create New...