---
source: dbb9f10784e9
---

# Webhookondertekening voor databronnen

Gebruik je een **webhook-databron** (Lumicast ontvangt HTTP-`POST`-verzoeken van jouw systeem in plaats van zelf gegevens op te halen), dan kun je eisen dat elk binnenkomend verzoek **ondertekend** is met een gedeelde geheime sleutel. Niet-ondertekende verzoeken worden geweigerd, zodat niemand die toevallig de webhook-URL kent gegevens naar je schermen kan sturen.

Ondertekening is **optioneel**. Een webhook-databron zonder ondertekeningssleutel blijft niet-ondertekende `POST`s accepteren, precies zoals voorheen. Er verandert niets tot je dit aanzet.

## Wanneer zet je dit aan?

De webhook-URL is een willekeurige identifier, geen geheim. Iedereen die hem in handen krijgt, via een gedeelde Slack-thread, een logbestand of een screenshot, kan willekeurige JSON naar je databron sturen. Voor dashboards waar weinig van afhangt is dat meestal geen probleem. Zet ondertekening aan als:

- de databron iets aanstuurt waar mensen op vertrouwen (zaalreserveringen, wachtrijnummers, veiligheidsmeldingen);
- de afzender een systeem is dat je zelf beheert en kunt aanpassen om verzoeken te ondertekenen;
- je een URL die mogelijk is uitgelekt wilt afschermen zonder de URL zelf te wijzigen.

## Ondertekening inschakelen

1. Ga naar **Data koppelingen**. Klik op **Data koppeling aanmaken** en kies **Webhook** onder **Open data**, of open een bestaande webhook-databron.
2. Zet **Ondertekende verzoeken vereisen** aan.
3. Klik op **Opslaan**.

Bij het opslaan maakt Lumicast een ondertekeningssleutel aan en toont die **één keer** in een pop-up waarmee je hem naar het klembord kopieert. Zet hem in je afzender (bijvoorbeeld in een omgevingsvariabele) voordat je de pop-up sluit: de sleutel in leesbare vorm wordt daarna nooit meer getoond.

::: warning
De sleutel wordt precies één keer getoond. Raak je hem kwijt, dan kun je hem niet meer opvragen. Je moet de sleutel dan rouleren en je afzender bijwerken met de nieuwe.
:::

Zet je de schakelaar uit en sla je op, dan wordt de opgeslagen sleutel gewist en accepteert de webhook weer niet-ondertekende verzoeken.

## De sleutel rouleren

Open de databron, klik naast de webhook-URL op **Ondertekeningssleutel rouleren** en bevestig. Er wordt een nieuwe sleutel gegenereerd en één keer getoond. De vorige sleutel werkt direct niet meer, dus werk je afzender meteen bij: elk verzoek dat met de oude sleutel is ondertekend, wordt geweigerd met `401`.

## Hoe de handtekening werkt

Je afzender berekent een HMAC-SHA256 over een korte string die de body aan een tijdstempel koppelt, en stuurt het resultaat samen met die tijdstempel mee:

| Header | Waarde |
|---|---|
| `X-Lumicast-Signature` | Hex in kleine letters van `HMAC-SHA256(secret, "{timestamp}.{body}")` |
| `X-Lumicast-Timestamp` | Huidige tijd in **milliseconden** sinds de Unix-epoch |

- `{body}` is precies de ruwe body van het verzoek, byte voor byte: dezelfde bytes die je verstuurt. Serialiseer de JSON na het ondertekenen niet opnieuw.
- `{timestamp}` is dezelfde waarde die je in de header zet, als string.
- Verzoeken waarvan de tijdstempel meer dan **5 minuten** afwijkt van de klok van Lumicast, worden geweigerd. Dat beperkt het risico op replay-aanvallen en laat ruimte voor een kleine klokafwijking.

Ontbreekt een van de headers, is de tijdstempel verlopen of klopt de handtekening niet, dan antwoordt Lumicast met `401` en negeert het verzoek.

## Voorbeeldafzenders

Dezelfde uitleg vind je ook in Lumicast zelf: open de databron en klap **Hoe verzoeken te ondertekenen** uit om een codevoorbeeld te kopiëren waarin je webhook-URL al is ingevuld.

### Node.js

```js
import crypto from 'crypto';

const url = 'https://…/hooks/<identifier>';
const secret = process.env.LUMICAST_WEBHOOK_SECRET;
const body = JSON.stringify({ hello: 'world' });
const timestamp = Date.now().toString();

const signature = crypto.createHmac('sha256', secret)
  .update(`${timestamp}.${body}`)
  .digest('hex');

await fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Lumicast-Signature': signature,
    'X-Lumicast-Timestamp': timestamp,
  },
  body,
});
```

### Python

```python
import hmac, hashlib, json, os, time, requests

url = "https://…/hooks/<identifier>"
secret = os.environ["LUMICAST_WEBHOOK_SECRET"]
body = json.dumps({"hello": "world"}, separators=(",", ":"))
timestamp = str(int(time.time() * 1000))

signature = hmac.new(
    secret.encode(),
    f"{timestamp}.{body}".encode(),
    hashlib.sha256,
).hexdigest()

requests.post(
    url,
    data=body,
    headers={
        "Content-Type": "application/json",
        "X-Lumicast-Signature": signature,
        "X-Lumicast-Timestamp": timestamp,
    },
)
```

### curl

```bash
SECRET="${LUMICAST_WEBHOOK_SECRET}"
BODY='{"hello":"world"}'
TS=$(date +%s%3N)   # gebruik zo nodig gdate op macOS
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')

curl -X POST 'https://…/hooks/<identifier>' \
  -H 'Content-Type: application/json' \
  -H "X-Lumicast-Signature: $SIG" \
  -H "X-Lumicast-Timestamp: $TS" \
  --data-raw "$BODY"
```

## Veelgemaakte fouten

- **De body na het ondertekenen opnieuw serialiseren.** Onderteken precies de bytes die je verstuurt. Een JSON-object netjes opmaken of opnieuw naar een string omzetten verandert de bytes en maakt de handtekening ongeldig.
- **Tijdstempel in seconden in plaats van milliseconden.** `Date.now()` in JS en `time.time() * 1000` in Python geven allebei milliseconden. Een tijdstempel in seconden lijkt meteen meer dan 5 minuten oud.
- **Klokafwijking bij de afzender.** Loopt de klok van je afzender meer dan 5 minuten uit de pas, dan mislukt elk verzoek met `401`. Repareer eerst NTP voordat je de ondertekeningscode gaat debuggen.
- **De sleutel in versiebeheer opslaan.** Behandel hem als elk ander wachtwoord: alleen in een omgevingsvariabele of een secrets manager.

## Gerelateerd

- [Databronnen](/nl/features/datasources): overzicht van webhook-databronnen en databronnen die periodiek gegevens ophalen.
- [Webhooks](/nl/features/webhooks): *uitgaande* webhooks (Lumicast → jouw server). Ondertekening werkt daar hetzelfde, maar de sleutel hoort bij de webhook, niet bij de databron.
