ssrf

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 1, 2022 License: MIT Imports: 5 Imported by: 6

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

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 (
	// 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("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

func New(opts ...Option) *Guardian

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

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, 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

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

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

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

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

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

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

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

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

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
ssrfgen command

Jump to

Keyboard shortcuts

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