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 ( // IPv4SpecialPurpose contains IPv4 special purpose IP prefixes // https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml IPv4SpecialPurpose = []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"), } // IPv6SpecialPurpose contains IPv6 special purpose IP prefixes // https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml IPv6SpecialPurpose = []netip.Prefix{ netip.MustParsePrefix("::1/128"), netip.MustParsePrefix("::/128"), netip.MustParsePrefix("::ffff:0:0/96"), netip.MustParsePrefix("64:ff9b::/96"), netip.MustParsePrefix("64:ff9b:1::/48"), netip.MustParsePrefix("100::/64"), netip.MustParsePrefix("2001::/23"), netip.MustParsePrefix("2001:db8::/32"), netip.MustParsePrefix("2002::/16"), netip.MustParsePrefix("2620:4f:8000::/48"), netip.MustParsePrefix("fc00::/7"), netip.MustParsePrefix("fe80::/10"), netip.MustParsePrefix("fec0::/10"), netip.MustParsePrefix("ff00::/8"), } )
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 2 defaults:
- tcp4 and tcp6 are considered the only valid networks/protocols
- 80 and 443 are considered the only valid ports
Both can be overridden by calling WithNetworks and WithPorts to specify different ones, or WithAnyNetwork and WithAnyPort to disable checking for those entirely.
A Guardian always checks if the IP encountered is in the IPv4 or IPv6 special prefixes IPv4SpecialPurpose or IPv6SpecialPurpose. If you want to allow a call to one of those prefixes you can explicitly permit it with WithAllowedV4Prefixes or WithAllowedV6Prefixes. You can also add additional explicitly denied prefixes through WithDeniedV4Prefixes and WithDeniedV6Prefixes.
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, otherwise move on to the next check
- Does the port match one of our permitted ports? If not, deny the quest, otherwise move on to the next check
Then, for either IPv4 or IPv6:
- Is the IP within an explicitly allowed IPvX prefix? If so, allow it, otherwise move on to the next check
- Is the IP within an explicitly denied IPvX prefix? If so, deny it, otherwise move on to the next check
- Is the IP within the IPvXSpecialPrefix? If so, deny it
If nothing matched, the request is permitted.
type Option ¶
type Option = func(g *Guardian)
Option sets an option on a Guardian
func WithAllowedV4Prefixes ¶
WithAllowedV4Prefixes allows explicitly whitelisting (additiona) IPv4 prefixes. If a prefix is passed here that overlaps with IPv4SpecialPurpose the request will be permitted.
This function overrides the allowed IPv4 prefixes, it does not accumulate.
func WithAllowedV6Prefixes ¶
WithAllowedV6Prefixes allows explicitly whitelisting (additiona) IPv6 prefixes. If a prefix is passed here that overlaps with IPv6SpecialPurpose the request will be permitted.
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 just IPv4SpecialPurpose.
This function overrides the denied IPv4 prefixes, it does not accumulate.
The prefixes passed in are prepended to IPv4SpecialPurpose. If you want to allow calls to a prefix in IPv4SpecialPurpose, use WithAllowedV4Prefixes instead.
func WithDeniedV6Prefixes ¶
WithDeniedV6Prefixes allows denying IPv6 prefixes in case you want to deny more than just IPv6SpecialPurpose.
This function overrides the denied IPv6 prefixes, it does not accumulate.
The prefixes passed in are prepended to IPv6SpecialPurpose. If you want to allow calls to a prefix in IPv6SpecialPurpose, use WithAllowedV6Prefixes instead.
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.