De helpdesk #7: Importeren metingen uit basisbeveiliging, via JSON naar je SIEM

Diverse grote organisaties importeren dagelijks hun meetresultaten van basisbeveiliging naar hun SIEM systeem. Dit is makkelijk omdat de metingen als JSON worden gepubliceerd. In dit artikel lees je hoe je deze metingen zelf kan importeren in jouw SIEM. De meeste organisaties geven aan dat integratie ongeveer een half uur duurt. Daarna kan het mee in de bestaande veiligheidsprocessen.

Wanneer je de metingen structureel gebruikt vragen we om deelnemer te worden van de stichting, dat zorgt ervoor dat deze informatie beschikbaar en up to date blijft. Ook draagt dat bij aan nieuwe metingen, doelgroepen en onderzoeken.

Metingen zijn ook in excel te downloaden, dit is een foto van een spreadsheet
Spreadsheets zijn te downloaden in het rapport. Dit artikel gaat een stap verder.

Wie zoekt naar een spreadsheet van het rapport: basisbeveiliging biedt twee soorten spreadsheets bij ieder rapport. De Excel spreadsheet met alle metingen is vanaf begin augustus 2024 te vinden op de rapportpagina. Het CSV overzicht van metingen is te vinden in het tabblad “samenvatting”. Zie de rapportpagina, bijvoorbeeld hier. Wie op zoek is naar lijsten met domeinen: deze worden via datasets aangeboden in diverse formaten.

Dit artikel is bedoeld voor engineers. Junior engineers kunnen makkelijk aan de slag met de gegevens van basisbeveiliging. Het hangt van het SIEM af hoe makkelijk het importeren is.

Structuur van de data

Alle rapporten van basisbeveiliging zijn beschikbaar in JSON. Dit is een bescheiden dataformaat dat leesbaar is door zowel computers als mensen. We richten ons op rapporten van organisaties, dit is een stabiele API van basisbeveiliging. Andere data van basisbeveiliging is via dezelfde weg te benaderen die straks wordt uitgelegd, maar hierop zit geen garantie dat dit blijft werken.

In een rapport zijn gegevens op de volgende manier gestructureerd. Alle metingen zijn met een paar stappen te achterhalen. Metingen zitten op twee punten: bij Url en bij Endpoint. Deze zijn te herkennen aan het woord Rating. In het meetbeleid staan de technische namen en de context van alle metingen.

Organisatie <- Url (+ Rating) <- Endpoint (+ Rating)

We verwachten dat ergens in 2025 de structuur licht wordt aangepast, waarbij alle metingen (Rating) op naam te vinden zijn in plaats van in een naamloze lijst. Dit staat ook al genoemd in de broncode hieronder. De wijziging zal een kwartiertje werk opleveren t.z.t. Dit wordt ook aangekondigd in een update of helpdesk artikel.

Rapport downloaden en verwerken

Om het juiste rapport op te halen is een organisatienummer en kaart nodig. Deze is te vinden op de site van basisbeveiliging. Zoek naar je organisatie, bijvoorbeeld de Internet Cleanup Foundation. Deze heeft nummer 1337. Dit zie je in de titelbalk staan, en anders bij de eigenschappen van de organisatie in het rapport (binnenkort). Bijvoorbeeld hier.

Het is mogelijk om het rapport van een specifieke datum op te halen. Dit kan door een ISO8601 datum mee te geven met het verzoek. Simpel gezegd gaan we het bestand downloaden op het volgende adres: https://basisbeveiliging.nl/data/report/NL/cyber/1337/2024-08-09/. Het nieuwste rapport is op te halen door de datum te veranderen in een 0. Let op dat je hierbij doorverwijzingen moet volgen naar dezelfde host, zoals in de voorbeeldcode hieronder.

De voorbeeldcode haalt een rapport op en toont alle metingen. Dit is geschreven in python en maakt gebruik van niquests (dit is geen spelfout van requests, maar een update) om metingen te downloaden. Let op de stukken commentaar in de code: dit zijn hints waardoor je nog meer informatie kan importeren.

