K
Kodama Vault
knowledge hub
Vault
HomeBoardMap of ContentChatConversasAuditoria
Agentes
AgentsIssuesCriar IssueTerminalPreviews
Sistema
MCPSetup MCPSettings
Brain
amazon-arb-scoutcode-standards-auditordesign-master (subagent)erica-nardi-auditorfeature-auditorGlobal agent instructionskodama-hub-auditorAgente: kodama-hub-launch-qalanding-page-architect (subagent spec)meta-campaign-builder (subagent spec)need-context-auditorprospek-blog-auditor — gate editorial do blog do Prospekprospek-blog-author — autor do blog do Prospekprospek-campaign-manager — gerente de campanhas do Prospekprospek-content-director — diretor de conteúdo diário do blogProspek Demo RecorderSubagent — prospek-marketing-creativeprospek-qaprospek-social-producerprospek-social-publisherprospek-social-strategistroblox-sim-buildersageland-auditor (subagent spec)seo-geo-optimizerteam-leadervek1-auditor — subagent specvek1-styleguide-auditor
Análise custos migração — evitar senha no payloadLevantamento fluxo registro + duplicados StripeRelatório segurança + pentes finos (Cláudio)Revisão security concerns e race conditionsMagic link / esqueceu senha via SupabaseCorrigir erros pós-upgrade TypeScriptTestar PRs do agente Vault para mergeAnálise de 3 issues para iniciarErro no terminal do VSCodePR #173 — aguardando aprovação do LeoTestar fluxo ponta a ponta — criação de clients no StripePR #172 — testar e subir correção de funções deprecatedPitch de vendas SaaS — agendar call de conversãoOrganizar issues e bugs rápidos para a semanaMerge PR cadastro-novo — funcionalidades e correçõesCorrigir bugs PR #173 e #172 — image domainsPR mesosóico — página de acesso mobile + segurança OTPRefatoração de códigos — PR #202Ajustes em PRs abertos de ontemEstudo de jornada de compra e técnicas de fechamentoDefinir preço e entregável do produtoProspecção de reuniões para esta semanaAgente anti AI slop — centralização de conhecimento ConnfitPR #179 — resolver conflitos e erros de teste CLIAlinhamento de preços e usos da ConffitFix adicional para PR #183 — perfil do usuárioCorrigir estilização da Connfit para identidade visualSubir modificações no copy da ConnfitCriação de 4 campanhas no Meta AdsRevisão de PRs do GilinesExploração do Roblox EditorRelatório João — devolutiva TikTok ShopReunião presencial Zassi Uniformes — diagnóstico automaçõesCriar repositório de diagnósticos e relatórios de entrevistasDiagnóstico da ZassiGeração de relatórios para reuniões de fechamentoProposta Zassi — apresentação amanhãProspecção — Clínica Odontológica Dr. ButAlinhamento com ADRIANO sobre produtos e simulaçãoCombinar com Lauro os produtos do diagnósticoSolicitar recursos (vbucks) à INEDIA/ObiettoAnálise de issues do Kodama-Hub e início pelo vaultIssue KH03 — estudo de abordagem DockerKH-12 — script de correção e PR no kodama-hubTeste de despacho e agentes da vaultRemover issues 7, 9 e 10 do fluxo de trabalhoKH-15 — testar e preparar para Gilini testar em prod (Kodama Hub)VEK-1 — testar no WhatsAppBot local — testar localmenteEscrever issues para replicação do modelo de LPSwarm — modelar landing pages para tecnologias concorrentes (Google Ads)Configurar Docker no Windows para tarefas do Hub LisaLP de Suplementos — iniciar issue #96 (Vek)PR #93 git — subir para testar em prodPR #94 git — despachar agents pelo vaultTestes e documentação de bugs no site VEKPR #98 de LP — cosméticosRemover issues concluídas do board (#5, #10, #11, #12, #13)PRs de comparação vek1 vs LPs — correções e mergeDocumentação de uso e bugs na Vek1Criação de issues via Vault — bugs VekFix bug redirect botão Produtos na sidebar colapsada (vek)Planejamento de issues e mini sprint no site da Vek1facilitabusca — cron de fetch parado desde 05/08 (RESOLVIDO 11/08)
kodama-watchdog — self-heal + alerta pra todos os projetos da VPS HermesVPS Hermes — acesso e estrutura
Memory namespacing (multi-user)
OpenSpec -- Spec-Driven Development no VaultPlano de Teste — OpenSpec Vault Persistence
CaumzitoNyxzZanini
Amazon Arb (atacado→varejo BR)
Claude Code — Setup MCP VaultClaude Desktop — Setup MCP Vault (remote)VS Code + Copilot — Setup MCP Vault
Skill — Carousel Designer (Paper Style)carousel-paperPlugin marketing-skills (coreyhaines31/marketingskills)
Standup 2026-05-14Standup 2026-05-15Standup 2026-05-16Standup 2026-05-17Standup 2026-05-18Standup 2026-05-19Standup 2026-05-20Standup 2026-05-21Standup 2026-05-22Standup 2026-05-25Standup 2026-05-26Standup 2026-05-27Standup 2026-05-28Standup 2026-05-29Standup 2026-06-01Standup 2026-06-02Standup 2026-06-03Standup 2026-06-05Standup 2026-06-11Standup 2026-06-15Standup 2026-06-16Standup 2026-06-17Standup 2026-06-18Standup 2026-06-22Standup 2026-06-23Standup 2026-06-29Standup 2026-06-30Standup 2026-07-01Standup 2026-07-02Standup 2026-07-03Standup 2026-07-06Standup 2026-07-07Standup 2026-07-08Standup 2026-07-09Standup 2026-07-10Standup 2026-07-13Standup 2026-07-14Standup 2026-07-15Standup 2026-07-16Standup 2026-07-17Standup 2026-07-21Standup 2026-07-22Standup 2026-07-23Standup 2026-07-28Standup 2026-07-29Standup 2026-07-30Standup 2026-07-31Standup 2026-08-03Standup 2026-08-06Standup 2026-08-07Standup 2026-08-10Standup 2026-08-11Standup 2026-08-12Standups
MOCStandup 2026 07 23Welcome
v0.3
K
Kodama Vault
brain / projects / vek1 / skills

