Server Side Request Forgery (SSRF)
- Rule ID: go_gosec_injection_ssrf_injection
- Languages: go
- Source: ssrf_injection.yml
Description
Server-Side Request Forgery (SSRF) is a security vulnerability that occurs when a server-side application makes HTTP requests to arbitrary URLs controlled by the user. SSRF can be exploited by attackers to target internal systems behind firewalls that are otherwise inaccessible from the external network, by tricking the server into making requests to these systems.
Remediations
To mitigate SSRF vulnerabilities, follow these guidelines:
✅ Validate User Input
Avoid using direct user input to construct URLs for backend requests. If you must use user input, validate or sanitize it rigorously.
✅ Restrict URLs to Known Safe Domains
Where possible, limit requests to a predefined set of safe URLs or domains. This can be done using server-side mapping from user-supplied keys to URLs.
✅ Implement IP Safelists and Blocklists
Use an HTTP client that allows customizing and blocking specific IP ranges, such as private network addresses and other non-routable IP ranges.
✅ Use Network-Level Security
If the HTTP client doesn't support IP range blocking, consider running it with restricted system permissions, or within a secure network where firewall rules can block dangerous addresses.
✅ Leverage a Secure HTTP Proxy
As a last resort, route all backend HTTP requests through a secure proxy that can filter out and block requests to potentially harmful addresses.
import (
"context"
"crypto/tls"
"errors"
"net"
"net/http"
"time"
)
// IsDisallowedIP checks if an IP address falls within a range of disallowed IPs.
func IsDisallowedIP(hostIP string) bool {
ip := net.ParseIP(hostIP)
// Add more checks as necessary
return ip.IsMulticast() || ip.IsUnspecified() || ip.IsLoopback() || ip.IsPrivate()
}
// SafeTransport defines a custom transport that filters out disallowed IP addresses.
func SafeTransport(timeout time.Duration) *http.Transport {
return &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
c, err := net.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())
if IsDisallowedIP(ip) {
c.Close()
return nil, errors.New("ip address is not allowed")
}
return c, err
},
DialTLS: func(network, addr string) (net.Conn, error) {
dialer := &net.Dialer{Timeout: timeout}
c, err := tls.DialWithDialer(dialer, network, addr, &tls.Config{})
if err != nil {
return nil, err
}
ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())
if IsDisallowedIP(ip) {
c.Close()
return nil, errors.New("ip address is not allowed")
}
return c, c.Handshake()
},
TLSHandshakeTimeout: timeout,
}
}
// httpRequest performs a secure HTTP request, filtering out disallowed IPs.
func httpRequest(requestUrl string) {
const clientConnectTimeout = time.Second * 10
httpClient := &http.Client{
Transport: SafeTransport(clientConnectTimeout),
}
resp, err := httpClient.Get(requestUrl)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// Process response
}
Resources
Associated CWE
OWASP Top 10
Configuration
To skip this rule during a scan, use the following flag
bearer scan /path/to/your-project/ --skip-rule=go_gosec_injection_ssrf_injection
To run only this rule during a scan, use the following flag
bearer scan /path/to/your-project/ --only-rule=go_gosec_injection_ssrf_injection
Ready to take the next step? Learn more about Bearer Cloud.