Direct naar inhoud

IP-adres uit een header vertrouwd

CWE-290CWE-348OWASP A01:2021Bijgewerkt 3 september 20265 min leestijd

Achter een proxy staat het echte IP-adres van de bezoeker in een header als X-Forwarded-For. Die header wordt echter net zo goed geaccepteerd wanneer een client hem zelf meestuurt. Vertrouwt uw applicatie hem zonder te weten wie hem heeft gezet, dan bepaalt de aanvaller zijn eigen IP-adres, en omzeilt hij limieten, blokkades en toegangsregels.

Bijna elke applicatie draait tegenwoordig achter een loadbalancer, een reverse proxy of een CDN. Daardoor ziet de applicatie niet meer het adres van de bezoeker maar dat van de proxy, en wordt het echte adres in een header meegestuurd. Die oplossing werkt, tot iemand die header zelf invult. De kunst is onderscheiden wie hem heeft gezet.

Waarom is de header niet te vertrouwen?

Wanneer een proxy een verzoek doorstuurt, voegt hij het adres van de oorspronkelijke bezoeker toe aan een header, meestal X-Forwarded-For. De applicatie leest die waarde en weet zo van wie het verzoek kwam. Zo is het bedoeld.

Het punt is dat een header onderdeel is van het verzoek. Een client kan hem zelf meesturen, en een proxy die een bestaande waarde aantreft, voegt zijn eigen gegevens er meestal achter toe in plaats van hem te vervangen. De header is dus een lijst, waarvan het begin door de client is bepaald en het einde door uw eigen infrastructuur.

Van een vertrouwd IP-adres uit een header spreken we wanneer een applicatie de eerste waarde uit die lijst neemt, of de hele waarde klakkeloos overneemt, zonder te weten welk deel van haar eigen proxy komt. Vergelijk het met een pakket waarop meerdere afzenders staan gestempeld: die van het distributiecentrum kunt u vertrouwen, die de verzender er zelf op heeft geschreven niet. Wie de bovenste leest, leest wat de verzender wilde dat u zou lezen.

Hoe misbruikt een aanvaller X-Forwarded-For?

Kwetsbaar:

function ipVan(req) {
  // De eerste waarde: precies degene die de client zelf kan bepalen
  const keten = req.headers['x-forwarded-for'];
  return keten ? keten.split(',')[0].trim() : req.socket.remoteAddress;
}

app.post('/inloggen', async (req, res) => {
  const ip = ipVan(req);
  if (await teVeelPogingen(ip)) return res.status(429).send('Te veel pogingen');
  // ...
});

// En een toegangsregel op basis van hetzelfde adres
app.use('/beheer', (req, res, next) => {
  if (ipVan(req).startsWith('10.0.')) return next();     // 'intern'
  res.sendStatus(403);
});

Twee maatregelen die allebei op dezelfde onbetrouwbare bron rusten. De aanvaller stuurt eenvoudigweg zelf een header mee:

POST /inloggen HTTP/1.1
X-Forwarded-For: 203.0.113.7

Bij elke poging een ander adres, en de snelheidsbeperking telt nooit verder dan één. Een wachtwoordaanval die na vijf pogingen had moeten stoppen, loopt ongehinderd door. En de toegangsregel op het beheerpaneel is nog directer te omzeilen:

GET /beheer/gebruikers HTTP/1.1
X-Forwarded-For: 10.0.4.12

HTTP/1.1 200 OK

De aanvaller heeft zichzelf tot intern systeem verklaard, en de applicatie gelooft hem.

Veilig:

// Het aantal proxy's vóór de applicatie is bekend en vast
app.set('trust proxy', 2);      // bijvoorbeeld: CDN + eigen loadbalancer

app.post('/inloggen', async (req, res) => {
  const ip = req.ip;            // telt terug vanaf de achterkant van de lijst
  if (await teVeelPogingen(ip)) return res.status(429).send('Te veel pogingen');
  // ...
});
# En de proxy overschrijft wat de client meestuurde
location / {
    proxy_set_header X-Forwarded-For $remote_addr;   # niet toevoegen: vervangen
    proxy_set_header X-Real-IP       $remote_addr;
    proxy_pass http://applicatie;
}

De applicatie telt nu vanaf de achterkant van de lijst het bekende aantal vertrouwde proxy’s terug, waarmee ze precies de waarde neemt die haar eigen infrastructuur heeft toegevoegd. Daarnaast de proxy vervangt de header in plaats van eraan toe te voegen, zodat wat de client meestuurde helemaal verdwijnt.

Voor de toegangsregel op het beheerpaneel geldt bovendien: een IP-adres is een aanwijzing, geen bewijs van identiteit. Gebruik het hooguit als aanvullende voorwaarde bovenop echte authenticatie en autorisatie.

