Skip to content

Instantly share code, notes, and snippets.

@etherealite
Created September 12, 2022 01:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save etherealite/9cfab4548596074992055328eed8a701 to your computer and use it in GitHub Desktop.
Save etherealite/9cfab4548596074992055328eed8a701 to your computer and use it in GitHub Desktop.
Wordpress class to enable Content Security Policies
<?php
class WpCsp {
public function register(): void
{
add_filter('script_loader_tag', [$this, 'filter_script_loader_tag'], 3, 50);
add_filter('wp_inline_script_attributes', [$this, 'action_wp_inline_script_attributes']);
add_filter('wp_script_attributes', [$this, 'action_wp_script_attributes']);
add_filter('wp_headers', [$this, 'action_wp_headers'], 1, 50);
}
public function action_wp_headers(): array
{
$nonce = $this->nonce();
$headers['X-Frame-Options'] = 'DENY';
$headers['X-Content-Type-Options'] = 'nosniff';
$headers['Referrer-Policy'] = 'strict-origin-when-cross-origin';
$headers['Strict-Transport-Security'] = 'max-age=63072000; includeSubDomains; preload';
$headers['Access-Control-Allow-Origin'] = WP_SITEURL;
$headers['Content-Security-Policy'] = preg_replace('/\n\s+/', " ", "
default-src 'self';
script-src 'self' 'nonce-$nonce;
style-src 'self' 'nonce-$nonce';
object-src 'none';
base-uri 'self';
connect-src 'self';
font-src 'self';
frame-src 'self';
img-src 'self';
manifest-src 'self';
media-src 'self';
worker-src 'none';
");
$headers['Cross-Origin-Opener-Policy'] = 'same-origin';
$headers['Cross-Origin-Resource-Policy'] = 'same-site';
$headers['Cross-Origin-Embedder-Policy'] = 'require-corp';
$headers['X-Powered-By'] = 'Ponies';
return $headers;
}
public function action_wp_inline_script_attributes(array $attributes): array
{
return $this->addNonceAttribute($attributes);
}
public function action_wp_script_attributes(array $attributes): array
{
return $this->addNonceAttribute($attributes);
}
public function filter_script_loader_tag(string $tag, string $handle, string $src): string
{
$attributes = $this->parseAttributes($tag);
$attributes = $this->addNonceAttribute($attributes);
$pairedStrings = [];
foreach ($attributes as $key => $value) {
$pairs[] = "$key=\"$value\"";
}
$combined = implode(" ", $pairedStrings);
return "<script $combined>";
}
public function parseAttributes(string $tag): array
{
$matches = [];
preg_match_all(
'/(\S+)=["\']?((?:.(?!["\']?\s+(?:\S+)=|\s*\/?[>"\']))+.)["\']?/',
$tag,
$matches
);
$keyValues = [];
foreach ($matches as $match) {
$keyValue[$match[1]] = $match[2];
}
return $keyValues;
}
public function addNonceAttribute(array $attributes): array
{
if (!($attributes['nonce'] ?? false)) {
$attributes['nonce'] = $this->nonce();
}
return $attributes;
}
public function nonce(): string
{
if($this->nonce ?? false) {
return $this->nonce;
}
$this->nonce = base64_encode(random_bytes(20));
return $this->nonce;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment