Een verzoek dat bij je applicatie binnenkomt, draagt twee soorten adresbewijs: de peer die de netwerkverbinding opende, en een HTTP-veld dat het adres van een eerdere client beweert. Ze beantwoorden verschillende vragen. De socket-peer wordt waargenomen, terwijl de header wordt beweerd door wie hem schreef, en geen van beide zou de ander stilzwijgend moeten vervangen in een log of een toegangsregel. De vergelijking hieronder houdt ze apart en toont daarna de enige opstelling waarin de header bruikbaar wordt.

Beschrijf het pad voordat je iets vergelijkt:

Client -> approved reverse proxy -> application origin

In die opstelling is de socket-peer van de oorsprong meestal de proxy, en een doorstuurheader kan de bewering van de proxy over de client dragen. De waarde komt uit de beheerste relatie met die proxy, niet uit de naam van de header.

Twee soorten adresbewijs

De socket-peer is een feit over één TCP-verbinding: iets voltooide een handdruk vanaf dat adres. Het is niet per se de persoon, het apparaat of het account waar je om geeft, want een VPN of een Tor-uitgang kan wettig de bron zijn die een dienst waarneemt. Het blijft wel het enige adres dat de host zelf heeft geverifieerd, en dat is de hele waarde ervan.

Een doorstuurveld is metadata over een keten. X-Forwarded-For, X-Real-IP en het gestandaardiseerde veld Forwarded kunnen door elke client worden geschreven die een eindpunt bereikt dat ze terugstuurt, dus hun betrouwbaarheid hangt volledig af van wie ze mocht instellen.

Houd beide in de verzoekcontext onder verschillende namen. Een applicatie die de peer met de header overschrijft, verliest het vermogen om later een vertrouwensfout te reconstrueren, en dat is precies het moment waarop je die nodig hebt.

Een header leidt de verbinding nooit om

Een adres in een doorstuurveld schrijven verandert de bron van de TCP-verbinding die bij de server aankomt niet, en verandert ook nergens op het pad de routering. Gewone VPN-routering of bron-NAT levert verkeer zonder enige HTTP-header. Evenzo herschrijft een transitgateway een goed beschermde HTTPS-uitwisseling niet zonder de opstelling van het TLS-eindpunt te wijzigen.

De omgekeerde fout is even gewoon: HTTPS maakt een door de client aangeleverde doorstuurheader niet waarheidsgetrouw. Het beschermt het transport van een valse bewering precies zo goed als van een ware.

Als een collega een headerwijziging voorstelt om een routeringsprobleem op te lossen, is de diagnose al misgegaan. Repareer het pad, of repareer de proxyconfiguratie die de header schrijft.

Noem de proxy die mag beweren

Twee regels maken een doorstuurveld bruikbaar, en beide zijn configuratie in plaats van code. De rand normaliseert of gooit weg wat van buiten komt, volgens zijn eigen gedocumenteerde gedrag voor die velden. Daarna neemt de oorsprong een vervangend adres uit het proxypad dat je noemde en uit niets anders. Die tweede regel geldt alleen zolang clients de oorsprong niet op een andere manier kunnen bereiken, want een oorsprong die zowel directe als via proxy binnenkomende verzoeken beantwoordt, weet niet welke ervan hij bedient.

De NGINX-module voor het echte IP toont de splitsing duidelijk. set_real_ip_from noemt de afzenders wier bewering telt, en real_ip_header zegt welk veld die draagt; de eerste doet het vertrouwen, de tweede zegt alleen waar je moet kijken.

set_real_ip_from 10.20.0.0/24;
real_ip_header X-Forwarded-For;
# real_ip_recursive off - take the rightmost value in the field
# real_ip_recursive on  - walk the list back past every trusted address

Een richtlijn voor headerselectie alleen stelt nog steeds niets vast over wie de waarde heeft gestuurd.

Transportbeveiliging moet de hele weg lopen tussen de componenten die je vertrouwt. Een rand die correct normaliseert, verliest die waarde op het moment dat een onbeschermde hop verderop iemand anders de bewering laat bewerken terwijl die onderweg is.

Behandel ketens met meerdere hops bewust

Meerdere proxy's vereisen een expliciet ketenmodel. Neem niet de meest linkse of rechtse kommagescheiden waarde omdat een blogpost dat deed; zoek uit welke van je proxy's toevoegt, welke vervangt en welke het binnenkomende veld bewaart. NGINX kan bijvoorbeeld de lijst achterstevoren door zijn vertrouwde set lopen en stoppen bij het eerste adres dat het niet bezit, en dat is de regel van dat product, geen wet die elk framework volgt. Lees de documentatie van je eigen proxy met dezelfde twee vragen: welke waarde selecteert hij, en welke afzenders gelooft hij?

Schrijf de keten op: hopvolgorde, welke hop het veld zet en welke hop inkomende kopieën verwijdert. Houd die notitie dan naast de proxyconfiguratie, niet alleen in de ticket. Test het daarna door een verzoek met een vervalste header via het goedgekeurde validatieproces te sturen en te bevestigen dat de oorsprong het negeert.

Wanneer je een hop toevoegt, herzie dat document. Een nieuwe loadbalancer vóór de vertrouwde proxy kan veranderen welk adres wint zonder dat iemand de applicatie bewerkt.

Houd beide waarnemingen in het log

Waar diagnostiek is toegestaan, log dan de peer van de verbinding en het genormaliseerde clientadres als twee afzonderlijke velden, en voeg alleen de doorstuurmetadata toe die het doel echt nodig heeft. Bewijs de uitrol daarna met twee verzoeken: een via de proxy dat beide waarden verstandig zou moeten vastleggen, en een poging rechtstreeks naar de oorsprong die moet worden geweigerd.

Onthoud wat het paar betekent. De header voegt context toe die een component levert die je hebt gekozen te vertrouwen; de peer blijft het enige adres dat je host werkelijk heeft gezien. Geen van beide is bewijs van individuele identiteit, en een ontwerp dat een van beide als bewijs behandelt, zal het over iemand mis hebben.

Gerelateerde artikelen