Documentation
¶
Overview ¶
This package aims to help with implementing SSRF protections.
A Guardian.Safe method is provided that you can hook into a net.Dialer to prevent it from ever dialing to endpoints using certain protocols, destination ports or IPs in certain networks.
Once you have the dialer, you can pass it into things like an net/http.Transport to create an net/http.Client that won't allow requests to certain destinations. It's worth pointing out that DNS resolution of the destination will still take place, so that a name can be translated to an IP first.
s := ssrf.New() dialer := &net.Dialer{ Control: s.Safe, } transport := &http.Transport{ DialContext: dialer.DialContext, } client := &http.Client{ Transport: transport, }
Index ¶
- Variables
- type Guardian
- type Option
- func WithAllowedV4Prefixes(prefixes ...netip.Prefix) Option
- func WithAllowedV6Prefixes(prefixes ...netip.Prefix) Option
- func WithAnyNetwork() Option
- func WithAnyPort() Option
- func WithDeniedV4Prefixes(prefixes ...netip.Prefix) Option
- func WithDeniedV6Prefixes(prefixes ...netip.Prefix) Option
- func WithNetworks(networks ...string) Option
- func WithPorts(ports ...uint16) Option
Constants ¶
This section is empty.
Variables ¶
var ( // ErrProhibitedNetwork is returned when trying to dial a destination whose // network type is not in our allow list ErrProhibitedNetwork = errors.New("prohibited network type") // ErrProhibitedPort is returned when trying to dial a destination on a port // number that's not in our allow list ErrProhibitedPort = errors.New("prohibited port number") // ErrProhibitedIP is returned when trying to dial a destionation whose IP // is on our deny list ErrProhibitedIP = errors.New("prohibited IP address") // ErrInvalidHostPort is returned when [netip.ParseAddrPort] is unable to // parse our destination into its host and port constituents ErrInvalidHostPort = errors.New("invalid host:port pair") )
var ( // IPv4DeniedPrefixes contains IPv4 special purpose IP prefixes from IANA // as well as a number of other prefixes we wish to block by default // https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml IPv4DeniedPrefixes = []netip.Prefix{ netip.MustParsePrefix("0.0.0.0/8"), netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("100.64.0.0/10"), netip.MustParsePrefix("127.0.0.0/8"), netip.MustParsePrefix("169.254.0.0/16"), netip.MustParsePrefix("172.16.0.0/12"), netip.MustParsePrefix("192.0.0.0/24"), netip.MustParsePrefix("192.0.2.0/24"), netip.MustParsePrefix("192.31.196.0/24"), netip.MustParsePrefix("192.52.193.0/24"), netip.MustParsePrefix("192.88.99.0/24"), netip.MustParsePrefix("192.168.0.0/16"), netip.MustParsePrefix("192.175.48.0/24"), netip.MustParsePrefix("198.18.0.0/15"), netip.MustParsePrefix("198.51.100.0/24"), netip.MustParsePrefix("203.0.113.0/24"), netip.MustParsePrefix("240.0.0.0/4"), netip.MustParsePrefix("224.0.0.0/4"), } // IPv6GlobalUnicast is the prefix set aside by IANA for global unicast // assignments, i.e "the internet" IPv6GlobalUnicast = netip.MustParsePrefix("2000::/3") // IPv6NAT64Prefix is the prefix set aside for NAT64. This allows a server // to only have an IPv6 address but still be able to talk to an IPv4-only // server through DNS64+NAT64 IPv6NAT64Prefix = netip.MustParsePrefix("64:ff9b::/96") // IPv6DeniedPrefixes contains IPv6 special purpose IP prefixes from IANA // within the IPv6 Global Unicast range that we wish to block by default // https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml IPv6DeniedPrefixes = []netip.Prefix{ netip.MustParsePrefix("2001::/23"), netip.MustParsePrefix("2001:db8::/32"), netip.MustParsePrefix("2002::/16"), netip.MustParsePrefix("2620:4f:8000::/48"), } )
Functions ¶
This section is empty.
Types ¶
type Guardian ¶
type Guardian struct {
// contains filtered or unexported fields
}
Guardian will help ensure your network service isn't able to connect to certain network/protocols, ports or IP addresses. Once a Guardian has been created it is safe for concurrent use, but must not be modified.
The Guardian returned by New should be set as the net.Dialer.Control function.
func New ¶
New returns a Guardian initialised and ready to keep you safe
It is initialised with some defaults:
- tcp4 and tcp6 are considered the only valid networks/protocols
- 80 and 443 are considered the only valid ports
- For IPv4, any prefix in the IANA Special Purpose Registry for IPv4 is denied
- For IPv6, any prefix outside of the IPv6 Global Unicast range is denied, as well as any prefix in the IANA Special Purpose Registry for IPv6 that falls within the IPv6 Global Unicast range
Networks and ports can be overridden with WithNetworks and WithPorts to specify different ones, or WithAnyNetwork and WithAnyPort to disable checking for those entirely.
For prefixes WithAllowedV4Prefixes, WithAllowedV6Prefixes WithDeniedV4Prefixes and WithDeniedV6Prefixes can be used to customise which prefixes are additionally allowed or denied.
Guardian.Safe details the order in which things are checked.
func (*Guardian) Safe ¶
Safe is the function that should be passed in the net.Dialer's Control field
This function checks a number of things, in sequence:
- Does the network string match the permitted protocols? If not, deny the request
- Does the port match one of our permitted ports? If not, deny the request
Moving on, we then check if the IP we're given is an IPv6 address. IPv4-mapped-IPv6 addresses are considered as being an IPv6 address.
For IPv6 we then check:
- Is the IP part of one of the prefixes passed in through WithAllowedV6Prefixes? If so, allow the request
- Is the IP part of the IPv6 Global Unicast range? If not, deny the request
- Is the IP part of one of the prefixes passed in through WithDeniedV6Prefixes or within the denied prefixes in the IPv6 Global Unicast range? If so deny the request
If the IP is not IPv6, the it's IPv4 and so we check:
- Is the IP part of one of the prefixes passed in through WithAllowedV4Prefixes? If so, allow the request
- Is the IP part of one of the prefixes passed in through WithDeniedV4Prefixes or within the built-in denied IPv4 prefixes? If so, deny the request
If nothing matched, the request is permitted.
type Option ¶
type Option = func(g *Guardian)
Option sets an option on a Guardian
func WithAllowedV4Prefixes ¶
WithAllowedV4Prefixes adds explicitly allowed IPv4 prefixes. Any prefixes added here will be checked before the built-in deny list and as such can be used to allow connections to otherwise denied prefixes.
This function overrides the allowed IPv4 prefixes, it does not accumulate.
func WithAllowedV6Prefixes ¶
WithAllowedV6Prefixes adds explicitly allowed IPv6 prefixes. Any prefixes added here will be checked before the checks on global unicast range membership, the denied prefixes from WithDeniedV6Prefixes and the built-in deny list within the global unicast space. This can be used to allow connections to ranges outside of the global unicast range or connections to otherwise denied prefixes within the global unicast range.
This function should be called with IPv6NAT64Prefix as one of the prefixes, if you run in an IPv6-only environment but provide IPv4 connectivity through a combination of DNS64+NAT64. The NAT64 prefix is outside of the IPv6 global unicast range and as such blocked by default. Allowing it is typically harmless in dual-stack setups as your clients need an explicit route for 64:ff9b::/96 configured which won't be the case by default. Beware that allowing this prefix may allow for an address like 64:ff9b::7f00:1, i.e 127.0.0.1 mapped to NAT64. A NAT64 gateway should drop this. Ideally a DNS64 server would never generate an address for an RFC1918 IP in an A-record.
This function overrides the allowed IPv6 prefixes, it does not accumulate.
func WithAnyNetwork ¶
func WithAnyNetwork() Option
WithAnyNetwork allows requests to any network. It is equivalent to calling WithNetworks without any arguments.
func WithAnyPort ¶
func WithAnyPort() Option
WithAnyPort allows requests to any port number. It is equivalent to calling WithPorts without any arguments.
func WithDeniedV4Prefixes ¶
WithDeniedV4Prefixes allows denying IPv4 prefixes in case you want to deny more than the built-in set of denied prefixes. These prefixes are checked before checking the prefixes listed in IPv4DeniedPrefixes.
This function overrides the denied IPv4 prefixes, it does not accumulate.
func WithDeniedV6Prefixes ¶
WithDeniedV6Prefixes allows denying IPv6 prefixes in case you want to deny more than the built-in set of denied prefixes within the global unicast range. These prefixes are checked before checking the prefixes listed in IPv6DeniedPrefixes but after the IPv6GlobalUnicast membership check.
This function overrides the denied IPv6 prefixes, it does not accumulate.
func WithNetworks ¶
WithNetworks allows overriding which network types/protocols are considered valid. By default only tcp4 and tcp6 are permitted.
This function overrides the allowed networks, it does not accumulate.