To detect potential SSRF attacks, you can implement a servlet filter that inspects incoming requests for suspicious URL patterns. Before we look at the implementation, let's understand what patterns are commonly targeted in SSRF attacks and why:
Private IP Ranges (RFC 1918):
- 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16: These are private network ranges used in internal networks. Attackers use SSRF to access internal services that aren't exposed to the internet, such as databases, admin panels, or internal APIs.
Loopback Addresses:
- 127.0.0.1, localhost: These refer to the server itself. Attackers can use SSRF to access services running only on localhost (like development databases or internal management interfaces) that should never be accessible from outside.
Cloud Metadata Services:
- 169.254.169.254: Cloud providers (AWS, Azure, GCP) expose instance metadata at this address. Attackers can retrieve sensitive information like access credentials, API keys, and instance configurations through SSRF.
Internal Hostnames:
- "internal" substring: Organizations often use naming conventions like "internal.company.com" or "service-internal" for internal-only services. Detecting "internal" as a substring helps catch attempts to access these services.
Alternative Protocols:
- file://, dict://, gopher://: These protocols can be used to read local files, interact with services in unexpected ways, or perform other malicious actions beyond simple HTTP requests.
Here's how you might implement detection for these patterns in Java:
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.logging.*;
public class SSRFDetectionFilter implements Filter {
private static final Logger logger = Logger.getLogger(SSRFDetectionFilter.class.getName());
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String url = req.getParameter("url");
if (url == null && "application/json".equals(req.getContentType())) {
// Optionally, parse JSON body for "url" field if needed
}
if (url != null && isSuspiciousUrl(url)) {
logger.warning("POTENTIAL SSRF ATTACK: " + req.getRemoteAddr() + " tried to access " + url);
alertAdmin("Potential SSRF Attack", "IP: " + req.getRemoteAddr() + ", URL: " + url);
// Optionally, block the request or continue processing
}
chain.doFilter(request, response);
}
private boolean isSuspiciousUrl(String url) {
String lowerUrl = url.toLowerCase();
// Check for loopback addresses - access to the server itself
if (lowerUrl.contains("127.0.0.1") || lowerUrl.contains("localhost")) {
return true;
}
// Check for cloud metadata service - AWS/Azure/GCP credentials
if (lowerUrl.contains("169.254.169.254")) {
return true;
}
// Check for private network ranges - internal services
if (lowerUrl.contains("10.") || lowerUrl.contains("172.16.") || lowerUrl.contains("192.168.")) {
return true;
}
// Check for internal hostname patterns - internal-only services
if (lowerUrl.contains("internal")) {
return true;
}
// Check for dangerous protocols - file access and other exploits
String[] dangerousProtocols = {"file:", "dict:", "gopher:", "ftp:"};
for (String protocol : dangerousProtocols) {
if (lowerUrl.startsWith(protocol)) {
return true;
}
}
return false;
}
private void alertAdmin(String subject, String message) {
// Implementation of alerting mechanism (see next section)
System.out.println("ALERT: " + subject + " - " + message);
}
}
Important Note: This detection approach uses substring matching as a first line of defense. However, attackers may use obfuscation techniques (like URL encoding, IP address formats in decimal or hexadecimal, DNS rebinding, or redirects) to bypass these checks. For production systems, combine this with the allowlist-based validation approach from the previous lesson and consider using more sophisticated URL parsing and validation libraries.
Register this filter in your application to monitor and log suspicious requests.