Around Go: service security · 3 MIN READ
SSRF: let user input choose data, not an arbitrary network destination
Keep outbound destinations controlled and validate redirects and connection-time policy.
A URL supplied by a user can target internal services, metadata endpoints or a different host after redirection. Checking that the string begins with https is insufficient. Prefer a small configured set of upstream services and let user input select a resource within one of them. When arbitrary destinations are a real requirement, enforce address and egress policy at connection time as well as during URL parsing.
security · A MAP OF THE IDEA
- Choose configured upstream
- Encode user data
- Enforce redirect + dial policy
- Bound response and time
Useful when
- Keep outbound destinations controlled and validate redirects and connection-time policy.
- Makes the owner and the failure boundary explicit.
Watch out
- A domain allowlist does not protect a compromised allowed service from returning hostile data, and a URL parser does not establish permission.
- Run native service and failure tests in your own development environment.
Implementation & operation cost: Additional state and tests must be justified by the demonstrated failure; this lesson is not a requirement for every Go program.
The contract and the failure
A URL supplied by a user can target internal services, metadata endpoints or a different host after redirection. Checking that the string begins with https is insufficient. Prefer a small configured set of upstream services and let user input select a resource within one of them. When arbitrary destinations are a real requirement, enforce address and egress policy at connection time as well as during URL parsing.
Work through the example
Use an owned http.Client with explicit time and response-size budgets. Disable automatic redirects or revalidate every hop under the same policy. Consider userinfo, ports, IP literals, IPv6, alternate encodings and DNS resolution/rebinding. A reviewed dialer or infrastructure egress control must enforce forbidden internal ranges against the actual connection target; validating a hostname once before a later independent DNS lookup is not enough.
package main
import (
"fmt"
"net/url"
)
func profileURL(id string) string {
base := url.URL{Scheme: "https", Host: "api.vendor.example", Path: "/profiles"}
q := url.Values{}
q.Set("id", id)
base.RawQuery = q.Encode()
return base.String()
}
func main() {
fmt.Println(profileURL("http://127.0.0.1/admin"))
// The untrusted value is an encoded query value, never the destination host.
}The attacker-shaped string stays inside a query parameter on the configured example domain. The code does not send a request and does not claim the upstream application is safe from injection. Its narrower guarantee is that this input cannot select the network host or scheme.
Production decisions
A domain allowlist does not protect a compromised allowed service from returning hostile data, and a URL parser does not establish permission. Do not follow a redirect into a forbidden host or log upstream credentials in the URL. Test fixed-destination behavior, hostile resource IDs, redirects, private addresses, changed DNS answers and decompressed response limits in an isolated native environment.
Explain it in an interview
An allowed public URL redirects to a private address. May a client follow it without rechecking policy? No. Each redirect and actual connection must satisfy the egress policy; fixed configured destinations reduce the attack surface. Describe the state before the operation, its owner, the failure between steps and the evidence you would test. Distinguish what this example demonstrates from the infrastructure a production service still needs.
TAKE THIS WITH YOU
Keep outbound destinations controlled and validate redirects and connection-time policy. No. Each redirect and actual connection must satisfy the egress policy; fixed configured destinations reduce the attack surface.
Check your understanding
An allowed public URL redirects to a private address. May a client follow it without rechecking policy?
Go to the source
Progress stays in this browser. No account needed.