More from this category
Subresource integrity for WordPress scripts and stylesheets
https://snippets.webaware.com.au/howto/subresource-integrity-wordpress-scripts-stylesheets/ 3 December 2018
More Resources
- https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
- https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
When we load scripts or stylesheets from other websites, we are inherently trusting those sites to deliver something safe. But what if they get hacked? Use subresource integrity to ensure authenticity and protect website visitors.
The problem
We all want our websites to be faster, because our visitors love faster websites. One way we achieve that is by loading commonly used scripts and stylesheets from a central place, a content distribution network or CDN, like Cloudflare’s CDNJS. The benefits include faster loading because those files can come from a server nearer the visitor; and the visitor’s browser might even have a copy of the file already in its cache.
But what happens if the CDN’s copy is damaged, either deliberately or by accident? This is not an academic problem, it has happened and has caused real problems for website owners:
- in February 2018, the website for the Independent Commissioner’s Office in the UK was found to be loading cryptocurrency-mining software on visitors’ browsers
- repeatedly in 2018, some script for stealing credit card details was loaded on many ecommerce websites, including heavyweights such as British Airways and Ticketmaster
Loading locally
A simple solution is to only load scripts and stylesheets from our local resource, the website itself. We have full control of that, and with good attention to security and code management, this should not be a problem. But remember that we’re using CDN-hosted resources for performance benefits, so there’s a trade-off we must accept if we’re going to do that. We can claw back that lost performance through a CDN… which is sounding eerily familiar!
Subresource Integrity
Modern browsers now have a feature for ensuring the integrity of scripts and stylesheets loaded in the browser. It’s called Subresource Integrity or SRI, and it works by comparing a cryptographic hash of a file’s contents with the expected hash in our web page. Here’s what it looks like for the classic Font Awesome v4.7.0 stylesheet:
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0="
crossorigin="anonymous" />
When a browser loads the file from CDNJS, it calculates the cryptographic hash and compares it to the expected hash in the integrity attribute of the link element. If the hashes don’t match, the browser won’t use that file.
NB: this works well for versioned resources, i.e. when you can request a specific, non-changing version of a resource. That’s true for many scripts and stylesheets, particularly ones that do things in your browser. It’s often not the case for services, which is well explained by Troy Hunt in his article, The JavaScript Supply Chain Paradox: SRI, CSP and Trust in Third Party Libraries.
Using SRI in WordPress
As at WordPress 5.0, there’s no native support for SRI. There’s an open ticket for adding SRI into the script loading framework in WordPress, but it could be a little while before it gets formalised and added into the core code.
In the meantime, here’s how to add SRI to specific external resources, the stylesheet for Font Awesome 4.7.0 and the script for Twemoji:
load custom CSS / JS required for fonts
add_action('wp_enqueue_scripts', function() {
wp_enqueue_style('font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css', [], null);
add_filter('style_loader_tag', NAMESPACE . 'add_font_awesome_sri', 10, 2);
wp_enqueue_script('twemoji', 'https://cdnjs.cloudflare.com/ajax/libs/twemoji/11.2.0/2/twemoji.min.js', [], null, true);
add_filter('script_loader_tag', NAMESPACE . 'add_twemoji_sri', 10, 2);
add_action('wp_print_footer_scripts', NAMESPACE . 'twemoji_script');
});
/**
- add SRI attributes to Font Awesome style loader element
- @param string $html
- @param string $handle
-
@return string */ function add_font_awesome_sri($html, $handle) { if ($handle === 'font-awesome') { $html = str_replace(' />', ' integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />', $html); } return $html; }
/**
- add SRI attributes to Twemoji script loader element
- @param string $html
- @param string $handle
-
@return string */ function add_twemoji_sri($html, $handle) { if ($handle === 'twemoji') { $html = str_replace('>', ' integrity="sha256-AsQ6AskD2N30tG+vyEyJzpGgXIOQ0Z5AAFE7l+GmZ5s=" crossorigin="anonymous">', $html); } return $html; }
/**
- add script to initialise twemoji (Twitter Emoji)
*/function twemoji_script() { echo ''; }
/**
-
Important! if you copy this code, please go to the source sites for the integrity hashes!
*/
PLUGIN FOR SRI SCRIPT
If we want something a little more automatic, there’s even a WordPress plugin that adds SRI to your scripts for you.
Where do we find the integrity hash?
Good CDNs provide us with the hash when we get the URL for the file. For example, CDNJS has a drop-down list next to each file, from which we can choose to copy the integrity hash:
Copy the SRI hash for a file on CDNJS[/caption] Copy the SRI hash for a file on CDNJS
Font Awesome provides the integrity hash as well, when we use their CDN.
So now we can use a CDN-hosted script or stylesheet, and still ensure the integrity of the file and the safety of our website visitors.
What about Google web fonts?
Well… it’s messy. Google does a bunch of browser sniffing to determine the most efficient way to deliver fonts to each browser. In other words, you can get very different resource content sent to different browsers, which is exactly what SRI is trying to stop! So no SRI for Google web fonts, at least not for now.
What about IE11?
Internet Explorer doesn’t support SRI. :sad trombone:
But let’s not let that stop us from making the Internet safer for people who update their web browsers.
Author: Ross McKay
Tags: css, javascript, wordpress Posted in How-to– – – – – –
Use Subresource Integrity – ARTICLE 2
https://hacks.mozilla.org/2015/09/subresource-integrity-in-firefox-43/ Mozilla Firefox Developer Edition 43 and other modern browsers help websites to control third-party JavaScript loads and prevent unexpected or malicious modifications. Using a new specification called Subresource Integrity, a website can include JavaScript that will stop working if it has been modified. With this technology, developers can benefit from the performance gains of using Content Delivery Networks (CDNs) without having to fear that a third-party compromise can harm their website.
Using Subresource Integrity is rather simple:
<script src="https://code.jquery.com/jquery-2.1.4.min.js"
integrity="sha384-R4/ztc4ZlRqWjqIuvf6RX5yb/v90qNGx6fS48N0tRxiGkqveZETq72KgDVJCp2TC"
crossorigin="anonymous">
The idea is to include the script along with its cryptographic hash (e.g. SHA-384) when creating the web page. The browser can then download the script and compute the hash over the downloaded file. The script will only be executed if both hashes match. The security properties of a collision resistant hash function, ensure that a modification results in a very different hash. This helps the site owner detect and prevent any changes, whether they come from a compromised CDN or an evil administrator.
An important side note is that for Subresource Integrity to work, the CDN must support Cross-Origin Resource Sharing (CORS). The
crossorigin
attribute in the above code snippet enforces a CORS-enabled load. The anonymous value means that the browser should omit any cookies or authentication that the user may have associated with the domain. This prevents cross-origin data leaks, and also makes the request smaller.
Integrity syntax
As you may have noticed, the integrity attribute does not just include the hash value. It also contains the digest name. The syntax for the integrity attribute allows multiple tokens of this name-value format. This allows site owners to specify hashes of different strengths as well as the values of multiple scripts that may be behind a URL. This is useful for browser sniffing or content negotiation.
<script src="https://code.jquery.com/jquery-2.1.4.min.js"
integrity="sha384-R4/ztc4ZlRqWjqIuvf6RX5yb/v90qNGx6fS48N0tRxiGkqveZETq72KgDVJCp2TC
sha256-8WqyJLuWKRBVhxXIL1jBDD7SDxU936oZkCnxQbWwJVw="
crossorigin="anonymous">
Failover
For the best performance, users would load all resources from the CDN, but if integrity cannot be verified, you don’t want your users to be trapped on a non-working web page. To make failover work, we recommend hosting a copy of the script on your own origin. To recover from failure one could then extend the previous snippet with the following code:
This code will check if jQuery has been defined and could otherwise insert a script tag that loads the same origin version of the script.
Please note that many scripts update regularly, especially if they do not come with a version number. If you want to secure your CDN-loaded scripts, it is best to stick to a specific version and not use filenames with the word ‘latest’ in them.
HTTP or HTTPS?
Subresource Integrity works on both HTTP and HTTPS. If you are serving your page over plain HTTP, the browser can still figure out if the script was modified on the CDN, but it is not protected against active network attackers, as they would be able to just remove the integrity attribute from your HTML. It is, however, in the interest of your users to provide confidentiality, integrity, and authenticity of your web applications by using HTTPS for the entirety of your website.
Stylesheet support
While we are working on adding support for subresources other than scripts, you can also use Subresource Integrity for CSS. Just use the integrity attribute that you now know so well on your tag!
Try Subresource Integrity Now!
If you want to test browser support or toy with examples, take a look at https://srihash.org/, which can do all the grunt work of computing hashes as well as checking if your CDN already supports HTTPS. A few early adopters like BootstrapCDN, CloudFlare and GitHub are already experimenting with it.
There is some additional documentation of Subresource Integrity on MDN. But if you want to read all the fine details of Subresource Integrity, take a look at the specification. To conclude, Subresource Integrity can make your website safer when using a CDN that you do not fully control. It’s as simple as adding just a few extra attributes to your script tags.
https://wordpress.stackexchange.com/questions/317035/how-to-add-crossorigin-and-integrity-to-wp-register-style-font-awesome-5 # WORDPRESS: How to add crossorigin and integrity to wp_register_style? (Font Awesome 5)
I am upgrading Font Awesome 4 to version 5 which introduces both integrity and crossorigin attributes to the
markup.
Currently, I am using:
wp_register_style('FontAwesome', 'https://example.com/font-awesome.min.css', array(), null, 'all' );
wp_enqueue_style('FontAwesome');
Which outputs as:
With Font Awesome 5 it introduces two new attributes and values (
integrity
and
crossorigin
) e.g:
Possible duplicate of How to add extra attribute to stylesheet link?
style_loader_tag
style_loader_tag is an official WordPress API, see the documentation: https://developer.wordpress.org/reference/hooks/style_loader_tag/
apply_filters( 'style_loader_tag', $html, $handle, $href, $media )
Filters the HTML link tag of an enqueued style.
First enqueue your stylesheet, see documentation: https://developer.wordpress.org/reference/functions/wp_enqueue_style/
wp_enqueue_style( 'font-awesome-5', 'https://use.fontawesome.com/releases/v5.5.0/css/all.css', array(), null );
The
$handle
is
'font-awesome-5'
I do
null
so that there is no version number. This way it will be cached.
Simple str_replace A simple str_replace is enough to achieve what you want, see example below
function add_font_awesome_5_cdn_attributes( $html, $handle ) {
if ( 'font-awesome-5' === $handle ) {
return str_replace( "media='all'", "media='all' integrity='sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU' crossorigin='anonymous'", $html );
}
return $html;
}
add_filter( 'style_loader_tag', 'add_font_awesome_5_cdn_attributes', 10, 2 );
Add different atributes
Below an example to add different atributes to (multiple) different stylesheets
function add_style_attributes( $html, $handle ) {
if ( 'font-awesome-5' === $handle ) { return str_replace( "media='all'", "media='all' integrity='sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU' crossorigin='anonymous'", $html ); } if ( 'another-style' === $handle ) { return str_replace( "media='all'", "media='all' integrity='blajbsf' example", $html ); } if ( 'bootstrap-css' === $handle ) { return str_replace( "media='all'", "media='all' integrity='something-different' crossorigin='anonymous'", $html ); } return $html;
}
add_filter( 'style_loader_tag', 'add_style_attributes', 10, 2 );
Add attributes to all styles
Below an example to add the same atributes to more than one stylesheet
function add_attributes_to_all_styles( $html, $handle ) {
// add style handles to the array below $styles = array( 'font-awesome-5', 'another-style', 'bootstrap-css' ); foreach ( $styles as $style ) { if ( $style === $handle ) { return str_replace( "media='all'", "media='all' integrity='sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU' crossorigin='anonymous'", $html ); } } return $html;
}
add_filter( 'style_loader_tag', 'add_attributes_to_all_styles', 10, 2 );script_loader_tag
I also would like to explain the script_loader_tag, because this is also handy, but this API works in combination with wp_enqueue_script. The script_loader_tag API is almost the same, only some small differences, see documentation: https://developer.wordpress.org/reference/hooks/script_loader_tag/
apply_filters( 'script_loader_tag', $tag, $handle, $src )
Filters the HTML script tag of an enqueued script.
Below an example to defer a single script
function add_defer_jquery( $tag, $handle ) { if ( 'jquery' === $handle ) { return str_replace( "src", "defer src", $tag ); } return $tag; } add_filter( 'script_loader_tag', 'add_defer_jquery', 10, 2 );
Below an example to defer more than one script
function add_defer_attribute( $tag, $handle ) {
// add script handles to the array below $scripts_to_defer = array( 'jquery', 'jquery-migrate', 'bootstrap-bundle-js' ); foreach ( $scripts_to_defer as $defer_script ) { if ( $defer_script === $handle ) { return str_replace( "src", "defer src", $tag ); } } return $tag;
}
add_filter( 'script_loader_tag', 'add_defer_attribute', 10, 2 );So I have explained both API’s
style_loader_tag
and
script_loader_tag
. And I gave some examples for both API’s I hope that this is useful for a lot of people out there. I have experience with both API’s.
script loader – example 2
https://stackoverflow.com/questions/44827134/wordpress-script-with-integrity-and-crossorigin You can use the script_loader_tag hook (the main part is actually not my code, but I honestly don’t remember where I got it, probably somewhere here on SO or WP Stack Exchange):
add_filter( 'script_loader_tag', 'add_attribs_to_scripts', 10, 3 ); function add_attribs_to_scripts( $tag, $handle, $src ) {
// The handles of the enqueued scripts we want to defer $async_scripts = array( 'jquery-migrate', 'sharethis', );
$defer_scripts = array( 'contact-form-7', 'jquery-form', 'wpdm-bootstrap', 'frontjs', 'jquery-choosen', 'fancybox', 'jquery-colorbox',
'search' );$jquery = array( 'jquery' );
if ( in_array( $handle, $defer_scripts ) ) { return '' . "n"; } if ( in_array( $handle, $async_scripts ) ) { return '' . "n"; } if ( in_array( $handle, $jquery ) ) { return '' . "n"; } return $tag; }
UPDATE
@CKMacLeod Thank you for your update, this clarifies things. We’re mostly on the same page. As I said, I’m a WordPress developer and if you want to publish a theme and/or plugin on https://wordpress.org you’re essentially forced to abide by the WordPress Coding Standards or they will simply reject your theme and/or plugin. That’s why I encourage developers out there to use the WordPress way. I understand that sometimes there are no differences whatsoever, but it’s also convenience. As you said yourself you could download Font Awesome and include it in your theme and/or plugin, this way you would only need to remove the style_loader_tag function and modify your wp_enqueue_style function. And one last thing, in the past (5 years ago) some caching, combining and minifying plugins didn’t work and most of the times I would find out why those developers who made the theme simply put things in the head in the theme and/or echoed them. Most caching plugins, who also (most of the time) provide options to combine, minify and delay execution of scripts became smarter and better at detecting bad code and working around them. But this is not preferred. That’s why I encourage people to code with standards/conventions in mind. As a developer, you always need to take into consideration what people could do with your code and taking compatibility in mind. So not taking the easy solution but the best optimal solution. I hope I have clarified my viewpoint. EDIT
@CKMacLeod Thank you for the (technical) debate, I hope that you realize how important this is (using WordPress API’s in your own development). Again, I have been looking around and even now if I look at the FAQ’s of most popular minify plugins I usually see this one way or the other, for example: > Question: Why are some of the CSS and JS files not being merged?
Answer: The plugin only processes JS and CSS files enqueued using the official WordPress API method – https://developer.wordpress.org/themes/basics/including-css-javascript/ -as well as files from the same domain (unless specified on the settings).
See FAQ: https://wordpress.org/plugins/fast-velocity-minify/