Skip to content

Commit 41adbf5

Browse files
authored
Merge commit from fork
* fix(aws-lambda): use last X-Forwarded-For IP in ALB adapter ALB appends the real client IP to the end of the X-Forwarded-For header. Taking the first value allows attackers to spoof their IP by prepending a trusted address, bypassing ipRestriction middleware. Use the last IP instead, which is the one appended by ALB. * refactor(test): use it.each for ALB x-forwarded-for tests Consolidate two similar ALB tests into a parameterized it.each to improve readability and make each test case clearly distinguishable.
1 parent 2de30d7 commit 41adbf5

File tree

2 files changed

+31
-21
lines changed

2 files changed

+31
-21
lines changed

src/adapter/aws-lambda/conninfo.test.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,27 +71,36 @@ describe('getConnInfo', () => {
7171
})
7272

7373
describe('ALB', () => {
74-
it('Should return the client IP from x-forwarded-for header', () => {
75-
const ip = '192.0.2.50'
76-
const req = new Request('http://localhost/', {
77-
headers: {
78-
'x-forwarded-for': `${ip}, 10.0.0.1`,
79-
},
80-
})
81-
const c = new Context(req, {
82-
env: {
83-
requestContext: {
84-
elb: {
85-
targetGroupArn: 'arn:aws:elasticloadbalancing:...',
74+
it.each([
75+
{
76+
description: 'ALB appends real client IP',
77+
xff: '10.0.0.1, 192.0.2.50',
78+
expected: '192.0.2.50',
79+
},
80+
{
81+
description: 'attacker-controlled first IP',
82+
xff: '127.0.0.1, 192.168.1.100',
83+
expected: '192.168.1.100',
84+
},
85+
])(
86+
'Should return the last IP from x-forwarded-for ($description)',
87+
({ xff, expected }) => {
88+
const req = new Request('http://localhost/', {
89+
headers: { 'x-forwarded-for': xff },
90+
})
91+
const c = new Context(req, {
92+
env: {
93+
requestContext: {
94+
elb: {
95+
targetGroupArn: 'arn:aws:elasticloadbalancing:...',
96+
},
8697
},
8798
},
88-
},
89-
})
90-
91-
const info = getConnInfo(c)
92-
93-
expect(info.remote.address).toBe(ip)
94-
})
99+
})
100+
const info = getConnInfo(c)
101+
expect(info.remote.address).toBe(expected)
102+
}
103+
)
95104

96105
it('Should return undefined when no x-forwarded-for header', () => {
97106
const c = new Context(new Request('http://localhost/'), {

src/adapter/aws-lambda/conninfo.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ export const getConnInfo: GetConnInfo = (c: Context<Env>) => {
5959
else {
6060
const xff = c.req.header('x-forwarded-for')
6161
if (xff) {
62-
// First IP is the client
63-
address = xff.split(',')[0].trim()
62+
const ips = xff.split(',')
63+
// ALB appends the real client IP to the end of the header
64+
address = ips[ips.length - 1].trim()
6465
}
6566
}
6667

0 commit comments

Comments
 (0)