Skip to content

Instantly share code, notes, and snippets.

@003random
Last active December 7, 2020 15:15
Show Gist options
  • Save 003random/df02998f804c075fb4223f1791983888 to your computer and use it in GitHub Desktop.
Save 003random/df02998f804c075fb4223f1791983888 to your computer and use it in GitHub Desktop.
Golang SSRF protection IPv4
package main
import (
"context"
"errors"
"log"
"net"
"net/http"
"strings"
"time"
)
type blacklist struct {
Ranges []net.IPNet
}
func main() {
tr := &http.Transport{
DialContext: func(ctx context.Context, network string, addr string) (conn net.Conn, err error) {
s := strings.LastIndex(addr, ":")
IPs, err := net.LookupHost(addr[:s])
if err != nil {
return nil, err
}
b := newBlacklist()
for _, IP := range IPs {
if !b.isAllowed(net.ParseIP(IP)) {
err = errors.New("IP not allowed")
return
}
conn, err = net.Dial(network, IP+addr[s:])
if err == nil {
break
}
}
return
},
}
var client = &http.Client{
Transport: tr,
Timeout: time.Duration(5 * time.Second),
}
req, err := http.NewRequest("GET", "http://localhost", nil)
if err != nil {
log.Println(err)
return
}
_, err = client.Do(req)
if err != nil {
log.Println(err)
return
}
}
func newBlacklist() (bl blacklist) {
r := []net.IPNet{}
ranges := []string{
"0.0.0.0/32", // Current network (only valid as source address)
"240.0.0.0/4", // Reserved for future use
"203.0.113.0/24", // Assigned as TEST-NET-3
"198.51.100.0/24", // Assigned as TEST-NET-2, documentation and examples
"198.18.0.0/15", // Used for benchmark testing of inter-network communications between two separate subnets
"192.0.2.0/24", // Assigned as TEST-NET-1, documentation and examples
"100.64.0.0/10", // Shared address space for communications between a service provider and its subscribers when using a carrier-grade NAT.
"255.255.255.255/32", // Reserved for the "limited broadcast" destination address
"192.0.0.0/24", // IETF Protocol Assignments
"192.0.2.0/24", // Assigned as TEST-NET-1, documentation and examples
"192.88.99.0/24", // Reserved. Formerly used for IPv6 to IPv4 relay (included IPv6 address block 2002::/16)
"192.168.0.0/16", // Used for local communications within a private network
"172.16.0.0/12", // Used for local communications within a private network
"10.0.0.0/8", // Used for local communications within a private network
"127.0.0.0/8", // Used for loopback addresses to the local host
"169.254.0.0/16", // Used for link-local addresses between two hosts on a single link when no IP address is otherwise specified
"224.0.0.0/4", // In use for IP multicast.[9] (Former Class D network)
}
for _, sCIDR := range ranges {
_, c, _ := net.ParseCIDR(sCIDR)
r = append(r, *c)
}
bl.Ranges = r
return
}
func (b *blacklist) isAllowed(IP net.IP) bool {
for _, r := range b.Ranges {
if r.Contains(IP) {
return false
}
}
return true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment