Wordt u aangevallen? Vraag hulp bij incidentrespons

[email protected]
Webbeveiliging

Test elk ID in uw app op IDOR en BOLA

Vind endpoints die records van andere gebruikers teruggeven bij een gewijzigd ID, herstel ze met scoped lookups en weet wat u in code review checkt.

Ook beschikbaar in:EnglishTürkçeDeutschFrançaisEspañolItalianoPolskiEesti

Illustratie: Test elk ID in uw app op IDOR en BOLA

Een insecure direct object reference (IDOR) bestaat overal waar uw applicatie een identifier van de client accepteert en het bijbehorende record teruggeeft of wijzigt, zonder te vragen of deze gebruiker er wel aan mag komen. In de API-wereld heet dezelfde fout broken object-level authorisation (BOLA), en die staat bovenaan de OWASP API Security-lijst voor 2023. Voorkomen vraagt om een autorisatiecheck bij elke toegang tot een object, toegepast in de hele applicatie en afgedekt door tests.

Waaruit een IDOR bestaat en waar hij zich verstopt

De OWASP IDOR cheat sheet verdeelt een IDOR in drie onderdelen: een object (een account, document, ticket, transactie), een verwijzing ernaar (een ID, UUID, rekeningnummer, token of slug) en een ontbrekende check op objectniveau. Dat derde onderdeel is de fout.

De verwijzing is niet altijd /users/123 in de URL. Ze kan in een verborgen formulierveld zoals user_id zitten, in een query string, in een request header of in een JSON-payload. Het kan ook een bestandsnaam zijn: als GET /documents/annual-report.pdf werkt, probeer dan een andere geldige documentnaam.

De OWASP API Security-pagina over BOLA beoordeelt de uitbuitbaarheid als eenvoudig, de prevalentie als wijdverbreid en de detecteerbaarheid als eenvoudig. API-servers houden meestal geen clientstatus bij, dus vertrouwen ze op door de client verstuurde ID's om te bepalen welk object ze laden. De voorbeeldscenario's:

  • Een shop-analytics-endpoint van de vorm /shops/{shopName}/revenue_data.json, gecombineerd met een tweede endpoint dat alle winkelnamen opsomt, stelt de verkoopgegevens van duizenden winkels bloot aan een eenvoudig script.
  • Een voertuig-API die een VIN accepteert maar nooit controleert of het voertuig bij de ingelogde gebruiker hoort.
  • Een GraphQL-mutation deleteReports die elk document-ID verwijdert dat ze ontvangt.

Het laatste geval is een delete, dus test updates en deletes net zo zorgvuldig als de pagina die het record toont.

Test met twee accounts

Maak User A en User B aan, geef elk eigen objecten (documenten, tickets, facturen), authenticeer als A en speel requests opnieuw af met de identifiers van B.

  1. Maak twee accounts aan met verschillende autorisatiescopes.
  2. Maak objecten aan die door elke gebruiker worden beheerd.
  3. Log in als User A en leg elk request vast dat een objectverwijzing bevat.
  4. Vervang die verwijzingen door die van User B in het pad, de query, de body en de headers.
  5. Controleer dat de applicatie de toegang elke keer weigert.

Dek lezen, aanmaken, wijzigen, verwijderen, exporteren en beheeracties af. Sla een endpoint niet over omdat de identifiers willekeurig lijken. Volgens de cheat sheet moet toegang geweigerd worden, of de identifier nu voorspelbaar is of onraadbaar.

Maak er een regressietest van

Zodra de handmatige ronde werkt, zet u hem om in code. Een schets in pytest, uitgaande van fixtures die u een ingelogde sessie voor User A geven en het ID van een document van B. Elk request moet verder geldig zijn, met een correct opgebouwde body, zodat een 400 of 415 vanuit inputvalidatie de test niet om de verkeerde reden laat slagen:

import os
import pytest

BASE_URL = os.environ["APP_BASE_URL"]  # from your test config
DENIED = 404  # your chosen response for foreign IDs

ENDPOINTS = [
    ("GET", "/documents/{id}", None),
    ("PUT", "/documents/{id}", {"title": "changed"}),
    ("DELETE", "/documents/{id}", None),
    ("GET", "/documents/{id}/export", None),
    ("POST", "/documents/{id}/comments", {"text": "hello"}),
]

@pytest.mark.parametrize("method,path,body", ENDPOINTS)
def test_a_cannot_touch_bs_document(method, path, body, session_a, b_document_id):
    url = BASE_URL + path.format(id=b_document_id)
    r = session_a.request(method, url, json=body)
    assert r.status_code == DENIED

Kies bewust tussen 403 en 404. Een 403 bij een vreemd ID bevestigt dat het ID bestaat, wat een aanvaller helpt bij enumeratie. Welke u ook kiest, controleer op die ene code. Genereer de endpointlijst uit uw routetabel zodat nieuwe endpoints automatisch verschijnen, en blokkeer deployments wanneer de suite faalt.

GraphQL vraagt om twee extra gevallen. Vraag een vreemd ID op op elk niveau van de boom, om te zien of geneste resolvers de check van de parent herhalen. Stuur bij batchoperaties zoals de reportKeys-array hierboven een request met een mix van uw eigen ID's en die van een ander, en controleer dat de hele aanroep faalt of dat het vreemde item wordt overgeslagen.

Beperk de lookup tot de gebruiker

Controleer de autorisatie voor elk object waar een gebruiker toegang toe probeert te krijgen, met het mechanisme dat uw framework biedt, zodat de check structureel is en niet iets dat elke ontwikkelaar moet onthouden. Het meest directe patroon is objecten opzoeken binnen de verzameling die de huidige gebruiker mag bereiken. De Rails-variant:

# vulnerable, searches all projects
@project = Project.find(params[:id])

# secure, searches projects related to the current user
@project = @current_user.projects.find(params[:id])

De Spring Boot-handler in de cheat sheet roept findById(id) aan en geeft daardoor elk document terug aan iedereen die een ID kan raden of bemachtigen. Het onderstaande fragment is ons eigen voorbeeld, niet de code van OWASP. Het gaat uit van een Spring Data derived query op een ownerId-veld, en currentUser() staat voor uw helper die de vertrouwde authenticatiecontext uitleest:

@GetMapping("/documents/{id}")
public Document getDocument(@PathVariable Long id) {
    return documentRepository
        .findByIdAndOwnerId(id, currentUser().getId())
        .orElseThrow();
}

Vermijd waar mogelijk het blootstellen van identifiers en leid de gebruiker af uit de sessie. Bewaar bij meerstapsflows de identifiers in de sessie, zodat de client er tussen de stappen niet mee kan knoeien.

Willekeurige identifiers helpen, maar alleen als defense in depth. Voeg een kolom met een willekeurige string toe en gebruik die in URL's, of gebruik UUID's als primaire sleutel. Versleutel ID's niet om ze te verbergen; dat veilig doen is lastig.

Wat thuishoort in code review

Lees elke handler die een door de client aangeleverd ID verwerkt en zoek waar de permissiecheck plaatsvindt. Kijk eerst of de query draait op een aan de gebruiker gekoppelde verzameling, of een globale find aanroept en pas daarna controleert, als dat al gebeurt. Controleer vervolgens of de check bestaat in elke functie die via clientinput bij een record komt, inclusief update, delete en export.

Let op een handler die alleen het gebruikers-ID uit de sessie (bijvoorbeeld uit de JWT gehaald, die u kunt inspecteren met onze JWT-tool) vergelijkt met de ID-parameter. Volgens OWASP dekt dat slechts een klein deel van de gevallen. Autorisatie moet het beleid en de hiërarchie van gebruikers volgen. In een multi-tenant systeem kan een admin in tenant X een factuur opvragen die bij tenant Y hoort. De gebruiker is geauthenticeerd, de rol is admin en het ID is geldig, dus een eenvoudige rolcheck kan slagen terwijl het object in de verkeerde tenant staat.

Houd bij triage twee fouten uit elkaar. Bij BOLA mag de gebruiker het endpoint van nature aanroepen, en is het gemanipuleerde ID de overtreding. Als een gebruiker bij een endpoint komt waar hij helemaal niet bij hoort, is dat broken function-level authorisation, met een andere oplossing.

Waar u kunt beginnen

  1. Inventariseer elk endpoint, formulier en elke GraphQL-operatie die een identifier accepteert, inclusief bestandsnamen en verborgen velden. Begin bij uw OpenAPI-spec of routetabel en voeg toe wat alleen in de proxygeschiedenis verschijnt terwijl u door de applicatie klikt.
  2. Voer de tweegebruikersronde hierboven eerst uit op de tien gevoeligste objecten, zoals facturen, persoonsgegevens en beheeracties, en leg elke respons vast die geen weigering is.
  3. Vervang globale lookups door lookups die tot de gebruiker beperkt zijn, met het autorisatiemechanisme van uw framework.
  4. Neem de geparametriseerde cross-usertests op in uw pipeline en laat een falende test de release blokkeren.
  5. Voeg de reviewvragen toe aan uw pull-requesttemplate. Voor een externe controle van wat uw tests hebben gemist, kunt u onze penetratietests laten uitvoeren.

Hoe wij kunnen helpen

Penetratietesten

Wij vallen uw netwerken, applicaties en medewerkers aan zoals een echte tegenstander dat zou doen, en laten zien hoe u elk gat dicht.

JWT-decoder

Decodeer een JSON Web Token en controleer het op bekende beveiligingsfouten. Het token blijft in uw browser.

Neem contact op
AngelForce-team

Geschreven door het AngelForce-team: securityconsultants met CEH-, LPT Master- en CISA-certificering die systemen van organisaties in heel Europa en Turkije testen, bewaken en beschermen.

Geschreven met hulp van AI op basis van de openbare bronnen hieronder, en vóór publicatie gecontroleerd door het team van AngelForce.

Bronnen

  1. cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html
  2. api-security.owasp.org/editions/2023/en/0xa1-broken-object-level-authorization/

Artikelen over Webbeveiliging

Vertel ons wat u wilt beschermen

Beschrijf uw omgeving en wat u wilt bereiken. Wij antwoorden per e-mail.

Neem contact op
[email protected]

AngelForce OÜ · Tallinn, Estland