Vertrouw niet blind op de instelling die “alle proxy’s vertrouwen” heet. In veel frameworks komt dat neer op het accepteren van de eerste waarde in de lijst, en dat is precies de waarde die de client bepaalt. Geef altijd het exacte aantal proxy’s op dat voor uw applicatie staat.

Wat is de impact van een vertrouwde IP-header?

De ernst loopt van middelzwaar tot hoog, afhankelijk van waarvoor het adres wordt gebruikt. Wordt het alleen voor statistieken gebruikt, dan blijft de schade beperkt tot vervuilde cijfers. Zodra er beveiligingsbeslissingen op rusten, wordt het ernstig.

De meest voorkomende gevolgen zijn het omzeilen van snelheidsbeperkingen en blokkades. Een aanvaller die zijn adres per verzoek wisselt, kan onbeperkt wachtwoorden proberen, codes raden of gegevens ophalen zonder ooit een limiet te raken. De maatregel is er, maar telt nooit door.

Ernstiger is het omzeilen van toegangsregels. Systemen die een beheerinterface, een monitoringpagina of een interne API afschermen op basis van een IP-bereik, geven die toegang weg aan iedereen die de juiste header meestuurt. Dat is een rechtstreekse route naar functionaliteit die als afgeschermd gold.

Ten slotte is er de kant van de logbestanden. Wordt het adres uit een onbetrouwbare header vastgelegd, dan kan een aanvaller zijn sporen op een willekeurig ander adres laten wijzen. Bij een incidentonderzoek leidt dat naar de verkeerde conclusie, en soms naar een onschuldige derde.

Hoe spoor je een vertrouwde IP-header op?

Een tester stuurt verzoeken met een zelfgekozen X-Forwarded-For en kijkt of de applicatie zich anders gedraagt: wordt de snelheidsbeperking opnieuw geteld, verandert het adres in een terugkoppeling of in een logregel, of wordt een afgeschermde route opeens toegankelijk?

Daarnaast worden de varianten geprobeerd. Naast X-Forwarded-For zijn er X-Real-IP, X-Client-IP, X-Originating-IP, Forwarded en True-Client-IP; applicaties kijken vaak naar meerdere en vertrouwen er soms één die de proxy helemaal niet zet. Ook wordt getoetst wat er gebeurt bij meerdere waarden in de lijst en bij een verzonnen waarde vooraan. Verder wordt gekeken of de proxy de header vervangt of eraan toevoegt, en of de applicatie het juiste aantal proxy’s hanteert. AssistSec besteedt daarbij bijzondere aandacht aan toegangsregels die op een IP-bereik rusten, omdat daar het verschil tussen een omzeilde limiet en een omzeilde autorisatie ligt.

Hoe voorkom je een vertrouwde IP-header?

  • Configureer uw applicatie met het exacte aantal vertrouwde proxy’s dat ervoor staat.
  • Laat uw eigen proxy de forwarding-header vervangen in plaats van eraan toevoegen.
  • Neem nooit de eerste waarde uit de lijst; tel terug vanaf de achterkant.
  • Negeer de header volledig wanneer uw applicatie rechtstreeks aan het internet hangt.
  • Gebruik een IP-adres nooit als enige grond voor toegang; het is een aanwijzing, geen identiteit.
  • Beperk toegang tot interne functionaliteit met netwerksegmentatie, een VPN of wederzijdse authenticatie.
  • Let ook op de alternatieve headers die uw framework mogelijk meeneemt.
  • Baseer snelheidsbeperkingen op het account waar dat kan, en op het IP-adres alleen voor anonieme verzoeken.
  • Leg in uw logbestanden vast welke waarde is gebruikt en waar die vandaan kwam.

Bronnen

Veelgestelde vragen

Hoe bepaal ik het echte IP-adres dan wel?

Door te weten hoeveel vertrouwde proxy's er voor uw applicatie staan en vanaf de achterkant van de lijst dat aantal terug te tellen. De waarden die uw eigen proxy heeft toegevoegd zijn betrouwbaar; alles wat de client zelf meestuurde staat vooraan en is dat niet.

Waarom is dit erger dan alleen een omzeilde limiet?

Omdat het IP-adres in veel systemen ook wordt gebruikt voor toegangsregels, voor het herkennen van vertrouwde locaties en in logbestanden. Een aanvaller die zijn adres bepaalt, kan zich voordoen als een intern systeem én zijn sporen op een ander adres laten wijzen.

Mag ik de header helemaal negeren?

Draait uw applicatie rechtstreeks aan het internet, dan is dat de juiste keuze: gebruik het adres van de verbinding. Staat er een proxy voor, dan hebt u de header nodig, maar alleen het deel dat uw eigen proxy heeft toegevoegd.

Wat als er meerdere proxy's zijn?

Dan is het aantal wat telt. Configureer uw framework met het exacte aantal vertrouwde proxy's, zodat het precies zoveel posities terugtelt. Een instelling die alle proxy's vertrouwt, komt neer op de client vertrouwen.

Verwante artikelen

Druk op / om te zoeken · Esc