HIGH ssrfdigitalocean

Ssrf on Digitalocean

How SSRF Manifests in Digitalocean

Server-Side Request Forgery (SSRF) in Digitalocean environments often exploits the platform's metadata service and internal networking. Digitalocean droplets expose a metadata endpoint at http://169.254.169.254 that provides instance-specific data including SSH keys, user data, and network configuration. When applications running on Digitalocean accept user-controlled URLs without proper validation, attackers can craft requests to this metadata endpoint to extract sensitive information.

A common Digitalocean-specific SSRF pattern occurs when applications proxy requests to external services. For example, a Digitalocean App Platform application that fetches images from user-provided URLs might inadvertently access internal services. The 169.254.169.254 metadata endpoint is accessible from any Digitalocean droplet without authentication, making it a prime target for SSRF attacks.

Digitalocean's internal networking also creates SSRF opportunities. Droplets can communicate with other services within the same account using private IP addresses. An application that accepts URLs without proper validation might be tricked into making requests to internal APIs, database servers, or other microservices within the Digitalocean infrastructure.

Consider this vulnerable Digitalocean Node.js application that proxies requests to user-provided URLs:

const express = require('express');
const axios = require('axios');
const app = express();

app.get('/proxy', async (req, res) => {
  const url = req.query.url;
  const response = await axios.get(url);
  res.send(response.data);
});

app.listen(3000, () => console.log('Running on port 3000'));

This code is vulnerable to SSRF because it accepts any URL without validation. An attacker could request http://169.254.169.254/latest/meta-data/ to access Digitalocean's metadata service and potentially extract SSH keys or other sensitive information.

Digitalocean Spaces (object storage) introduces another SSRF vector. Applications that validate URLs by checking the domain might be bypassed using Digitalocean's internal routing. A request to myapp.digitaloceanspaces.com might resolve internally and allow access to internal APIs if the application doesn't properly validate the resolved IP address.

Digitalocean-Specific Detection

Detecting SSRF vulnerabilities in Digitalocean environments requires testing against platform-specific endpoints and behaviors. The primary target is the metadata service at http://169.254.169.254, but effective testing must also consider Digitalocean's internal networking and service architecture.

middleBrick's black-box scanning approach is particularly effective for Digitalocean SSRF detection because it tests the unauthenticated attack surface without requiring credentials or configuration. The scanner attempts requests to http://169.254.169.254 and other internal endpoints that might be accessible from your Digitalocean droplet.

For manual testing of SSRF in Digitalocean applications, you can use curl to test metadata access:

# Test if metadata service is accessible
curl -s http://169.254.169.254/latest/meta-data/

# Test for specific metadata
curl -s http://169.254.169.254/latest/meta-data/public-keys/
curl -s http://169.254.169.254/latest/meta-data/user-data/

middleBrick automatically tests these endpoints and provides a security risk score with findings. The scanner also tests for SSRF against common Digitalocean internal services and validates whether the application properly validates URLs before making requests.

Digitalocean App Platform applications require special attention because they run in a managed environment where the metadata service is always accessible. middleBrick's Digitalocean-specific checks include:

Check TypeTargetRisk Level
Metadata Service169.254.169.254Critical
Internal DNS.digitaloceanspaces.comHigh
Private Networking10.x.x.x rangesHigh
Service Discoverylocalhost:3000+Medium

The scanner also tests for SSRF against Digitalocean's API endpoints. If your application makes requests to Digitalocean's API without proper authentication, an attacker could potentially use your application to make unauthorized API calls or enumerate your Digitalocean resources.

For Digitalocean Droplets, middleBrick can be run directly from the command line:

npx middlebrick scan http://your-digitalocean-app.com

# Or install globally
npm install -g middlebrick
middlebrick scan http://your-digitalocean-app.com

The scanner provides detailed findings including the specific URLs that were successfully accessed, the type of information that could be extracted, and remediation guidance specific to Digitalocean's architecture.

Digitalocean-Specific Remediation

Remediating SSRF vulnerabilities in Digitalocean applications requires a defense-in-depth approach that addresses both the application logic and Digitalocean's specific attack surface. The primary defense is strict URL validation combined with Digitalocean-specific security measures.

