-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
fix: strip port from X-Forwarded-For in request.ip (#827) #1943
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 3 commits
5059663
f64bc73
3776120
2514803
3bb63fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| 'use strict' | ||
|
|
||
| const { describe, it } = require('node:test') | ||
| const assert = require('node:assert/strict') | ||
| const Stream = require('stream') | ||
| const Koa = require('../..') | ||
| const Request = require('../../test-helpers/context').request | ||
|
|
||
| describe('req.ip with port in X-Forwarded-For', () => { | ||
| describe('when XFF contains IPv4 with port', () => { | ||
| it('should strip the port', () => { | ||
| const req = request() | ||
| req.app.proxy = true | ||
| req.header['x-forwarded-for'] = '1.2.3.4:8080, 5.6.7.8' | ||
|
Comment on lines
+8
to
+12
|
||
| assert.strictEqual(req.ip, '1.2.3.4') | ||
| }) | ||
| }) | ||
|
|
||
| describe('when XFF contains IPv6 with port', () => { | ||
| it('should strip the port and brackets', () => { | ||
| const req = request() | ||
| req.app.proxy = true | ||
| req.header['x-forwarded-for'] = '[::1]:8080, 127.0.0.1' | ||
| assert.strictEqual(req.ip, '::1') | ||
| }) | ||
| }) | ||
|
|
||
| describe('when XFF contains plain IPv4 (no port)', () => { | ||
| it('should return the address as-is', () => { | ||
| const req = request() | ||
| req.app.proxy = true | ||
| req.header['x-forwarded-for'] = '1.2.3.4, 5.6.7.8' | ||
| assert.strictEqual(req.ip, '1.2.3.4') | ||
| }) | ||
| }) | ||
|
|
||
| describe('when XFF contains plain IPv6 (no port)', () => { | ||
| it('should return the address as-is', () => { | ||
| const req = request() | ||
| req.app.proxy = true | ||
| req.header['x-forwarded-for'] = '::1, 127.0.0.1' | ||
| assert.strictEqual(req.ip, '::1') | ||
| }) | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,7 @@ const sp = require('./search-params.js') | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| const typeis = require('type-is') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fresh = require('fresh') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const only = require('./only.js') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const forwarded = require('forwarded') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const util = require('util') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const IP = Symbol('context#ip') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -23,6 +24,28 @@ const IP = Symbol('context#ip') | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Prototype. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Strip port from an IP address string. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Handles IPv4 (e.g. "1.2.3.4:8080") and bracketed IPv6 (e.g. "[::1]:8080"). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} addr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {string} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @private | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function stripPort(addr) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!addr) return '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (addr.charCodeAt(0) === 91 /* [ */) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const end = addr.indexOf(']') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return end > 1 ? addr.slice(1, end) : addr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // IPv4 with port has exactly one colon; IPv6 has multiple | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const lastColon = addr.lastIndexOf(':') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (lastColon > 0 && addr.indexOf(':') === lastColon) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return addr.slice(0, lastColon) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return addr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module.exports = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -462,7 +485,13 @@ module.exports = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| get ip () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this[IP]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this[IP] = this.ips[0] || this.socket.remoteAddress || '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Use forwarded() to properly parse the X-Forwarded-For chain. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // forwarded() returns [socketAddr, ...xffEntriesReversed], so the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // first XFF entry (original client) is at the end of the array. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // See: https://github.com/koajs/koa/issues/827 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const addrs = forwarded(this.req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const addr = stripPort(this.ips[0]) || stripPort(addrs[addrs.length - 1]) || addrs[0] || '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+487
to
+492
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Use forwarded() to properly parse the X-Forwarded-For chain. | |
| // forwarded() returns [socketAddr, ...xffEntriesReversed], so the | |
| // first XFF entry (original client) is at the end of the array. | |
| // See: https://github.com/koajs/koa/issues/827 | |
| const addrs = forwarded(this.req) | |
| const addr = stripPort(this.ips[0]) || stripPort(addrs[addrs.length - 1]) || addrs[0] || '' | |
| let addr | |
| // When behind a proxy, trust only the configured proxyIpHeader via this.ips. | |
| if (this.app && this.app.proxy) { | |
| const ips = this.ips | |
| if (ips && ips.length > 0) { | |
| addr = stripPort(ips[0]) | |
| } | |
| } | |
| // Fallback to the remote socket address when not behind a proxy | |
| // or when no trusted forwarded header is available. | |
| if (!addr) { | |
| const socket = this.req && this.req.socket | |
| const remoteAddress = | |
| socket && | |
| (socket.remoteAddress || | |
| (socket.socket && socket.socket.remoteAddress)) | |
| addr = stripPort(remoteAddress || '') | |
| } |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -44,6 +44,7 @@ | |||
| "destroy": "^1.2.0", | ||||
| "encodeurl": "^2.0.0", | ||||
| "escape-html": "^1.0.3", | ||||
| "forwarded": "^0.2.0", | ||||
|
||||
| "forwarded": "^0.2.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stream,Koa, andRequestare currently unused in this test file, which will fail thestandardlinter (andRequestis also inconsistent with other request tests that import the helper asrequest). Please remove unused imports and/or use the same helper pattern as the existing request tests.