# Server Side Request Forgery

#### Tools recognizing this:

<kbd>Opengrep</kbd> <kbd>Fortify</kbd> <kbd>Checkmarx</kbd> <kbd>SonarQube</kbd> <kbd>Snyk</kbd> <kbd>Semgrep</kbd> <kbd>CodeQL</kbd>

## What is SSRF and How Does it Work?

Server Side Request Forgery (SSRF) is a web security vulnerability that allows attackers to induce the server-side application to make requests to unintended locations. It occurs when an application accepts user-supplied URLs without proper validation, allowing attackers to manipulate the server into making malicious requests.

The attacker can potentially:

* Access internal services behind firewalls
* Scan internal networks
* Interact with cloud service metadata endpoints
* Execute remote code in some cases
* Perform denial of service attacks

This guide covers SSRF attacks, examples, prevention methods, and how to test for SSRF vulnerabilities using real-world techniques.

## One Simple SSRF Attack Example

Consider this classic example of a URL fetch:

{% code overflow="wrap" %}

```
String url = request.getParameter("url");
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
```

{% endcode %}

An attacker could provide this input for the URL:

<mark style="color:red;">`http://internal-service:8080/admin`</mark>

The resulting request would attempt to access:

<kbd>http\://</kbd><kbd><mark style="color:red;">internal-service:8080/admin<mark style="color:red;"></kbd>

This allows the attacker to access internal services that should not be publicly accessible.

## SSRF Prevention Methods: How to Fix Your Code

The most efficient way to fix an SSRF issue in your code is implementing proper URL validation, including whitelisting allowed domains and blocking requests to internal networks.

URL validation should include checking the protocol, domain, and path against an allowlist of permitted values, and ensuring requests cannot be made to private IP ranges or internal hostnames.

### Code Samples

{% tabs %}
{% tab title="Java" %}
**Vulnerable Code**

<pre class="language-java"><code class="lang-java">String url = request.getParameter("url");
URL obj = new URL(<a data-footnote-ref href="#user-content-fn-1">url</a>);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
</code></pre>

**Fixed Code**

```java
String url = request.getParameter("url");
if (!isUrlAllowed(url)) {
    throw new SecurityException("URL not allowed");
}
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setConnectTimeout(5000);
con.setReadTimeout(5000);
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));

private boolean isUrlAllowed(String url) {
    List<String> allowedDomains = Arrays.asList("api.example.com", "cdn.example.com");
    try {
        URL u = new URL(url);
        return allowedDomains.contains(u.getHost()) && u.getProtocol().equals("https");
    } catch (MalformedURLException e) {
        return false;
    }
}
```

**Fix Explanation**

The vulnerable code accepts any URL without validation.The fix implements URL validation against a whitelist of allowed domains.Only HTTPS protocol is allowed.Timeouts are set to prevent hanging connections.
{% endtab %}

{% tab title="JavaScript" %}
**Vulnerable Code**

```javascript
const url = req.query.url;
const response = await fetch(url);
const data = await response.json();
res.send(data);
```

**Fixed Code**

```javascript
const url = req.query.url;
const allowedDomains = ['api.example.com', 'cdn.example.com'];

function isUrlAllowed(urlString) {
    try {
        const urlObj = new URL(urlString);
        return allowedDomains.includes(urlObj.hostname) && 
               urlObj.protocol === 'https:';
    } catch {
        return false;
    }
}

if (!isUrlAllowed(url)) {
    throw new Error('URL not allowed');
}

const response = await fetch(url, {
    timeout: 5000,
    follow: 1
});
const data = await response.json();
res.send(data);
```

**Fix Explanation**

The vulnerable code makes requests to any provided URL.The fix validates URLs against a whitelist of allowed domains.Only HTTPS protocol is allowed.Request timeout and redirect limits are set.
{% endtab %}

{% tab title="Python" %}
**Vulnerable Code**

```python
url = request.args.get('url')
response = requests.get(url)
return response.content
```

**Fixed Code**

```python
from urllib.parse import urlparse
import requests

ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com']

def is_url_allowed(url):
    try:
        parsed = urlparse(url)
        return (parsed.scheme == 'https' and 
                parsed.netloc in ALLOWED_DOMAINS)
    except:
        return False

url = request.args.get('url')
if not is_url_allowed(url):
    raise ValueError('URL not allowed')

response = requests.get(url, 
                       timeout=5,
                       allow_redirects=False,
                       verify=True)
return response.content
```

