mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-07-15 21:48:13 +03:00
fix: block link-local addresses in SSRF protection
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -56,7 +57,17 @@ func IsTailscaleIP(ip net.IP) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsPrivateIP(ip net.IP) bool {
|
func IsPrivateIP(ip net.IP) bool {
|
||||||
return IsLocalhostIP(ip) || IsPrivateLanIP(ip) || IsTailscaleIP(ip) || IsLocalIPv6(ip)
|
if IsLocalhostIP(ip) || IsPrivateLanIP(ip) || IsTailscaleIP(ip) || IsLocalIPv6(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, ok := netip.AddrFromSlice(ip)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = addr.Unmap()
|
||||||
|
return addr.IsLoopback() || addr.IsPrivate() || addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast() || addr.IsUnspecified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsURLPrivate(ctx context.Context, u *url.URL) (bool, error) {
|
func IsURLPrivate(ctx context.Context, u *url.URL) (bool, error) {
|
||||||
|
|||||||
@@ -110,12 +110,19 @@ func TestIsPrivateIP(t *testing.T) {
|
|||||||
ip string
|
ip string
|
||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
{"127.0.0.1", true}, // localhost
|
{"127.0.0.1", true}, // localhost
|
||||||
{"192.168.1.1", true}, // private LAN
|
{"192.168.1.1", true}, // private LAN
|
||||||
{"100.64.0.1", true}, // Tailscale
|
{"100.64.0.1", true}, // Tailscale
|
||||||
{"fd00::1", true}, // local IPv6
|
{"169.254.169.254", true}, // IPv4 link-local
|
||||||
{"8.8.8.8", false}, // public IPv4
|
{"169.254.170.2", true}, // IPv4 link-local
|
||||||
{"2001:4860:4860::8888", false}, // public IPv6
|
{"::ffff:169.254.169.254", true}, // IPv4-mapped link-local
|
||||||
|
{"fe80::1", true}, // IPv6 link-local
|
||||||
|
{"ff02::1", true}, // IPv6 link-local multicast
|
||||||
|
{"0.0.0.0", true}, // IPv4 unspecified
|
||||||
|
{"::", true}, // IPv6 unspecified
|
||||||
|
{"fd00::1", true}, // private IPv6
|
||||||
|
{"8.8.8.8", false}, // public IPv4
|
||||||
|
{"2001:4860:4860::8888", false}, // public IPv6
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -231,6 +238,36 @@ func TestIsURLPrivate(t *testing.T) {
|
|||||||
expectPriv: true,
|
expectPriv: true,
|
||||||
expectError: false,
|
expectError: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "IPv4 link-local metadata IP",
|
||||||
|
urlStr: "http://169.254.169.254",
|
||||||
|
expectPriv: true,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IPv4-mapped link-local metadata IP",
|
||||||
|
urlStr: "http://[::ffff:169.254.169.254]",
|
||||||
|
expectPriv: true,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IPv6 link-local IP",
|
||||||
|
urlStr: "http://[fe80::1]",
|
||||||
|
expectPriv: true,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IPv4 unspecified IP",
|
||||||
|
urlStr: "http://0.0.0.0",
|
||||||
|
expectPriv: true,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "IPv6 unspecified IP",
|
||||||
|
urlStr: "http://[::]",
|
||||||
|
expectPriv: true,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "public IP - Google DNS",
|
name: "public IP - Google DNS",
|
||||||
urlStr: "http://8.8.8.8",
|
urlStr: "http://8.8.8.8",
|
||||||
|
|||||||
Reference in New Issue
Block a user