flawopen.com/ssrf/Swift
How URLSession allows internal IP queries in Swift/Vapor, and how to enforce IP boundaries.
कल्पना करें कि आप एक सहायक को सार्वजनिक दुकान से पार्सल लाने के लिए भेजते हैं, लेकिन उसे प्रबंधक के कार्यालय में रखी बंद तिजोरी का पता देते हैं। चूंकि सहायक के पास आंतरिक सुरक्षा बैज है, वह तिजोरी खोलकर कंपनी के रहस्य आपको सौंप देता है।
Web Application SecurityCWE-918.CWE-918CWE-918): Standard Common Weakness Enumeration classification for ssrf-swift.Defense-in-Depthएप्लिकेशन इमेज फ़ेचिंग, वेबहुक या पीडीएफ़ जनरेशन के लिए उपयोगकर्ता द्वारा दिया गया URL स्वीकार करता है।
हमलावर क्लाउड मेटाडेटा सेवा (उदा. http://169.254.169.254/latest/meta-data/) या लूपबैक एड्रेस इनपुट करता है।
सर्वर बिना आईपी सत्यापन के आंतरिक वीपीसी नेटवर्क से अनुरोध भेज देता है।
आंतरिक मेटाडेटा सेवा सर्वर के अनुरोध पर विश्वास करके अस्थायी आईएएम टोकन और गुप्त कुंजियाँ लौटा देती है।
// VULNERABLE: Unchecked reqwest::get
use reqwest;
async fn fetch_remote_resource(user_url: &str) -> Result<Vec<u8>, reqwest::Error> {
// Attacker passes: http://169.254.169.254/latest/meta-data/
let resp = reqwest::get(user_url).await?;
let bytes = resp.bytes().await?;
Ok(bytes.to_vec())
}
// HARDENED: Resolve DNS, assert non-private IP, and disable redirects
use std::net::{IpAddr, ToSocketAddrs};
use reqwest::redirect::Policy;
use url::Url;
fn is_safe_ip(ip: IpAddr) -> bool {
match ip {
IpAddr::V4(ipv4) => {
!ipv4.is_loopback() &&
!ipv4.is_private() &&
!ipv4.is_link_local() &&
!ipv4.is_unspecified()
}
IpAddr::V6(ipv6) => {
!ipv6.is_loopback() &&
!ipv6.is_unspecified()
}
}
}
async fn fetch_remote_resource(user_url: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let parsed_url = Url::parse(user_url)?;
if parsed_url.scheme() != "http" && parsed_url.scheme() != "https" {
return Err("Invalid URL scheme".into());
}
let host = parsed_url.host_str().ok_or("Missing host")?;
let port = parsed_url.port_or_known_default().unwrap_or(80);
// 1. Resolve host and inspect all IP addresses
let socket_addrs: Vec<_> = format!("{}:{}", host, port).to_socket_addrs()?.collect();
for sa in &socket_addrs {
if !is_safe_ip(sa.ip()) {
return Err("Access to private/internal IP rejected".into());
}
}
// 2. Build client with redirects disabled
let client = reqwest::Client::builder()
.redirect(Policy::none())
.timeout(std::time::Duration::from_secs(3))
.build()?;
let resp = client.get(parsed_url).send().await?;
let bytes = resp.bytes().await?;
Ok(bytes.to_vec())
}
ip.is_private() && !ip.is_link_local()।none() on reqwest Client को सही ढंग से कॉन्फ़िगर करें।