First, implement URL validation that checks both the domain and the resolved IP address. Digitalocean applications should explicitly block requests to the metadata service and internal networks:

const express = require('express');
const axios = require('axios');
const app = express();
const validUrl = require('valid-url');

function isBlockedDomain(hostname) {
  const blockedDomains = [
    '169.254.169.254', // Digitalocean metadata
    'digitaloceanspaces.com',
    'myapp.digitaloceanspaces.com'
  ];
  return blockedDomains.some(domain => hostname.includes(domain));
}

function isInternalIP(ip) {
  const privateIPRanges = [
    /^10\./, // Class A private
    /^172\.(1[6-9]|2[0-9]|3[0-1])\./, // Class B private
    /^192\.168\./, // Class C private
    /^127\./, // Loopback
    /^::1/, // IPv6 loopback
    /^fd00/ // IPv6 unique local
  ];
  return privateIPRanges.some(range => range.test(ip));
}

app.get('/proxy', async (req, res) => {
  const url = req.query.url;
  
  if (!validUrl.isUri(url)) {
    return res.status(400).json({ error: 'Invalid URL format' });
  }
  
  try {
    const parsedUrl = new URL(url);
    const hostname = parsedUrl.hostname;
    
    if (isBlockedDomain(hostname)) {
      return res.status(403).json({ error: 'Blocked domain' });
    }
    
    const lookup = await dnsPromises.lookup(hostname);
    if (isInternalIP(lookup.address)) {
      return res.status(403).json({ error: 'Internal IP address blocked' });
    }
    
    const response = await axios.get(url, {
      timeout: 10000,
      headers: { 'User-Agent': 'Digitalocean-App-Proxy' }
    });
    
    res.send(response.data);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

For Digitalocean App Platform, use the platform's built-in security features. Digitalocean provides environment variables and secrets management that should be used instead of exposing sensitive data through metadata. Configure your application to use process.env variables rather than relying on metadata for configuration.

Digitalocean's firewall service can add another layer of protection by blocking outbound requests to known malicious or internal IP ranges. Configure firewall rules to restrict your application's network access to only the services it needs to communicate with.

For applications that must make requests to external services, implement a whitelist approach rather than a blacklist. Define exactly which domains and services your application should communicate with, and reject all other requests:

const allowedDomains = [
  'api.example.com',
  'images.example.com',
  'cdn.example.net'
];

function isAllowedDomain(hostname) {
  return allowedDomains.some(domain => 
    hostname === domain || hostname.endsWith('.' + domain)
  );
}

// In your request handler
if (!isAllowedDomain(hostname)) {
  return res.status(403).json({ error: 'Domain not allowed' });
}

Digitalocean Spaces applications should use signed URLs for temporary access to objects rather than allowing direct URL input. This prevents attackers from manipulating URLs to access unauthorized resources or internal services.

Finally, implement request timeouts and size limits to prevent SSRF from being used for server-side denial of service attacks. Digitalocean's network infrastructure can handle large volumes of traffic, but your application should still enforce reasonable limits:

const axiosInstance = axios.create({
  timeout: 10000,
  maxContentLength: 1024 * 1024 * 5, // 5MB max response
  maxBodyLength: 1024 * 1024 * 2 // 2MB max request
});

These Digitalocean-specific mitigations, combined with general SSRF prevention techniques, significantly reduce the risk of server-side request forgery in your Digitalocean applications.

Related CWEs: ssrf

CWE IDNameSeverity
CWE-918Server-Side Request Forgery (SSRF) CRITICAL
CWE-441Unintended Proxy or Intermediary (Confused Deputy) HIGH

Frequently Asked Questions

Why is Digitalocean's metadata service a special concern for SSRF?
Digitalocean's metadata service at 169.254.169.254 is accessible from any droplet without authentication and contains sensitive information like SSH keys, user data, and network configuration. Unlike some cloud providers that require authentication or use different endpoints, Digitalocean's metadata service is intentionally designed to be easily accessible to the instance itself, making it a prime target for SSRF attacks when applications accept user-controlled URLs.
Can middleBrick detect SSRF vulnerabilities in my Digitalocean App Platform application?