-
Notifications
You must be signed in to change notification settings - Fork 0
🔒 Fix IP Spoofing Vulnerability in Rate Limiter #368
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: main
Are you sure you want to change the base?
Changes from all commits
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 | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -66,12 +66,35 @@ function isValidIp(value: string): boolean { | |||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| function isTrustedProxy(ip: string): boolean { | ||||||||||||||||||||||||||||||||||
| // Matches standard private IPv4 ranges (RFC 1918) and localhost | ||||||||||||||||||||||||||||||||||
| const privateIpv4 = /^(127\.0\.0\.1|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)/; | ||||||||||||||||||||||||||||||||||
| // Matches IPv6 localhost | ||||||||||||||||||||||||||||||||||
| const privateIpv6 = /^(::1|fd|fc00::)/; | ||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: src/lib/rateLimit.ts
Line: 73
Comment:
**IPv6 ULA の fc プレフィックス範囲が不完全**
`fc00::` というパターンは `fc00::` で始まるアドレス(例: `fc00::1`)にしかマッチせず、`fc01::1` や `fc12::1` のような `fc00::/7` 範囲内の他の `fc` プレフィックスアドレスをカバーできていません。RFC 4193 の ULA 範囲 `fc00::/7` は `fc` と `fd` の両方のプレフィックスを含むため、`fc` 単体のパターンに変更すべきです。
```suggestion
const privateIpv6 = /^(::1|fc|fd)/;
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return privateIpv4.test(ip) || privateIpv6.test(ip); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
We can fix these issues by updating the regexes to be case-insensitive and cover the full ranges.
Suggested change
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| export function getClientIp(request: Request): string { | ||||||||||||||||||||||||||||||||||
| const forwardedFor = request.headers.get("x-forwarded-for"); | ||||||||||||||||||||||||||||||||||
| if (!forwardedFor) return "unknown"; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const proxyObservedIp = forwardedFor.split(",").at(-1)?.trim(); | ||||||||||||||||||||||||||||||||||
| if (proxyObservedIp && isValidIp(proxyObservedIp)) return proxyObservedIp; | ||||||||||||||||||||||||||||||||||
| const ips = forwardedFor.split(",").map(ip => ip.trim()); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Iterate from right to left to find the first non-trusted IP | ||||||||||||||||||||||||||||||||||
| // For this example, we assume we want to skip internal/private IPs (trusted proxies) | ||||||||||||||||||||||||||||||||||
| // and find the true client IP. | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+86
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
コメント内の「For this example」という表現は、プロトタイプや例示コードの名残のような印象を与えます。本番コードにはより正確な意図を示す表現を使用すべきです。
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: src/lib/rateLimit.ts
Line: 85-86
Comment:
**"For this example" は本番コードに残すべきでないコメント**
コメント内の「For this example」という表現は、プロトタイプや例示コードの名残のような印象を与えます。本番コードにはより正確な意図を示す表現を使用すべきです。
```suggestion
// 右端から左に向かって走査し、最初の非プライベート(非信頼)IPを真のクライアントIPとして返す。
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||||||||||||||||||||||||||||||||||
| for (let i = ips.length - 1; i >= 0; i--) { | ||||||||||||||||||||||||||||||||||
| const ip = ips[i]; | ||||||||||||||||||||||||||||||||||
| if (ip && isValidIp(ip) && !isTrustedProxy(ip)) { | ||||||||||||||||||||||||||||||||||
| return ip; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Fallback: If all are trusted (which is unlikely for a client request, but possible if they spoof), | ||||||||||||||||||||||||||||||||||
| // or if we couldn't find a valid non-trusted one, return the left-most valid IP. | ||||||||||||||||||||||||||||||||||
| const firstIp = ips[0]; | ||||||||||||||||||||||||||||||||||
| if (firstIp && isValidIp(firstIp)) return firstIp; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return "unknown"; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
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.
127\.0\.0\.1は単一のアドレスのみにマッチしますが、ループバック範囲127.0.0.0/8全体(127.0.0.1~127.255.255.255)をカバーしていません。127.0.0.2など127.0.0.1以外のループバックアドレスをプロキシとして使用している環境では、そのアドレスが「信頼済みプロキシ」とみなされず、誤ってクライアント IP として返されます。結果として、そのプロキシを経由するすべてのリクエストが同一のレートリミットバケットに集約され、正当なユーザーが不当にブロックされる可能性があります。Prompt To Fix With AI