**Fix Explanation**

The vulnerable code makes requests to any URL without validation.The fix implements URL validation with an allowed domains list.Only HTTPS is allowed.Timeouts and SSL verification are enforced.
{% endtab %}

{% tab title="C#" %}
**Vulnerable Code**

```csharp
string url = Request.QueryString["url"];
using (var client = new HttpClient())
{
    var response = await client.GetAsync(url);
    return await response.Content.ReadAsStringAsync();
}
```

**Fixed Code**

```csharp
private static readonly HashSet<string> AllowedDomains = 
    new HashSet<string> { "api.example.com", "cdn.example.com" };

private static bool IsUrlAllowed(string url)
{
    if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
    {
        return uri.Scheme == "https" && 
               AllowedDomains.Contains(uri.Host);
    }
    return false;
}

string url = Request.QueryString["url"];
if (!IsUrlAllowed(url))
{
    throw new SecurityException("URL not allowed");
}

using (var client = new HttpClient())
{
    client.Timeout = TimeSpan.FromSeconds(5);
    var response = await client.GetAsync(url);
    return await response.Content.ReadAsStringAsync();
}
```

**Fix Explanation**

The vulnerable code accepts any URL without validation.The fix implements URL validation against a whitelist.Only HTTPS URLs are allowed.Request timeout is configured.
{% endtab %}

{% tab title="PHP" %}
**Vulnerable Code**

```php
$url = $_GET['url'];
$content = file_get_contents($url);
echo $content;
```

**Fixed Code**

```php
function isUrlAllowed($url) {
    $allowedDomains = ['api.example.com', 'cdn.example.com'];
    $parsed = parse_url($url);
    
    return isset($parsed['scheme']) && 
           $parsed['scheme'] === 'https' && 
           in_array($parsed['host'], $allowedDomains);
}

$url = $_GET['url'];
if (!isUrlAllowed($url)) {
    throw new Exception('URL not allowed');
}

$ctx = stream_context_create([
    'http' => [
        'timeout' => 5,
        'follow_location' => 0
    ]
]);

$content = file_get_contents($url, false, $ctx);
echo $content;
```

**Fix Explanation**

The vulnerable code fetches any provided URL.The fix validates URLs against an allowed domains list.Only HTTPS is allowed.Timeout and redirect settings are configured.
{% endtab %}

{% tab title="Go" %}
**Vulnerable Code**

```go
url := r.URL.Query().Get("url")
resp, err := http.Get(url)
if err != nil {
    return err
}
io.Copy(w, resp.Body)
```

**Fixed Code**

```go
var allowedDomains = map[string]bool{
    "api.example.com": true,
    "cdn.example.com": true,
}

func isUrlAllowed(urlStr string) bool {
    u, err := url.Parse(urlStr)
    if err != nil {
        return false
    }
    return u.Scheme == "https" && allowedDomains[u.Host]
}

url := r.URL.Query().Get("url")
if !isUrlAllowed(url) {
    return errors.New("URL not allowed")
}

client := &http.Client{
    Timeout: 5 * time.Second,
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        return http.ErrUseLastResponse
    },
}

resp, err := client.Get(url)
if err != nil {
    return err
}
io.Copy(w, resp.Body)
```

**Fix Explanation**

The vulnerable code makes requests to any URL.The fix implements URL validation with allowed domains.Only HTTPS is allowed.Timeout and redirect policies are set.
{% endtab %}
{% endtabs %}

## Need more help in preventing SSRF?

[Mobb](https://mobb.ai) supports fixing many forms of SSRF vulnerabilities, and can mitigate your issues in batch.

Start now for free at [app.mobb.ai](https://app.mobb.ai)

### We'd love your feedback!

We're excited to hear your thoughts and ideas about fixing vulnerabilities.

[Book a meeting](https://calendly.com/mobbai/demo) or [Contact us](https://content.mobb.ai/contact) if you have any corrections, questions or suggestions. Start now for free at <https://app.mobb.ai>

[^1]: This is the vulnerable part
