Ruby version: https://gitlab.com/gitlab-org/gitlab-foss/blob/master/lib%2Fgitlab%2Furl_blocker.rb
Last active
February 8, 2022 01:59
-
-
Save stefansundin/32e8399f0c67c07c372b5ab51560e004 to your computer and use it in GitHub Desktop.
Golang dialer control to prevent untrusted input from connecting to private IP addresses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"errors" | |
"fmt" | |
"net" | |
"net/http" | |
"os" | |
"syscall" | |
) | |
var privateIPBlocks []*net.IPNet | |
func init() { | |
for _, cidr := range []string{ | |
"127.0.0.0/8", // IPv4 loopback | |
"10.0.0.0/8", // RFC1918 | |
"172.16.0.0/12", // RFC1918 | |
"192.168.0.0/16", // RFC1918 | |
"169.254.0.0/16", // RFC3927 link-local | |
"::1/128", // IPv6 loopback | |
"fe80::/10", // IPv6 link-local | |
"fc00::/7", // IPv6 unique local addr | |
} { | |
_, block, err := net.ParseCIDR(cidr) | |
if err != nil { | |
panic(fmt.Errorf("parse error on %q: %v", cidr, err)) | |
} | |
privateIPBlocks = append(privateIPBlocks, block) | |
} | |
} | |
func isPrivateIP(ip net.IP) bool { | |
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { | |
return true | |
} | |
for _, block := range privateIPBlocks { | |
if block.Contains(ip) { | |
return true | |
} | |
} | |
return false | |
} | |
func protectedDialerControl(network, address string, c syscall.RawConn) error { | |
host, port, err := net.SplitHostPort(address) | |
if err != nil { | |
return err | |
} | |
ip := net.ParseIP(host) | |
if isPrivateIP(ip) { | |
return errors.New("private IP address not allowed.") | |
} | |
if port != "80" && port != "443" { | |
return errors.New("nonstandard port not allowed.") | |
} | |
return nil | |
} | |
func main() { | |
var untrustedUrl string | |
// untrustedUrl = "http://--1.sslip.io/" // Get "http://--1.sslip.io/": dial tcp [::1]:80: private IP address not allowed. | |
// untrustedUrl = "http://www.192.168.0.1.sslip.io/" // Get "http://www.192.168.0.1.sslip.io/": dial tcp 192.168.0.1:80: private IP address not allowed. | |
// untrustedUrl = "http://127.0.0.1.sslip.io/" // Get "http://127.0.0.1.sslip.io/": dial tcp 127.0.0.1:80: private IP address not allowed. | |
// untrustedUrl = "https://wikipedia.org:8080/" // Get "https://wikipedia.org:8080/": dial tcp [2620:0:863:ed1a::1]:8080: nonstandard port not allowed. | |
untrustedUrl = "https://wikipedia.org/" // 200 OK | |
fmt.Println(untrustedUrl) | |
protectedTransport := &http.Transport{ | |
DialContext: (&net.Dialer{ | |
Control: protectedDialerControl, | |
}).DialContext, | |
} | |
client := &http.Client{ | |
Transport: protectedTransport, | |
} | |
resp, err := client.Get(untrustedUrl) | |
if err != nil { | |
fmt.Fprintln(os.Stderr, err) | |
return | |
} | |
fmt.Println(resp.Status) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment