Kliniek A en Kliniek B delen één codebase, twee subdomeinen en een mix van bezoekers van kantoor en via VPN. Netwerkteams hebben de neiging de twee ingangen apart te configureren, en dat is prima, zolang de applicatie dat niet doet. De controles hieronder horen in de applicatie en moeten zich identiek gedragen, ongeacht welke deur het verzoek gebruikte.

Eerst: de hostnaam benoemt alleen een tenant

Los de gevraagde hostnaam op tegen een mapping die de beheerder onderhoudt, en weiger alles wat onbekend is. Gebruik de gevalideerde hostwaarde van het framework in plaats van de ruwe header, zodat een vervaardigde Host geen opzoeksleutel kan injecteren. Een geslaagde match vertelt de code welke tenantconfiguratie en welke branding geladen moeten worden. Het zegt niets over de vraag of de aanroeper de gegevens van die tenant mag zien. Houd die tweede vraag in een aparte functie met een aparte invoer.

Een gedeelde hulpfunctie die zowel context als toestemming uit één hostnaamopzoeking teruggeeft, is het begin van de bug.

Ten tweede: lidmaatschap komt uit de sessie

Zoek de actieve lidmaatschappen van de geauthenticeerde gebruiker op en vergelijk ze met de tenant uit de eerste controle. Een gebruiker die bij beide klinieken hoort, mag wisselen, maar de wissel moet een expliciete handeling zijn die in de verzoekcontext wordt vastgelegd, geen afleiding uit de URL. Een gebruiker die bij één kliniek hoort en de andere opvraagt, wordt geweigerd, ook voor downloads en API-aanroepen.

Test het negatieve geval door een recordidentificatie en een tenantparameter te wijzigen. Als een van beide wijzigingen gegevens teruggeeft, wordt de scope uit het verzoek gelezen en niet uit het geverifieerde lidmaatschap.

Cookiebereik en eenmalige aanmelding bepalen waar een account kan worden gepresenteerd. Ze bepalen niet wat dat account mag lezen.

Ten derde: het netwerk bewijst niets over de persoon

Een VPN-gateway authenticeert een tunnel, en een openbare allowlist authenticeert een adresbereik. Geen van beide identificeert een persoon. NAT brengt veel gebruikers samen in één bronadres, en een kantoorbereik doet hetzelfde voor iedereen in het gebouw. Behandel beide als transportfeiten en houd de autorisatie in de applicatie, of in een integratie die identiteit expliciet beweert en als zodanig wordt beoordeeld.

DNS is een derde transportfeit. Een privaat antwoord vereist een private route, en een publieke frontend vereist een ondersteund oorsprongspad. Geen van beide draagt een toestemming.

Eén zin voor aan de muur: het netwerk bepaalt hoe een verzoek binnenkomt, en de applicatie bepaalt wat het mag lezen.

Ten vierde: voer de matrix op beide ingangen uit

Gebruik gecontroleerde accounts om A bij A, B bij B, A bij B, een onbekende hostnaam en een geauthenticeerd account zonder lidmaatschappen te testen. De vierde en de vijfde moeten gesloten falen en mogen nooit terugvallen op een standaardtenant. Voer dezelfde matrix ook via de VPN-ingang uit, met één sessiepot per account:

curl -s -o /dev/null -w '%{http_code}' -b jar.txt https://clinic-b.example.com/records/42
curl -s -o /dev/null -w '%{http_code}' -b jar.txt https://clinic-a.example.com/records/42

Wijzig daarna een item in de allowlist en voer opnieuw uit. Een resultaat dat verschuift, betekent dat een netwerkcontrole een autorisatiebeslissing neemt.

Herhaal de matrix na elke wijziging in de frontend, omdat een tweede codepad dat de controle overslaat de meest voorkomende manier is waarop deze grens erodeert. Het verwachte resultaat is saai: elke ingang levert dezelfde antwoorden.

Gerelateerde artikelen