ssrf

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2023 License: MIT Imports: 6 Imported by: 2

README

🌐 ssrf 🔐

A Go library for implementing SSRF protections

Build Status Release Go report card GoDoc

This package aims to help with implementing SSRF protections. It differs from other packages in that it is kept automatically in sync with the IANA Special Purpose Registries for both IPv4 and IPv6 with some additions.

The generation is done by ssrfgen.

A 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 http.Transport to create an 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.

Usage

You can retrieve this package with:

go get code.dny.dev/ssrf

You can then call the New() method to get a Guardian and pass it on to your net.Dialer of choice.

s := ssrf.New()

dialer := &net.Dialer{
	Control: s.Safe,
}

transport := &http.Transport{
	DialContext: dialer.DialContext,
}

client := &http.Client{
	Transport: transport,
}

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

Constants

This section is empty.

Variables

View Source
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")
)
View Source
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

func New(opts ...Option) *Guardian

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

func (g *Guardian) Safe(network string, address string, _ syscall.RawConn) error

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

func WithAllowedV4Prefixes(prefixes ...netip.Prefix) Option

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

func WithAllowedV6Prefixes(prefixes ...netip.Prefix) Option

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

func WithDeniedV4Prefixes(prefixes ...netip.Prefix) Option

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

func WithDeniedV6Prefixes(prefixes ...netip.Prefix) Option

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

func WithNetworks(networks ...string) Option

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.

func WithPorts

func WithPorts(ports ...uint16) Option

WithPorts allows overriding which destination ports are considered valid. By default only requests to 80 and 443 are permitted.

This function overrides the allowed ports, it does not accumulate.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL