# GO-SSRF-001: Server-Side Request Forgery via go-resty HTTP Client

> **Severity:** HIGH | **CWE:** CWE-918 | **OWASP:** A10:2021

- **Language:** Go
- **Category:** Security
- **URL:** https://codepathfinder.dev/registry/golang/security/GO-SSRF-001
- **Detection:** `pathfinder scan --ruleset golang/GO-SSRF-001 --project .`

## Description

Server-Side Request Forgery (SSRF) occurs when an attacker can control the URL for an
outbound HTTP request made by the server. The application acts as a proxy, sending
requests to attacker-specified destinations that may be unreachable directly.

**go-resty** (github.com/go-resty/resty) is a popular HTTP client library for Go with
a fluent API. When user-controlled input reaches resty's URL parameters (R.Get(), R.Post(),
R.SetURL(), etc.) without validation, SSRF vulnerabilities arise.

**Cloud metadata endpoint attacks** (most critical impact):
- "**AWS IMDSv1** (169.254.169.254): `GET /latest/meta-data/iam/security-credentials/<role>`"
  returns temporary AWS credentials (AccessKeyId, SecretAccessKey, Token) in JSON.
  These credentials have the IAM permissions of the EC2 instance role — potentially
  S3 read/write, RDS access, Lambda invocation, etc. IMDSv1 requires no session token
  and is accessible from any process on the instance, including SSRF payloads.
- "**AWS IMDSv2**: Requires a PUT request first to obtain a session token, then GET"
  requests must include `X-aws-ec2-metadata-token`. Mitigates SSRF but is not universal
  — many older deployments still use IMDSv1.
- "**GCP metadata**: `http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token`"
  returns an OAuth2 access token with the instance's service account permissions.
  Requires `Metadata-Flavor: Google` header — but many SSRF tools set this automatically.
- "**Azure IMDS**: `http://169.254.169.254/metadata/instance?api-version=2021-02-01`"
  (requires `Metadata: true` header). Returns instance metadata and managed identity tokens.

**URL bypass techniques**: Naive host-string matching is bypassed by:
- "Decimal IP: `http://2130706433` = 127.0.0.1"
- "IPv6: `http://[::1]`, `http://[0:0:0:0:0:ffff:7f00:1]`"
- "IPv6-mapped IPv4: `http://[::ffff:169.254.169.254]`"
- "URL encoding: `http://169.254.169.254%2F` or `http://169.254.169%2e254`"
- "DNS rebinding: hostname resolves to external IP first (passes check), then attacker's"
  DNS returns 169.254.169.254 before the actual HTTP request — bypasses IP allowlisting
  based on DNS resolution at validation time.

The correct defense is to validate URLs by resolving them first and checking the
**resolved IP address** against a blocklist of private ranges, not just the URL string.


## Vulnerable Code

```python
# --- file: vulnerable.go ---
// GO-SSRF-001 positive test cases — all SHOULD be detected
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func ssrfViaGet(c *gin.Context) {
	target := c.Query("url")             // source
	http.Get(target)                     // SINK: user controls outbound URL
}

func ssrfViaFormValue(w http.ResponseWriter, r *http.Request) {
	target := r.FormValue("target")      // source
	http.Get(target)                     // SINK: SSRF
}

func ssrfViaPost(c *gin.Context) {
	endpoint := c.Query("endpoint")      // source
	http.Post(endpoint, "application/json", nil) // SINK
}

func ssrfViaQueryParam(w http.ResponseWriter, r *http.Request) {
	host := r.FormValue("host")
	client := &http.Client{}
	req, _ := http.NewRequest("GET", "https://"+host+"/api", nil) // SINK
	client.Do(req)
}

# --- file: go.mod ---
module example.com/go-ssrf-001/positive

go 1.25.0

require github.com/gin-gonic/gin v1.12.0

require (
	github.com/bytedance/gopkg v0.1.3 // indirect
	github.com/bytedance/sonic v1.15.0 // indirect
	github.com/bytedance/sonic/loader v0.5.0 // indirect
	github.com/cloudwego/base64x v0.1.6 // indirect
	github.com/gabriel-vasile/mimetype v1.4.12 // indirect
	github.com/gin-contrib/sse v1.1.0 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.30.1 // indirect
	github.com/goccy/go-json v0.10.5 // indirect
	github.com/goccy/go-yaml v1.19.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.3.0 // indirect
	github.com/leodido/go-urn v1.4.0 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.2.4 // indirect
	github.com/quic-go/qpack v0.6.0 // indirect
	github.com/quic-go/quic-go v0.59.0 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.3.1 // indirect
	go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
	golang.org/x/arch v0.22.0 // indirect
	golang.org/x/crypto v0.48.0 // indirect
	golang.org/x/net v0.51.0 // indirect
	golang.org/x/sys v0.41.0 // indirect
	golang.org/x/text v0.34.0 // indirect
	google.golang.org/protobuf v1.36.10 // indirect
)

# --- file: go.sum ---
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
```

## Secure Code

```python
// SECURE: allowlist validation + resolved IP check
import (
    "net"
    "net/url"
    "strings"
)

var allowedHosts = map[string]bool{
    "api.partner.com": true,
    "cdn.example.com": true,
}

// Block private/metadata IP ranges AFTER DNS resolution (prevents DNS rebinding)
func isPrivateIP(ip net.IP) bool {
    privateRanges := []string{
        "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16",
        "127.0.0.0/8", "169.254.0.0/16", "::1/128",
        "fc00::/7", "fe80::/10",
    }
    for _, cidr := range privateRanges {
        _, network, _ := net.ParseCIDR(cidr)
        if network.Contains(ip) {
            return true
        }
    }
    return false
}

func validateURL(rawURL string) error {
    parsed, err := url.Parse(rawURL)
    if err != nil {
        return fmt.Errorf("invalid URL")
    }
    if parsed.Scheme != "https" {
        return fmt.Errorf("only HTTPS allowed")
    }
    if !allowedHosts[parsed.Hostname()] {
        return fmt.Errorf("host not in allowlist")
    }
    // Resolve IP and check it's not private (prevents DNS rebinding)
    addrs, err := net.LookupHost(parsed.Hostname())
    if err != nil || len(addrs) == 0 {
        return fmt.Errorf("DNS resolution failed")
    }
    for _, addr := range addrs {
        if isPrivateIP(net.ParseIP(addr)) {
            return fmt.Errorf("resolved IP is in private range")
        }
    }
    return nil
}

func proxyHandler(c *gin.Context) {
    target := c.Query("url")
    if err := validateURL(target); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    client := resty.New()
    resp, err := client.R().Get(target)
    // ...
}

```

## Detection Rule (Python SDK)

```python
"""GO-SSRF-001: Server-Side Request Forgery via user-controlled URLs in HTTP client calls."""

from codepathfinder.go_rule import (
    GoGinContext,
    GoHTTPRequest,
    GoHTTPClient,
    GoRestyClient,
    GoEchoContext,
    GoFiberCtx,
    QueryType,
)
from codepathfinder import flows
from codepathfinder.presets import PropagationPresets
from codepathfinder.go_decorators import go_rule


@go_rule(
    id="GO-SSRF-001",
    severity="HIGH",
    cwe="CWE-918",
    owasp="A10:2021",
    tags="go,security,ssrf,http-client,CWE-918,OWASP-A10",
    message=(
        "User-controlled input flows into an HTTP client method (http.Get, http.Post, "
        "resty.Get, etc.). This creates a Server-Side Request Forgery (SSRF) vulnerability — "
        "attackers can make the server issue requests to internal services, cloud metadata "
        "endpoints (169.254.169.254), or other unintended destinations. "
        "Validate URLs against an explicit allowlist before making outbound requests."
    ),
)
def detect_ssrf():
    """Detect SSRF via user-controlled URLs in HTTP client calls."""
    return flows(
        from_sources=[
            GoGinContext.method("Query", "Param", "PostForm", "GetHeader", "GetRawData"),
            GoEchoContext.method("QueryParam", "FormValue", "Param"),
            GoFiberCtx.method("Params", "Query", "FormValue", "Get"),
            GoHTTPRequest.method("FormValue", "PostFormValue"),
            GoHTTPRequest.attr("URL.RawQuery", "URL.Path", "Body"),
        ],
        to_sinks=[
            GoHTTPClient.method("Get", "Post", "Do", "Head"),
            GoRestyClient.method("Get", "Post", "Put", "Delete", "SetBaseURL"),
        ],
        propagates_through=PropagationPresets.standard(),
        scope="global",
    )
```

## How to Fix

- Validate outbound URLs against an explicit allowlist of permitted hosts.
- After DNS resolution, verify the resolved IP is not in private ranges (10/8, 172.16/12, 192.168/16, 127/8, 169.254/16) to prevent DNS rebinding bypass.
- Block cloud metadata endpoints: 169.254.169.254 and metadata.google.internal.
- Enforce HTTPS-only for outbound requests in security-sensitive contexts.
- Disable HTTP redirects in the HTTP client or re-validate URLs after each redirect.
- Enable AWS IMDSv2 (instance metadata service v2) to require session tokens.
- Apply network-level egress filtering as defense-in-depth.

## Security Implications

- **Cloud IAM Credential Theft (AWS/GCP/Azure):** SSRF to 169.254.169.254 retrieves cloud instance metadata including temporary IAM
credentials. An attacker with these credentials can access AWS S3, invoke Lambda,
read RDS databases, or perform any action the instance role permits — often providing
full account access through privilege escalation via IAM.

- **Internal Service Enumeration:** The server can probe internal IPs and ports not accessible from the internet.
Responses (HTTP status codes, error messages, content length) reveal which services
are running, their versions, and internal network topology.

- **Internal API Exploitation:** Internal services often have weaker authentication assumptions ("only internal
clients reach this"). SSRF provides access to admin APIs, monitoring endpoints,
and service-to-service APIs without authentication.

- **Kubernetes Service Account Token Theft:** In Kubernetes environments, SSRF to the Kubernetes API server or the metadata
service can retrieve service account tokens. These tokens may have cluster-admin
permissions or access to secrets across namespaces.


## References

- [CWE-918: Server-Side Request Forgery — MITRE](https://cwe.mitre.org/data/definitions/918.html)
- [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
- [OWASP Top 10 A10:2021 — SSRF](https://owasp.org/Top10/A10_2021-Server-Side_Request_Forgery_%28SSRF%29/)
- [AWS IMDSv1 vs IMDSv2 — AWS documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html)
- [GCP Instance Metadata — Cloud documentation](https://cloud.google.com/compute/docs/metadata/overview)
- [go-resty documentation](https://github.com/go-resty/resty)
- [RFC 3986 — Uniform Resource Identifier (URI)](https://www.rfc-editor.org/rfc/rfc3986)

---

Source: https://codepathfinder.dev/registry/golang/security/GO-SSRF-001
Code Pathfinder — Open source, type-aware SAST with cross-file dataflow analysis
