bnomei / kirby3-security-headers Goto Github PK
View Code? Open in Web Editor NEWKirby Plugin for easier Security Headers setup
License: MIT License
Kirby Plugin for easier Security Headers setup
License: MIT License
will add an option disabling cps for panel by default. this option will be enabled by default once the issue has been fixed.
Question
With default setup my style attributes are rejected. how to fix this?
Answer 1
Create a custom snippet and allow unsafe inline. Not a very good idea.
Answer 2
Use CSS Object Model (CSSOM) to set style. Do this by storing at adata
-attribute and apply with jQuery/Zepto.
https://stackoverflow.com/questions/24713440/banned-inline-style-csp-and-dynamic-positioning-of-html-elements/29089970#29089970
json string in data attr
NOTE: json string needs "
so wrap in '
.
<div id="special" data-style='{ "backgroundColor": "#8EE", "fontSize": 28 }'>...</div>
jquery/zepto
https://api.jquery.com/css/#css-properties
https://zeptojs.com/#css
var specialStyle = JSON.parse($('#special').attr('data-style'));
elem.css(specialStyle);
$('[data-style]').each(function (idx, ele) {
let data = $(ele).attr('data-style')
if (data !== undefined && data.length > 0) {
let style = JSON.parse(data)
$(ele).css(style)
}
})
Kirby v3, security headers plugin and dependencies installed via composer in the most recent versions by the time of writing.
I set up this plugin to output CSP headers matching required hostnames from the website like this:
'bnomei.securityheaders.headers' => [
"X-Powered-By" => "", // unset
"X-Frame-Options" => "SAMEORIGIN",
"X-XSS-Protection" => "1; mode=block",
"X-Content-Type-Options" => "nosniff",
"strict-transport-security" => "max-age=31536000; includeSubdomains",
"Referrer-Policy" => "no-referrer-when-downgrade",
// Generated with https://www.cspisawesome.com/content_security_policies
"Content-Security-Policy" => "default-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval' stats.autark-app.de www.google.com www.gstatic.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self' data: a.basemaps.cartocdn.com b.basemaps.cartocdn.com c.basemaps.cartocdn.com; frame-src www.google.com; font-src 'self' fonts.gstatic.com; connect-src 'self'",
"X-Content-Security-Policy" => "default-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval' stats.autark-app.de www.google.com www.gstatic.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self' data: a.basemaps.cartocdn.com b.basemaps.cartocdn.com c.basemaps.cartocdn.com; frame-src www.google.com; font-src 'self' fonts.gstatic.com; connect-src 'self'",
"X-WebKit-CSP" => "default-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval' stats.autark-app.de www.google.com www.gstatic.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self' data: a.basemaps.cartocdn.com b.basemaps.cartocdn.com c.basemaps.cartocdn.com; frame-src www.google.com; font-src 'self' fonts.gstatic.com; connect-src 'self'",
],
But it seems to be ignored, as the headers sent to the browser are these:
Content-Security-Policy: base-uri 'self'; default-src 'self'; connect-src 'self'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; frame-src; img-src 'self' data:; media-src 'none'; object-src 'none'; script-src 'self' 'nonce-MTc1NWZjOGY1NDRiNmE0MmI3MjM1ZGUxNTdiZmI3MjU5YzgxNDMwYg=='; style-src 'self' 'nonce-MTc1NWZjOGY1NDRiNmE0MmI3MjM1ZGUxNTdiZmI3MjU5YzgxNDMwYg=='; worker-src; upgrade-insecure-requests
Using <?= $site->nonceAttr() ?>
returns an empty attribute, but if printed out in, for example, a paragraphs inner HTML it is not empty. I disabled or removed any plugin manipulating the html code but the problem persists.
If the debug mode in the config.php is enabled, everything works as expected but that is not a solution.
I am using this plugin in Kirby 4.
https://github.com/paragonie/csp-builder
to add support for worker-src and child-src.
but do major version increase.
Hi,
I have this plugin working with the default setup, but am struggling to use a custom json file to adjust the policy. This is my config.php (included in full in case there is an issue elsewhere):
<?php
return [
'bnomei.securityheaders.loader' => function () {
return kirby()->roots()->site() . '/csp.json';
},
'routes' => [
[
'pattern' => 'sitemap.xml',
'action' => function() {
$pages = site()->pages()->index();
// fetch the pages to ignore from the config settings,
// if nothing is set, we ignore the error page
$ignore = kirby()->option('sitemap.ignore', ['error']);
$content = snippet('sitemap', compact('pages', 'ignore'), true);
// return response with correct header type
return new Kirby\Cms\Response($content, 'application/xml');
}
],
[
'pattern' => 'sitemap',
'action' => function() {
return go('sitemap.xml', 301);
}
]
],
];
?>
Then in my site root I have the following json file (csp.json):
{
"report-only": false,
"base-uri": {
"self": true
},
"default-src": {
"self": true
},
"connect-src": {
"self": true
},
"font-src": {
"self": true
},
"form-action": {
"allow": [],
"self": true
},
"frame-ancestors": [],
"frame-src": {
"allow": [],
"self": false
},
"img-src": {
"self": true,
"data": true
},
"media-src": [],
"object-src": [],
"plugin-types": [],
"script-src": {
"allow": [],
"hashes": [],
"self": true,
"unsafe-inline": false,
"unsafe-eval": true
},
"style-src": {
"self": true
},
"upgrade-insecure-requests": true,
"worker-src": {
"allow": [],
"self": false
}
}
When using this setup I get no CSP generated at all. Can you please advise where I am going wrong? Thanks!
I have a little inline style applied to the body tag (style="height: 100%;"
) that I need to use a hash for in my CSP.
I have added this to my .json
file used to generate my CSP, but am getting an error when loading the page. This is my .json
file in full:
{
"report-only": false,
"base-uri": {
"self": true
},
"default-src": {
"self": true
},
"connect-src": {
"self": true
},
"font-src": {
"self": true,
"allow": ["https://use.typekit.net"]
},
"form-action": {
"allow": [],
"self": true
},
"frame-ancestors": [],
"frame-src": {
"allow": [],
"self": false
},
"img-src": {
"self": true,
"data": true
},
"media-src": [],
"object-src": [],
"plugin-types": [],
"script-src": {
"allow": [],
"hashes": [],
"self": true,
"unsafe-inline": true,
"unsafe-eval": true
},
"style-src": {
"self": true,
"allow": ["https://use.typekit.net", "https://p.typekit.net"],
"hashes": ["sha256-YTEza4CA2qPCNGLfB6mKa5FjY8kjkO/K7nQxeJxVd9E="]
},
"upgrade-insecure-requests": true,
"worker-src": {
"allow": [],
"self": false
}
}
As you can see I have added the hash to my style-src
. The error is:
Invalid argument supplied for foreach()
On line 882 of /vendor/paragonie/csp-builder/src/CSPBuilder.php
. The line in question looks like this:
foreach ($hash as $algo => $hashval) {
If you have any pointers as to where I might be going wrong it would be greatly appreciated
I'd like to have this plugin enabled during development to not only spot possible breakage through CSP issues in production. But currently setting 'debug' => true
always disables it.
I have two ideas how to solve this:
debug
it could be used to set the default for enable
. But this would change the behavior for people already using the plugin. 😢debug
option configurable just like enabled
as bnomei.securityheaders.debug
so it can have a different value than the global debug
option.As a temporary workaround I use this code/configuration for my dev setup:
return [
'debug' => true,
/**
* Weird workaround to enable the plugin even if debug is true
* Works like this:
* 1. Set enabled to false to skip the plugin instantiating the singleton
* 2. Instantiate it on our own but forcing debug to false and enabled to true
* 3. Profit
*/
'bnomei.securityheaders.enabled' => false,
'hooks' => [
'route:before' => function (): void {
\Bnomei\SecurityHeaders::singleton([
'debug' => false,
'enabled' => true,
])->sendHeaders();
},
],
];
Question:
How to add directives for other domains?
Answer:
you could create a custom snippet based on default one or just override the csp
in your config file. Example for vimeo:
<?php
use Phpcsp\Security\ContentSecurityPolicyHeaderBuilder;
return [
'bnomei.securityheaders.csp' => function() {
$policy = new ContentSecurityPolicyHeaderBuilder();
// root domain
$sourcesetID = kirby()->site()->title()->value();
$policy->defineSourceSet($sourcesetID, [kirby()->site()->url()]);
$directives = [
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_DEFAULT_SRC,
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_STYLE_SRC,
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_SCRIPT_SRC,
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_IMG_SRC,
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_FONT_SRC,
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_CONNECT_SRC,
];
foreach ($directives as $d) {
$policy->addSourceSet($d, $sourcesetID);
}
// vimeo
$sourcesetID = 'vimeo';
$policy->defineSourceSet($sourcesetID, ['player.vimeo.com']);
$directives = [
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_DEFAULT_SRC,
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_STYLE_SRC,
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_SCRIPT_SRC,
ContentSecurityPolicyHeaderBuilder::DIRECTIVE_IMG_SRC,
];
foreach ($directives as $d) {
$policy->addSourceSet($d, $sourcesetID);
}
return $policy;
},
];
Hey there,
when playing around with your plugin, I noticed that $site->nonce()
works for CSS files (via <link>
tag) and JS files (via <script>
tag), but not for inline CSS via <style>
- which is odd ..
My code looks like this:
<?php if (!option('debug')) : ?>
<?php
# Production = Minified inline CSS
$cssPath = $kirby->root('assets') . '/styles/main.min.css';
$css = F::read($cssPath);
?>
<style nonce="<?= $site->nonce() ?>"><?= $css ?></style>
<?php else : ?>
<?php
# Development = Unminified CSS file
$cssPath = '/assets/styles/main.css';
$css = Bnomei\Fingerprint::css($cssPath, [
'nonce' => $site->nonce(),
'integrity' => true,
]);
echo $css;
?>
<?php endif ?>
<?php
$jsFile = option('debug') ? 'main.js' : 'main.min.js';
$jsPath = '/assets/scripts/' . $jsFile;
echo Bnomei\Fingerprint::js($jsPath, [
'nonce' => $site->nonce(),
'defer' => true,
'integrity' => true
]);
?>
The only part failing me is <style nonce="<?= $site->nonce() ?>"><?= $css ?></style>
- which I cannot explain ..
Hello!
I was wondering if the plugin should not be updated since the Features policy changed?
https://scotthelme.co.uk/goodbye-feature-policy-and-hello-permissions-policy/
<?php
snippet('plugin-securityheaders');
?><!DOCTYPE html>
<!-- ... -->
Question:
Which policies should I create?
Answer:
The default values for this plugin are a good start and in most cases you just need to define some additional policies. Just make sure not to weaken the policies by enabling unsafe-inline
etc. Try finding the secure way to do these things.
Hello there
This plugin automatically adds nonce to headers. Is it possible to disable nonce in headers using csp loader?
'nonce-'
An allow-list for specific inline scripts using a cryptographic nonce (number used once). The server must generate a unique nonce value each time it transmits a policy. It is critical to provide an unguessable nonce, as bypassing a resource’s policy is otherwise trivial. See unsafe inline script for an example. Specifying nonce makes a modern browser ignore 'unsafe-inline' which could still be set for older browsers without nonce support.
Looks like 'unsafe-inline' doesn't work when nonce are present in headers. So without disabling nonce in headers 'unsafe-inline' option is not usable.
I know it's not a good idea to enable unsafe-inline, but sometimes inline scripts are generated by third parties.
Thank you
When generating a nonce using the setNonce() method, a random string with the prefix 'nonce-' is generated. Unfortunately this prefix doesn't appear correctly in the CSP. The '-' is missing.
Example:
Nonce in JS attribute: nonce-ZGJhOWRiYzM0YzM3NjZkOTNhNDI2OGFkMjczMDRkNjczYzA3MDhjNA==
Nonce in CSP header: nonce-nonceZGJhOWRiYzM0YzM3NjZkOTNhNDI2OGFkMjczMDRkNjczYzA3MDhjNA==
By just removing the '-' from the prefix in setNonce() the issue is gone.
Hey there,
I know that you struggled with this in #11, but there's an edge case I cannot solve for now:
When using your plugin together with custom-add-fields
, the latter stops working. Since I can't figure out how to fix this, I could circumvent this problem by disabling your plugin on the panel.
I'd be happy to provide a PR with an option like bnomei.securityheaders.enablePanel
or something like this ..
Cheers!
PS: If you're able to spot the problem in @steirico's index.js
with the speed of lightning, feel free to let us know 🦊
increase version to 1.x to avoid strict semantic versioning:
https://getcomposer.org/doc/articles/versions.md#caret-version-range-
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.