Security
Tips en tutorials
WP thema's

Subresource Integrity

Geplaatst op: - Laatste aanpassing:

Subresource Integrity ook wel SRI genoemd is een veiligheidsstandaard van het W3C waarmee webbrowsers kunnen controleren of de bronnen die zij ophalen niet gemanipuleerd zijn. Dit is bijvoorbeeld handig wanneer een website gebruik maakt van een Content Delivery Network (CDN).

In het kort wat is een CDN en wat zijn de voor- en nadelen?

Een CDN kan worden gebruikt om bestanden op te plaatsen zoals scripts en stylesheets welke door meerdere websites gebruikt kunnen worden. Doordat een CDN een netwerk van servers is (vaak geografisch verspreid) levert dit een website snelheidswinst en een besparing van dataverkeer op, website bezoekers kunnen bestanden downloaden vanaf een server bij hen in de buurt in plaats van een server op een ander continent. Dat klikt goed toch? maar helaas zitten hier ook nadelen aan – want stel dat een hacker controle over de CDN weet te krijgen dan zou hij of zij malafide code kunnen injecteren en bestanden kunnen wijzigen met als gevolg dat alle websites die bestanden uit deze CDN gebruiken daar de gevolgen van zullen ondervinden.

SRI to the rescue!

We kunnen dit voorkomen door Subresource Integrity (SRI) te gebruiken in de code van onze website en dat doen we door een integrity attribuut aan onze <script> en <link> elementen toe te voegen. Dit integrity attribuut geven we vervolgens een hash algoritme mee zoals sha-256, sha-384 of sha-512 en encoderen we met base64. Klinkt ingewikkeld? Dat valt wel mee… want gelukkig zijn hier hulpmiddelen voor, daarover zometeen meer.

Hieronder geven we een voorbeeld van een <script> tag waar we integrity op hebben toegepast.

<script 
  src='https://code.jquery.com/jquery-3.5.1.min.js?ver=3.5.1' 
  integrity='sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=' 
  crossorigin='anonymous'>
</script>

Hoe werkt SRI dan?

Wanneer je vanaf je website naar bestanden in een CDN verwijst dan doe je dit meestal naar een specifieke versie. Zo weet je als ontwikkelaar zeker dat deze versie werkt met andere onderdelen van je website en niet door een nieuwe versie kapot gaat.

Zodra een browser een <script> of <link> element tegenkomt met een integrity attribuut, gaat deze eerst kijken of de cryptografische hash overeenkomt met de verwachte inhoud van het bestand. De hash zegt namelijk iets over deze inhoud. Klopt de hash niet? Dan is de inhoud van het bestand niet meer zoals het ooit was, wat natuurlijk vreemd is wanneer je naar een specifieke versie verwijst. Dit kan erop duiden dat een derde partij het bestand heeft gewijzigd. De browser ziet dat de hash niet klopt en zorgt ervoor dat het bestand wordt geblokkeerd.

In de browser console kun je vervolgens een error zien zoals in het voorbeeld hieronder:

Screenshot van SHA-256 subresource integrity error in de Chrome browser console

En wat is het crossorigin attribuut?

Het is normaal gesproken niet toegestaan om bestanden vanaf andere domeinen (oftewel origins) te laden. Dit werkt alleen als het domein waar je het bestand vandaan wilt halen dit expliciet toestaat. Dit wordt ook wel Cross-Origin Resource Sharing (CORS) genoemd.

Met het crossorigin attribuut geef je aan dat je een CORS verzoek gaat maken en hoe het bestand opgehaald kan worden. Het attribuut accepteert de volgende waarden: anonymous, use-credentials en "" (dit is de standaardwaarde en komt op hetzelfde neer als anonymous). Met anonymous worden er geen gegevens uitgewisseld met de server waar je het bestand vandaan wilt halen, tenzij dit hetzelfde domein is. Met use-credentials zal de server die het verzoek ontvangt toegang hebben tot de cookies, client-side SSL certificaten en de Authorization header om het verzoek te valideren.

Hoe maak ik een SRI hash? (hulpmiddelen)

Je kunt de online SRI Hash Generator tool gebruiken om zelf een hash op basis van een URL te genereren. Daarnaast kun je dit ook doen met openssl via de command-line, zie het voorbeeld hieronder:

openssl dgst -sha256 -binary bestandsnaam.js | openssl base64 -A

Hoe pas ik SRI toe in een WordPress website?

In WordPress laden we scripts en stylesheets met de wp_enqueue_script en de wp_enqueue_style functies. Helaas kunnen we via deze functies geen extra attributen toevoegen. Toch kunnen we dit voor elkaar krijgen door hier een eigen functie voor aan te maken.

// Enqueue JS
add_action('wp_enqueue_scripts', function () {
  wp_enqueue_script('jquery', 'https://code.jquery.com/jquery-3.5.1.min.js', [], '3.5.1');
  wp_enqueue_script('jquery-ui', 'https://code.jquery.com/ui/1.13.2/jquery-ui.min.js', ['jquery'], '1.13.2', 1);
}

// Add integrity and crossorigin attributes to CDN scripts.
function theme_script_loader_tag($tag, $handle)
{
  $scripts_to_load = [
    [
      ('name')      => 'jquery',
      ('integrity') => 'sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=',
    ],
    [
      ('name')      => 'jquery-ui',
      ('integrity') => 'sha256-lSjKY0/srUM9BE3dPm+c4fBo1dky2v27Gdjm2uoZaL0=',
    ]
  ];

  $key = array_search($handle, array_column($scripts_to_load, 'name'));
  if (false !== $key) {
    $tag = str_replace('></script>', ' integrity=\'' . $scripts_to_load[$key]['integrity'] . '\' crossorigin=\'anonymous\'></script>', $tag);
  }

  return $tag;
}
add_filter('script_loader_tag', 'theme_script_loader_tag', 10, 2);

In het bovenstaande voorbeeld gebruiken we het script_loader_tag filter van WordPress. Met dit filter kunnen we HTML van alle script tags op de pagina filteren welke door wp_enqueue_scripts zijn geregistreerd. Volgens kijkt de code of de naam van onze $scripts_to_load array overeenkomt met de naam van de geregistreerde scripts en voegt daar de integrity en crossorigin attributen aan toe, met bijbehorende SRI hash.

Deze code stelt ons dus in staat de attributen toe te voegen voor meerdere referenties naar CDN’s of andere externe bronnen die we op de pagina kunnen hebben.

En hoe doe ik dit dan voor stylesheets?

Mocht je dit ook voor stylesheets willen doen dan kun je gebruik maken van het style_loader_tag filter van WordPress.

// Enqueue CSS
add_action('wp_enqueue_scripts', function () {
  wp_enqueue_style('animate-css', 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css', [], '4.2.1');
}

// Add integrity and crossorigin attributes to CDN styles.
function theme_style_loader_tag($tag, $handle)
{
  $styles_to_load = [
    [
      ('name')      => 'animate-css',
      ('integrity') => 'sha256-X7rrn44l1+AUO65h1LGALBbOc5C5bOstSYsNlv9MhT8=',
    ]
  ];

  $key = array_search($handle, array_column($styles_to_load, 'name'));
  if (false !== $key) {
    $tag = str_replace( '/>', ' integrity=\'' . $styles_to_load[$key]['integrity'] . '\' crossorigin=\'anonymous\' />', $html );
  }

  return $tag;
}
add_filter('style_loader_tag', 'theme_style_loader_tag', 10, 2);

Geef een reactie

Reacties zullen eerst worden gemodereerd, we publiceren je e-mailadres niet.