Server-Side Request Forgery (SSRF) Prevention Reference

Server-Side Request Forgery (SSRF) Prevention Reference

Overview

SSRF vulnerabilities allow attackers to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker's choosing. This can be used to:

  • Access internal services not exposed to the internet
  • Read cloud metadata (AWS, GCP, Azure credentials)
  • Scan internal networks
  • Bypass firewalls and access controls
  • Exploit internal services with known vulnerabilities

Attack Scenarios

Cloud Metadata Access (AWS)

# Attacker provides URL:
http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name

# Server fetches and returns AWS credentials:
{
  "AccessKeyId": "ASIA...",
  "SecretAccessKey": "...",
  "Token": "..."
}

Internal Service Access

# Attacker provides URL:
http://localhost:8080/admin/delete-all
http://internal-service.local/sensitive-data

# Server makes request to internal service that trusts localhost

Port Scanning

# Attacker probes internal network:
http://192.168.1.1:22    # SSH
http://192.168.1.1:3306  # MySQL
http://192.168.1.1:6379  # Redis

Prevention Strategies

1. Input Validation (Allowlist)

Preferred when target hosts are known.

# VULNERABLE: No validation
def fetch_url(url):
    return requests.get(url).content

# SAFE: Allowlist of permitted domains
ALLOWED_DOMAINS = {'api.example.com', 'cdn.example.com'}

def fetch_url(url):
    parsed = urlparse(url)

    # Validate scheme
    if parsed.scheme not in ('http', 'https'):
        raise ValueError("Invalid URL scheme")

    # Validate domain against allowlist
    if parsed.hostname not in ALLOWED_DOMAINS:
        raise ValueError("Domain not allowed")

    return requests.get(url).content

2. Block Internal Networks (Denylist)

Additional defense layer when allowlist isn't practical.

import ipaddress
import socket

