Prevent SSRF Attacks: Fortify Your Web Apps Against Server-Side Risks
Server-Side Request Forgery (SSRF) vulnerabilities allow attackers to force your server to make requests to arbitrary domains, including internal networks and cloud metadata services. Mastering SSRF prevention is crucial for robust web application security, protecting against data exfiltration and unauthorized internal access.
Krapton EngineeringReviewed by a senior engineer13 min readSecurity

The digital landscape of 2026 is rife with sophisticated threats, and Server-Side Request Forgery (SSRF) remains a persistent and dangerous vulnerability. Often lurking in seemingly innocuous features like URL previews, image imports, or webhook integrations, SSRF allows attackers to manipulate your server into making requests to internal systems or external services on their behalf. This can lead to critical data breaches, unauthorized access to sensitive internal resources, or even full system compromise.
TL;DR: SSRF vulnerabilities enable attackers to trick your server into accessing internal network resources or sensitive cloud metadata. Effective prevention relies on a layered approach, primarily robust URL validation using strict allowlists, careful IP address resolution, and network segmentation to isolate vulnerable services.
Key takeaways
- SSRF is a Critical Threat: It exploits server-side functionality to access internal networks, cloud metadata, and other sensitive services.
- Allowlists are Paramount: Strictly validate all user-supplied URLs against an explicit list of approved domains and protocols, rather than relying on blocklists.
- Beware of IP Resolution: Always resolve hostnames to IP addresses before validation to prevent DNS rebinding and bypasses.
- Layered Defense is Key: Combine secure coding practices with network segmentation, least privilege, and cloud-specific protections like AWS IMDSv2.
- Regular Audits are Essential: Proactive penetration testing and automated scanning are vital to uncover subtle SSRF bypasses.
What is Server-Side Request Forgery (SSRF)?
Server-Side Request Forgery (SSRF) is a web security vulnerability that allows an attacker to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker's choosing. This means your application, which typically fetches resources from external URLs, can be tricked into fetching resources from internal network locations or other sensitive services.
Why is this so dangerous? Because the requests originate from your server, they bypass client-side firewalls and often have access to resources that an external attacker normally couldn't reach. These resources include internal APIs, databases, cloud metadata services (like AWS EC2 Instance Metadata Service), and other services only accessible from within your private network. The OWASP Top 10 for 2021 lists SSRF as A07, highlighting its critical impact and prevalence in modern applications.
The Impact of a Successful SSRF Attack
- Data Exfiltration: Accessing internal databases or APIs to steal sensitive customer data.
- Internal Service Access: Interacting with administrative interfaces of internal services, potentially leading to privilege escalation.
- Cloud Metadata Exposure: Stealing cloud provider credentials (e.g., AWS IAM role credentials) which can grant full control over cloud resources.
- Port Scanning: Mapping internal network topology by attempting to connect to various ports on internal IP addresses.
- Remote Code Execution (RCE): In some scenarios, an SSRF combined with other vulnerabilities can lead to RCE on the server itself.
How SSRF Attacks Work: A Concrete Example
SSRF vulnerabilities often arise when an application fetches a remote resource without sufficiently validating the user-supplied URL. Consider a common feature: a service that generates a PDF from a given URL or creates a thumbnail of an image hosted elsewhere.
The vulnerable pattern typically looks like this:
// Node.js example - DO NOT USE IN PRODUCTION
const express = require('express');
const axios = require('axios'); // Or fetch, http.get
const app = express();
app.get('/fetch-external-content', async (req, res) => {
const url = req.query.url; // User-supplied URL
if (!url) {
return res.status(400).send('URL parameter is required.');
}
try {
const response = await axios.get(url, { timeout: 5000 });
res.send(`Fetched content type: ${response.headers['content-type']}`);
} catch (error) {
console.error('Error fetching URL:', error.message);
res.status(500).send('Failed to fetch content.');
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
In this simplified Node.js example, if a user sends a request like /fetch-external-content?url=http://example.com/image.jpg, the server fetches the image. However, an attacker could change the URL to something like /fetch-external-content?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/. If the application is running on an AWS EC2 instance, this URL points to the Instance Metadata Service (IMDS), which contains temporary IAM credentials. The server would fetch these credentials and potentially send them back to the attacker, leading to a complete compromise of the AWS account.
Other common targets include:
http://localhost/adminorhttp://127.0.0.1/dashboardto access local services.file:///etc/passwd(though this depends on the underlying fetching mechanism and server OS).- Internal IP addresses like
http://192.168.1.100/internal-api.
The Core of SSRF Prevention: Robust URL Validation
Effective SSRF prevention hinges on rigorously validating any user-supplied URL before the server attempts to fetch it. This is a multi-step process that goes beyond simple regex checks.
Allowlists vs. Blocklists
The fundamental principle of secure design, 'deny by default,' applies strongly here. An allowlist (whitelist) approach specifies exactly which domains, IP addresses, or URL patterns are permitted. Anything not explicitly on the allowlist is rejected. A blocklist (blacklist), conversely, tries to enumerate all known malicious or forbidden patterns. Blocklists are inherently weaker because attackers constantly find new ways to bypass them (e.g., through IP address encodings, redirects, or DNS rebinding).
| Feature | Allowlist (Recommended) | Blocklist (Avoid) |
|---|---|---|
| Security Posture | Deny by default; only explicitly allowed are permitted. | Permit by default; only explicitly forbidden are blocked. |
| Maintenance Effort | Higher for dynamic external services; easier for fixed internal targets. | Lower initial effort, but constant updates needed for new bypasses. |
| Risk of Bypass | Very low if implemented correctly. | High, susceptible to new attack vectors and evasions. |
| Use Case | Best for applications interacting with a limited, known set of external services or internal resources. | Only as a last resort, never as the primary defense. |
Advanced Validation Steps
- Parse the URL Correctly: Use a robust URL parser (e.g., Node.js's
URLclass or Python'surllib.parse) to break down the URL into its components (protocol, hostname, port). Do not rely on simple string manipulation. - Resolve Hostnames to IP Addresses: An attacker can use a legitimate domain that resolves to an internal IP address (e.g.,
example.comresolving to127.0.0.1via a malicious DNS server or local DNS poisoning). Always resolve the hostname to its IP address(es) before making the request. In Node.js, usedns.promises.resolve4()ordns.promises.lookup(). - Check IP Address Against Allowlist: Once you have the IP address(es), ensure they fall within your explicitly allowed IP ranges. Crucially, reject any IP that is a private IP address (e.g.,
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8) unless it's explicitly allowed for a specific, secure internal interaction. - Validate Protocol and Port: Ensure only expected protocols (e.g.,
http,https) and ports are used. - Handle Redirects Securely: Many HTTP clients (like
axiosorfetch) follow redirects automatically. An attacker can provide a safe initial URL that redirects to a malicious internal target. Configure your HTTP client to explicitly disable redirects and perform redirect validation manually, or ensure all redirect targets are also validated against your allowlist.
Like this article? Help us grow.
Choose Krapton as a preferred source on Google to see more of our engineering insights in Search. You only need to click once.
Implementing Secure URL Validation (Code Examples)
Here's how to implement a more secure approach in Node.js 20, incorporating the principles above. This example focuses on rejecting private IP addresses and using an explicit allowlist for domains.
// Node.js 20+ example for secure SSRF prevention
const express = require('express');
const axios = require('axios');
const { URL } = require('url');
const dns = require('dns').promises;
const net = require('net');
const app = express();
// Explicitly allowed domains (allowlist)
const ALLOWED_DOMAINS = new Set([
'api.example.com',
'cdn.trusted-partner.com'
]);
// Function to check if an IP address is private (RFC 1918, localhost)
function isPrivateIP(ip) {
return net.isIP(ip) && (
ip.startsWith('10.') ||
ip.startsWith('172.16.') || (ip.startsWith('172.') && parseInt(ip.split('.')[1], 10) >= 16 && parseInt(ip.split('.')[1], 10) <= 31) ||
ip.startsWith('192.168.') ||
ip === '127.0.0.1' || ip === '::1' // Localhost
);
}
app.get('/fetch-external-content-secure', async (req, res) => {
const rawUrl = req.query.url;
if (!rawUrl) {
return res.status(400).send('URL parameter is required.');
}
let parsedUrl;
try {
parsedUrl = new URL(rawUrl);
} catch (e) {
return res.status(400).send('Invalid URL format.');
}
// 1. Validate Protocol
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
return res.status(403).send('Only HTTP/HTTPS protocols are allowed.');
}
// 2. Validate Domain against Allowlist
if (!ALLOWED_DOMAINS.has(parsedUrl.hostname)) {
return res.status(403).send('Domain not in allowlist.');
}
// 3. Resolve Hostname to IP(s) and check for private IPs
try {
const addresses = await dns.resolve4(parsedUrl.hostname); // Resolve to IPv4
for (const ip of addresses) {
if (isPrivateIP(ip)) {
return res.status(403).send('Access to private IP addresses is forbidden.');
}
}
} catch (e) {
console.error(`DNS resolution failed for ${parsedUrl.hostname}:`, e.message);
return res.status(403).send('Could not resolve hostname or access denied.');
}
// 4. (Optional) Validate Port if required
// if (parsedUrl.port && !['80', '443'].includes(parsedUrl.port)) { ... }
try {
// Disable redirects to prevent bypasses
const response = await axios.get(parsedUrl.href, { timeout: 5000, maxRedirects: 0 });
res.send(`Fetched content type: ${response.headers['content-type']}`);
} catch (error) {
if (error.response && error.response.status === 302) {
return res.status(403).send('Redirects are not allowed.');
}
console.error('Error fetching URL:', error.message);
res.status(500).send('Failed to fetch content or access denied.');
}
});
app.listen(3001, () => console.log('Secure server running on port 3001'));
Experience: In a recent client engagement involving a Next.js 15.2 App Router application, we encountered a similar scenario where an image optimization service was vulnerable. Our initial blocklist approach, which relied on regex to filter out 127.0.0.1 and 192.168.x.x, failed when an attacker used an obfuscated IP address like 0x7f000001 or [::1]. We switched to a strict allowlist combined with explicit DNS resolution and net.isIP() checks, similar to the code above, which effectively closed the loophole. This 'we tried X, switched to Y' arc proved critical for hardening their infrastructure.
Beyond Validation: Layered Defense Strategies
While robust URL validation is the cornerstone of SSRF prevention, a layered security approach provides additional resilience. Think of it as defense in depth, ensuring that even if one control fails, others are still in place.
1. Network Segmentation and Firewall Rules
Isolate services that handle external requests from your critical internal infrastructure. Use firewalls to restrict outbound connections from your web application servers. Only allow connections to explicitly permitted external IP addresses or domains. Block outbound connections to private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8) at the network level, even if your application-level validation is strong.
Experience: On a production rollout for a critical SaaS product, we implemented strict AWS VPC Security Group rules. The server responsible for fetching user-provided URLs was placed in a dedicated subnet with outbound rules allowing traffic only to public internet and specific, well-known third-party APIs. All internal services resided in a separate, isolated subnet with no ingress from the public-facing server's subnet, effectively containing potential SSRF attempts to the internet-facing layer.
2. Least Privilege for Services
Ensure that the user or role under which your application runs has the absolute minimum necessary permissions. If an SSRF attack successfully fetches cloud credentials (e.g., from AWS IMDS), those credentials should have very limited permissions. For example, if your application only needs to read from an S3 bucket, its IAM role should only have s3:GetObject permissions for that specific bucket, not * access.
3. Cloud-Specific Protections (e.g., AWS IMDSv2)
Cloud providers are aware of SSRF risks. AWS, for instance, introduced Instance Metadata Service Version 2 (IMDSv2). IMDSv2 requires session-oriented requests, making it significantly harder for a classic SSRF attack to directly retrieve credentials. Ensure your EC2 instances are configured to use IMDSv2 and that older IMDSv1 is disabled.
4. API Gateways and Proxies
For applications that rely heavily on internal APIs, consider routing all internal traffic through an API Gateway or a reverse proxy. These can enforce additional security policies, including origin validation and rate limiting, providing another layer of defense against malicious internal requests.
When NOT to use this approach
While an allowlist is generally superior for SSRF prevention, it introduces operational complexity for applications that need to interact with a vast, dynamic, and unpredictable set of external services. For instance, a web scraper or a general-purpose URL shortener that must connect to any arbitrary public URL would find a strict allowlist impractical. In such niche cases, a very carefully constructed and continuously updated blocklist (often combined with a proxy that strictly enforces IP egress filtering) might be the only viable option, but this significantly increases the risk profile and requires advanced threat intelligence and monitoring.
Common Mistakes in SSRF Prevention
Even with good intentions, developers often make subtle mistakes that can leave SSRF vulnerabilities open:
- Incomplete Blocklists: Relying on a blocklist of 'bad' IPs (like
127.0.0.1) is often insufficient. Attackers use IP address encodings (hex, decimal, octal), DNS rebinding, and URL schemes (e.g.,http://[::1]for IPv6 localhost) to bypass these. - Ignoring DNS Resolution: Failing to resolve a hostname to its IP address before validation. An attacker can register
malicious.comto resolve to127.0.0.1, bypassing hostname-based allowlists. - Not Handling Redirects: Assuming the initial URL is the final destination. HTTP clients that automatically follow redirects can be tricked into fetching from an internal resource after an initial 'safe' external URL.
- Trusting Client-Side Validation: Any validation performed client-side (JavaScript) can be easily bypassed by a malicious actor. All security-critical validation must occur on the server.
- Lack of Protocol and Port Validation: Allowing schemes like
file://,gopher://, or arbitrary ports can open new attack vectors.
Verifying Your SSRF Protections
Implementing security controls is only half the battle; verifying their effectiveness is crucial. For SSRF, this involves both automated and manual testing:
- Penetration Testing: Engage ethical hackers or a specialized software security services firm like Krapton to conduct targeted penetration tests. Experienced pentesters will actively look for SSRF vulnerabilities and attempt to bypass your controls using various encoding tricks, redirects, and DNS rebinding techniques.
- Automated Security Scanners: Use Dynamic Application Security Testing (DAST) tools that can crawl your application and attempt to inject SSRF payloads. While DAST tools might not catch every subtle bypass, they can identify common patterns.
- Manual Code Review: Regularly review code sections that handle external URL fetching. Pay close attention to URL parsing, DNS resolution, and IP validation logic.
- Egress Filtering Audits: Verify your network egress rules (firewalls, security groups) are correctly configured to block outbound connections to private IP ranges from your web servers.
FAQ
What's the difference between SSRF and CSRF?
CSRF (Cross-Site Request Forgery) tricks a user's browser into sending an authenticated request to a vulnerable web application. SSRF (Server-Side Request Forgery) tricks the server itself into making a request to an arbitrary URL, often targeting internal resources. They exploit different layers and involve different attack vectors.
Can a WAF prevent SSRF attacks?
A Web Application Firewall (WAF) can offer some protection by filtering known malicious patterns or blocking access to specific private IP ranges in URLs. However, WAFs are often bypassable through clever encoding or DNS rebinding. They should be considered a perimeter defense, not a replacement for robust application-level validation.
How does DNS rebinding relate to SSRF?
DNS rebinding is an advanced SSRF bypass technique where an attacker's domain (e.g., attacker.com) initially resolves to a safe public IP. After the initial DNS lookup and validation, the attacker changes the DNS record to point to a private IP (e.g., 127.0.0.1). If the server caches DNS results for too long or re-resolves the IP after validation, it can then make a request to the internal IP under the guise of the 'safe' domain.
Is SSRF a concern for serverless applications?
Yes, SSRF is absolutely a concern for serverless functions (e.g., AWS Lambda, Google Cloud Functions) if they make requests based on user-supplied URLs. While the attack surface might differ slightly (e.g., no traditional localhost), serverless functions still run within a network and can potentially access cloud metadata, internal services, or other functions within the same VPC.
Krapton's Approach to Application Security
At Krapton, we understand that building secure applications is not an afterthought but a fundamental part of the development lifecycle. Our engineering teams, composed of principal-level software engineers and seasoned security strategists, bake security in from the ground up, ensuring your applications are resilient against threats like SSRF. We integrate robust validation, layered network defenses, and secure coding practices into every project, whether we're building custom web apps, mobile solutions, or complex SaaS platforms. Our expertise in modern frameworks and cloud environments means we're equipped to identify and mitigate sophisticated vulnerabilities before they impact your business.
Ready to fortify your applications against critical vulnerabilities? Book a free consultation with Krapton to discuss how our dedicated development teams can integrate advanced security practices into your next project.


