베타 Shoulder는 베타 버전입니다 — 결과가 가끔 잘못될 수 있습니다. 여러분의 피드백이 다음에 무엇을 고칠지 결정합니다. 피드백 공유
🌐

Server-Side Request Forgery (SSRF)

🛡️ 4 개의 규칙이 이를 탐지합니다

Server-Side Request Forgery (SSRF)

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.

By providing URLs to unexpected hosts or ports, attackers can make it appear that the server is sending the request, possibly bypassing access controls such as firewalls.

보급률
보통
3개 언어 지원
영향
높음
4개의 높은 심각도 규칙
예방
문서화됨
4개의 수정 예시
2 예방
2 예방

이 취약점을 수정하는 방법

4개의 Shoulder 탐지 규칙을 기반으로 한 Server-Side Request Forgery 예방 전략.

Server-Side Request Forgery (SSRF) HIGH

Parse URL and validate host against domain allowlist

+55 -11 go
  package main
  
  import (
      "io"
-     "net/http"
- )
- 
- func handler(w http.ResponseWriter, r *http.Request) {
-     targetURL := r.URL.Query().Get("url")
-     // Vulnerable: user-controlled URL
-     resp, err := http.Get(targetURL)
-     if err != nil {
-         http.Error(w, err.Error(), 500)
-         return
-     }
+     "net"
+     "net/http"
+     "net/url"
+ )
+ 
+ var allowedDomains = map[string]bool{
+     "api.example.com": true,
+     "cdn.example.com": true,
+ }
+ 
+ func isPrivateIP(ip net.IP) bool {
+     private := []string{
+         "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16",
+         "169.254.0.0/16", "127.0.0.0/8",
+     }
+     for _, cidr := range private {
+         _, network, _ := net.ParseCIDR(cidr)
+         if network.Contains(ip) {
+             return true
+         }
+     }
+     return false
+ }
+ 
+ func handler(w http.ResponseWriter, r *http.Request) {
+     targetURL := r.URL.Query().Get("url")
+ 
+     parsed, err := url.Parse(targetURL)
+     if err != nil {
+         http.Error(w, "Invalid URL", 400)
+         return
+     }
+ 
+     // Validate scheme
+     if parsed.Scheme != "https" {
+         http.Error(w, "HTTPS required", 400)
+         return
+     }
+ 
+     // Validate domain
+     if !allowedDomains[parsed.Host] {
+         http.Error(w, "Domain not allowed", 403)
+         return
+     }
+ 
+     // Block private IPs (DNS rebinding protection)
+     ips, _ := net.LookupIP(parsed.Hostname())
+     for _, ip := range ips {
+         if isPrivateIP(ip) {
+             http.Error(w, "Private IP not allowed", 403)
+             return
+         }
+     }
+ 
+     resp, _ := http.Get(targetURL)
      defer resp.Body.Close()
      io.Copy(w, resp.Body)
  }
  
SSRF in Next.js Server Actions HIGH

Validate URLs against an allowlist of permitted domains before fetching

+8 -4 javascript
  'use server'
- 
- export async function fetchData(url: string) {
-   const response = await fetch(url);
-   return await response.json();
+ const ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com'];
+ 
+ export async function fetchData(url: string) {
+   const parsed = new URL(url);
+   if (!ALLOWED_DOMAINS.includes(parsed.hostname)) {
+     throw new Error('URL not allowed');
+   }
+   return await fetch(url).then(r => r.json());
  }
  
Server-Side Request Forgery via HTTP Requests HIGH

Validate URLs against domain allowlist before making requests

+25 -5 javascript
  const http = require('http');
- 
- function fetchUrl(url) {
-   // Vulnerable: no URL validation
-   return new Promise((resolve, reject) => {
-     http.get(url, (res) => {
+ const https = require('https');
+ 
+ const ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com'];
+ 
+ function fetchUrl(url) {
+   const parsed = new URL(url);
+ 
+   // Validate protocol
+   if (!['http:', 'https:'].includes(parsed.protocol)) {
+     throw new Error('Invalid protocol');
+   }
+ 
+   // Validate against allowlist
+   if (!ALLOWED_DOMAINS.includes(parsed.hostname)) {
+     throw new Error('Domain not allowed');
+   }
+ 
+   // Block private/internal IPs
+   if (isPrivateIP(parsed.hostname)) {
+     throw new Error('Private IP not allowed');
+   }
+ 
+   const client = parsed.protocol === 'https:' ? https : http;
+   return new Promise((resolve, reject) => {
+     client.get(url, (res) => {
        let data = '';
        res.on('data', chunk => data += chunk);
        res.on('end', () => resolve(data));
      }).on('error', reject);
    });
  }
  
4 경고 신호
4 경고 신호

코드 리뷰에서 주의할 점

이 패턴은 잠재적인 Server-Side Request Forgery (SSRF) 취약점을 나타냅니다. 코드 리뷰와 보안 감사 중에 찾아보세요.

🟠
user input flowing to HTTP client requests, enabling Server-Side Request Forgery attacks go-ssrf
🟠
Server Action '...' has SSRF vulnerability: user input controls HTTP request URL javascript-nextjs-server-action-ssrf
🟠
user-controlled input flowing into HTTP request URLs in Server Actions javascript-nextjs-server-action-ssrf
🟠
user input flowing into HTTP request functions without URL validation javascript-ssrf
🟠
user input controlling URLs in HTTP requests, allowing requests to arbitrary destinations including python-ssrf
🔍

코드베이스를 스캔하세요: Server-Side Request Forgery (SSRF)

Shoulder CLI는 전체 코드베이스에서 취약한 패턴을 찾아냅니다.