BLOCKED_RANGES = [
    ipaddress.ip_network('127.0.0.0/8'),      # Loopback
    ipaddress.ip_network('10.0.0.0/8'),       # Private
    ipaddress.ip_network('172.16.0.0/12'),    # Private
    ipaddress.ip_network('192.168.0.0/16'),   # Private
    ipaddress.ip_network('169.254.0.0/16'),   # Link-local (metadata)
    ipaddress.ip_network('0.0.0.0/8'),        # Current network
    ipaddress.ip_network('100.64.0.0/10'),    # Shared address space
    ipaddress.ip_network('192.0.0.0/24'),     # IETF Protocol
    ipaddress.ip_network('192.0.2.0/24'),     # Documentation
    ipaddress.ip_network('198.51.100.0/24'),  # Documentation
    ipaddress.ip_network('203.0.113.0/24'),   # Documentation
    ipaddress.ip_network('224.0.0.0/4'),      # Multicast
    ipaddress.ip_network('240.0.0.0/4'),      # Reserved
]

def is_internal_ip(ip_str):
    try:
        ip = ipaddress.ip_address(ip_str)
        return any(ip in network for network in BLOCKED_RANGES)
    except ValueError:
        return True  # Invalid IP, block it

def validate_url(url):
    parsed = urlparse(url)

    # Validate scheme
    if parsed.scheme not in ('http', 'https'):
        raise ValueError("Invalid URL scheme")

    # Resolve hostname to IP
    hostname = parsed.hostname
    if not hostname:
        raise ValueError("Invalid URL")

    # Check for IP address directly in URL
    try:
        ip = ipaddress.ip_address(hostname)
        if is_internal_ip(str(ip)):
            raise ValueError("Internal IP addresses not allowed")
    except ValueError:
        # It's a hostname, resolve it
        try:
            ip = socket.gethostbyname(hostname)
            if is_internal_ip(ip):
                raise ValueError("Domain resolves to internal IP")
        except socket.gaierror:
            raise ValueError("Could not resolve hostname")

    return True

3. Disable Redirects

# VULNERABLE: Follows redirects (can bypass IP checks)
response = requests.get(url, allow_redirects=True)
# Attacker: http://attacker.com/redirect -> http://169.254.169.254/

# SAFE: Don't follow redirects automatically
response = requests.get(url, allow_redirects=False)

# If redirects needed, validate each location
def safe_fetch(url, max_redirects=5):
    for _ in range(max_redirects):
        validate_url(url)  # Validate before each request
        response = requests.get(url, allow_redirects=False)

        if response.status_code in (301, 302, 303, 307, 308):
            url = response.headers.get('Location')
            if not url:
                raise ValueError("Redirect without Location")
            continue

        return response

    raise ValueError("Too many redirects")

4. DNS Rebinding Protection

import socket
import time

def safe_fetch_with_dns_pinning(url):
    parsed = urlparse(url)
    hostname = parsed.hostname

    # Resolve DNS and pin the IP
    ip = socket.gethostbyname(hostname)

    # Validate IP is not internal
    if is_internal_ip(ip):
        raise ValueError("Internal IP not allowed")

    # Make request directly to IP with Host header
    # This prevents DNS rebinding attacks
    modified_url = url.replace(hostname, ip)
    headers = {'Host': hostname}

    response = requests.get(
        modified_url,
        headers=headers,
        allow_redirects=False,
        verify=True  # Still verify TLS with original hostname
    )

    return response

5. Cloud Metadata Protection

AWS IMDSv2

# Require IMDSv2 (token-based) - mitigates SSRF
aws ec2 modify-instance-metadata-options \
    --instance-id i-1234567890abcdef0 \
    --http-tokens required \
    --http-endpoint enabled
# With IMDSv2, attacker would need two requests:
# 1. PUT to get token (SSRF usually only does GET)
# 2. GET with token in header

# Block metadata IP regardless
if '169.254.169.254' in url or '169.254.170.2' in url:
    raise ValueError("Metadata endpoints not allowed")

GCP

# Block GCP metadata
BLOCKED_HOSTS = [
    'metadata.google.internal',
    'metadata.google.com',
    '169.254.169.254'
]

Azure

# Block Azure metadata
BLOCKED_HOSTS = [
    '169.254.169.254',
    'management.azure.com'
]

Framework-Specific Mitigations

Python (requests)

from urllib.parse import urlparse
import requests