Om deze code te laten werken is python met niquests nodig. Voor het gebruiksgemak installeren we ook ipython: dit kan de voorbeeldcode uitvoeren zonder gedoe met regels en inspringen. Voer na het installeren van python de volgende code uit:

pip install niquests ipython
ipython

In ipython is de volgende code te plakken. Onder de code staat een deel van de output hiervan. Door de naam van de kaart en het nummer van de organisatie aan te passen is een ander rapport te downloaden.

# Example metric processing script by Internet Cleanup Foundation.
# Geometry (c) Open Street Map contributors, CC-BY-SA 4.0.
# Metrics from Qualys SSL Labs is provided under their license, which requires publication and attribution.
# Location metric data licensed from Maxmind and Ripe, not allowed for commercial use / republication.

import niquests

WSM_INSTALLATION = "https://basisbeveiliging.nl/data"


def get_report_data(organization_id: int, country_code: str = "NL", layer_name: str = "cyber", at_when: str = "0"):
    return niquests.get(f"{WSM_INSTALLATION}/report/{country_code}/{layer_name}/{organization_id}/{at_when}/", allow_redirects=True).json()


def process_metrics(report_data) -> None:
    # Metrics contain the following attributes, which might be useful:
    # scan, comply or explain, evidence, meaning, impact and more.
    for url in report_data["calculation"]["organization"]["urls"]:
        for url_rating in url["ratings"]:
            # Iteration will change in a future version, which will have a key per rating.
            # Note that metrics can be explained. They are technically correct, but have no security impact.
            print(f"url: {url['url']}:  {url_rating['type']}: {url_rating['type']}: {impact(url_rating)}")
            import_metric(url, url_rating)
        for ep in url["endpoints"]:
            for ep_r in ep["ratings"]:
                result = f"{impact(ep_r)} {explained(ep_r)}"
                print(f"- IPv{ep['ip_version']}/{ep['protocol']}:{ep['port']} {ep_r['type']}: {result}")
                import_metric(url, ep_r)


def import_metric(url, metric):
    # sample method... pass whatever data you need to import the metric...
    ...


def import_report(country_code: str = "NL", layer_name: str = "cyber", organization_id: int = 1337, at_when: str = "0"):
    process_metrics(get_report_data(organization_id, country_code, layer_name, at_when))


def impact(metric: dict) -> str:
    severities = ["high", "medium", "low", "not_applicable", "not_testable", "error_in_test", "ok"]
    return next((severity for severity in severities if metric[severity]), "")


def explained(metric: dict) -> str:
    return "(explained)" if metric["is_explained"] else ""


if __name__ == "__main__":
    import_report("NL", "cyber", 1337)

Dit is een stuk voorbeeld wat eruit komt:

url: matomo.internetcleanup.foundation:  location_server: location_server: ok
- IPv6/http:80 http_security_header_x_content_type_options: low
- IPv6/http:80 http_security_header_strict_transport_security: ok
- IPv6/http:80 http_security_header_x_frame_options: ok
- IPv4/https:443 http_security_header_x_content_type_options: low
- IPv4/https:443 tls_qualys_encryption_quality: ok
- IPv4/https:443 http_security_header_strict_transport_security: ok
- IPv4/https:443 bannergrab: ok
- IPv4/https:443 web_privacy_cookie_products_no_consent: ok
- IPv4/https:443 web_privacy_tracking: ok
- IPv4/https:443 web_privacy_third_party_requests: ok
- IPv4/https:443 http_security_header_x_frame_options: ok
...

Meer over basisbeveiliging

Basisbeveiliging.nl is een initiatief van de Internet Cleanup Foundation. Wij zetten ons in voor een veilig online Nederland door middel van onderzoek en open data. Organisaties die ons werk waardevol vinden en hier gebruik van maken vragen we op enige vorm bij te dragen, bijvoorbeeld door de stichting te ondersteunen met deelnemerschap.