Beveiligingsoverwegingen
De JSON:API-module is ontworpen om het datamodel dat in Drupal is gedefinieerd met Drupal’s Entity API, Field API en Typed Data API, via een API die voldoet aan de JSON:API-specificatie te exposen, zodat interactie met de door Drupal beheerde data (entiteiten) wordt gefaciliteerd.
Daarbij respecteert het alle beveiligingsmaatregelen van Drupal voor die data:
- Entity Access wordt gerespecteerd.
- Field Access wordt gerespecteerd.
- Bij het wijzigen van data worden validatie-constraints gerespecteerd.
- De
internal
-vlag wordt gerespecteerd (zie de documentatie over hoe deze kan worden ingesteld op een entiteitstypedefinitie, velddefinitie of eigenschapsdefinitie).
Met andere woorden: JSON:API omzeilt geen van de bestaande beveiligingsmaatregelen en voegt geen eigen laag toe; het hergebruikt de basis van Drupal.
Bugs in entity types, field types en data types kunnen tot beveiligingslekken leiden
Niettemin bestaan er bugs in de code die entiteitstypen, veldtypen en datatypen implementeert, evenals in hun access control handlers en validatie-constraints. Dit is grotendeels toe te schrijven aan Drupal’s legacy: Drupal had oorspronkelijk geen validatie-constraints maar formuliervalidatie-callbacks; de verschuiving naar een API-first-mentaliteit kan in Drupal core als voltooid worden beschouwd, maar is niet gegarandeerd voor contributed of custom modules.
Deze bugs kunnen tot beveiligingskwetsbaarheden leiden; dat is in het verleden zeker gebeurd. Dergelijke kwetsbaarheden zijn niet beperkt tot de JSON:API-module; ze treffen bijvoorbeeld ook de module RESTful Web Services evenals elke PHP-code die met de Entity API interageert.
Aangezien een kwaadwillende gebruiker echter gemakkelijker toegang kan krijgen tot een HTTP-API zoals JSON:API of RESTful Web Services dan tot een PHP-API, is in dit geval extra voorzichtigheid geboden. In tegenstelling tot andere HTTP-API-modules heeft JSON:API out-of-the-box een groter API-oppervlak: alle niet-internal
entiteitstypen worden standaard beschikbaar gemaakt (waarbij natuurlijk nog steeds Entity Access wordt gerespecteerd) om de developer experience zo soepel mogelijk te maken.
Zes beveiligingsoverwegingen
1. Het belang van het gebruiken van stabiele contributed modules
Beveiligingskwetsbaarheden die worden veroorzaakt door entiteitstypen, veldtypen en datatypen worden zo snel mogelijk opgelost voor stabiele modules die op Drupal.org gepubliceerd zijn en die vallen onder het security advisory-beleid. Custom modules en niet-stabiele contributed modules vallen hier niet onder. Als je die gebruikt, wees dan extra voorzichtig.
2. Auditen van Entity & Field Access
Ongeacht of je JSON:API of een andere API-achtige module gebruikt, wordt het altijd aanbevolen om Entity Access & Field Access te auditen op Drupal-sites. Dit is vooral belangrijk als de schrijfmogelijkheden van JSON:API zijn ingeschakeld.
3. Alleen beschikbaar stellen wat je gebruikt
Wanneer specifieke resource types (entiteitstypen + bundles) niet hoeven te worden geëxposed, kun je—nadat je toegang daartoe hebt geweigerd—nog een stap verder gaan en ze uitschakelen. Om een resource type of veld uit te schakelen, is er een PHP-API die je in een custom module kunt implementeren, of je kunt de contribmodule JSON:API Extras, die een UI biedt voor het uitschakelen van resource types en velden, gebruiken. Dit is niet altijd mogelijk, maar in een situatie waarin de site-eigenaar ook alle API-clients beheert, kun je dit doen om het API-oppervlak zo klein mogelijk te maken.
4. Alleen-lezenmodus
Als je voor jouw specifieke behoeften alleen data hoeft te lezen, kun je JSON:API’s read-only-modus inschakelen op /admin/config/services/jsonapi
. Dit beperkt risico’s door hypothetische, nog onbekende bugs in bestaande validatie-constraints en schrijflogica. Omdat de meeste moderne decoupled Drupal-opzetten alleen data hoeven te lezen, staat de alleen-lezenmodus standaard aan. (In Drupal core’s JSON:API en in versie 2.4 en nieuwer van de contributed module.)
5. Security through obscurity: geheim basispad
Het basispad voor JSON:API is standaard /jsonapi
. Dit kan worden gewijzigd in iets als /hidden/b69dhj027ooae/jsonapi
, wat één manier is om de effectiviteit van geautomatiseerde aanvallen te verminderen. Maak sites/example.com/services.yml
aan als het nog niet bestaat en voeg dit toe:
parameters:
jsonapi.base_path: /hidden/b69dhj027ooae/jsonapi
6. Beperk welke entity bundles mogen worden aangemaakt of bewerkt door bepaalde routes te verwijderen
Als je via JSON:API slechts enkele entity bundles wilt kunnen aanmaken of bijwerken, kun je in een custom module een event subscriber implementeren om alle POST- en PATCH-routes te verwijderen behalve een whitelist. Dit heeft effect nadat je de alleen-lezenmodus hebt uitgeschakeld en kan een router-rebuild vereisen.
Voeg een service toe aan het services.yml-bestand van je module:
services:
mymodule.route_subscriber:
class: Drupal\mymodule\Routing\JsonapiLimitingRouteSubscriber
tags:
- { name: event_subscriber }
Maak de event subscriber. Dit voorbeeld maakt het ook onmogelijk om via JSON:API content te verwijderen.
<?php
namespace Drupal\mymodule\Routing;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Klasse JsonapiLimitingRouteSubscriber.
*
* Verwijder alle DELETE-routes van JSON:API-resources om content te beschermen.
*
* Verwijder POST- en PATCH-routes van JSON:API-resources behalve voor die
* welke eindgebruikers via de decoupled API mogen aanmaken en bijwerken.
*/
class JsonapiLimitingRouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection) {
$mutable_types = $this->mutableResourceTypes();
foreach ($collection as $name => $route) {
$defaults = $route->getDefaults();
if (!empty($defaults['_is_jsonapi']) && !empty($defaults['resource_type'])) {
$methods = $route->getMethods();
if (in_array('DELETE', $methods)) {
// We willen nooit data verwijderen, alleen depublishen.
$collection->remove($name);
}
else {
$resource_type = $defaults['resource_type'];
if (empty($mutable_types[$resource_type])) {
if (in_array('POST', $methods) || in_array('PATCH', $methods)) {
$collection->remove($name);
}
}
}
}
}
}
/**
* Haal de mutateerbare resource types op die gebruikers via de API mogen wijzigen.
*
* @return array
* Lijst met mutateerbare JSON:API-resource types als keys.
*/
public function mutableResourceTypes(): array {
return [
'node--article' => TRUE,
'node--document' => TRUE,
'custom_entity--custom_entity' => TRUE,
];
}
}
Beperk toegang tot alle JSON:API-routes met een extra permissie
Wanneer je JSON:API gebruikt voor backend-integraties, beperkte API-clients of andere niet-publieke use-cases, kan het wenselijk zijn om alle JSON:API te beperken tot gebruikers met een specifieke permissie. Voeg in plaats daarvan/aanvullend het volgende fragment toe aan de genoemde route subscriber:
// Beperk toegang tot alle JSON:API-routes met een extra permissie.
foreach ($collection as $route) {
$defaults = $route->getDefaults();
if (!empty($defaults['_is_jsonapi'])) {
$route->setRequirement('_permission', 'FOO custom access jsonapi');
}
}
Definieer die permissie vervolgens in FOO.permissions.yml en ken haar toe aan de gewenste gebruikersrollen.
Artikel uit Drupal-documentatie.