class SafeRequests:
    @staticmethod
    def get(url, **kwargs):
        validate_url(url)
        kwargs['allow_redirects'] = False
        kwargs['timeout'] = (5, 30)  # Connect and read timeout
        return requests.get(url, **kwargs)

Node.js

const dns = require('dns').promises;

async function safeFetch(targetUrl) {
    const parsed = new URL(targetUrl);

    // Validate scheme
    if (!['http:', 'https:'].includes(parsed.protocol)) {
        throw new Error('Invalid scheme');
    }

    // Resolve and check IP
    const addresses = await dns.lookup(parsed.hostname);
    if (isInternalIP(addresses.address)) {
        throw new Error('Internal IP not allowed');
    }

    return fetch(targetUrl, {
        redirect: 'error',
        signal: AbortSignal.timeout(30000)
    });
}

Java

public class SafeURLConnection {
    private static final Set<String> ALLOWED_PROTOCOLS = Set.of("http", "https");

    public static URLConnection openConnection(String urlString) throws IOException {
        URL url = new URL(urlString);

        if (!ALLOWED_PROTOCOLS.contains(url.getProtocol())) {
            throw new SecurityException("Protocol not allowed");
        }

        InetAddress address = InetAddress.getByName(url.getHost());
        if (isInternalIP(address)) {
            throw new SecurityException("Internal IP not allowed");
        }

        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setInstanceFollowRedirects(false);
        connection.setConnectTimeout(5000);
        connection.setReadTimeout(30000);

        return connection;
    }
}

Common Bypass Techniques to Block

URL Encoding

# Bypasses:
http://169.254.169.254/  # Normal
http://169%2e254%2e169%2e254/  # URL encoded dots
http://0251.0376.0251.0376/  # Octal
http://0xa9fea9fe/  # Hex
http://2852039166/  # Decimal

# Defense: Normalize and decode URL before validation
from urllib.parse import unquote

def normalize_url(url):
    return unquote(url)

DNS Rebinding

# Attack: Domain initially resolves to public IP, then internal IP
# First request: attacker.com -> 1.2.3.4 (passes validation)
# DNS changes: attacker.com -> 192.168.1.1
# Second request goes to internal IP

# Defense: Pin DNS resolution and re-validate

IPv6

# Bypasses:
http://[::1]/  # localhost
http://[::ffff:127.0.0.1]/  # IPv4-mapped IPv6
http://[0:0:0:0:0:ffff:169.254.169.254]/

# Defense: Check both IPv4 and IPv6 ranges
BLOCKED_RANGES.extend([
    ipaddress.ip_network('::1/128'),        # IPv6 loopback
    ipaddress.ip_network('fc00::/7'),       # IPv6 private
    ipaddress.ip_network('fe80::/10'),      # IPv6 link-local
])

Alternate Representations

# localhost alternatives:
localhost
127.0.0.1
127.0.0.2  # Any 127.x.x.x is loopback
2130706433  # Decimal for 127.0.0.1
0x7f000001  # Hex
0177.0.0.1  # Octal
127.1       # Short form

Grep Patterns for Detection

# URL fetching functions
grep -rn "requests\.get\|requests\.post\|urllib\.request\|urlopen\|fetch\|axios" --include="*.py" --include="*.js"

# URL from user input
grep -rn "request\.args\|request\.form\|request\.json\|req\.query\|req\.body" --include="*.py" --include="*.js" | grep -i "url"

# Potential SSRF sinks
grep -rn "curl_exec\|file_get_contents\|fopen\|readfile" --include="*.php"

# Missing validation
grep -rn "requests\.get(url\|fetch(url" --include="*.py" --include="*.js"

Testing Checklist

  • User-controlled URLs validated against allowlist
  • Internal IP ranges blocked (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
  • Cloud metadata IPs blocked (169.254.169.254)
  • IPv6 internal addresses blocked
  • URL redirects not followed blindly
  • DNS rebinding protected against
  • URL encoding/alternate representations handled
  • IMDSv2 required (AWS environments)
  • Timeouts configured to prevent DoS

References

  • OWASP SSRF Prevention Cheat Sheet
  • CWE-918: Server-Side Request Forgery
  • AWS IMDSv2 Documentation
  • PortSwigger SSRF Guide
notas relacionadas
carregando…