Skip to content

Conversation

ptrgits
Copy link

@ptrgits ptrgits commented Aug 4, 2025

const response = await nodeFetch(url, { method: 'HEAD' });

const response = await fetch(feature.apiUrlTemplate.replace('<address>', address));

return nodeFetch(url, {
...init,
headers,
body,
});

Directly incorporating user input in the URL of an outgoing HTTP request can enable a request forgery attack, in which the request is altered to target an unintended API endpoint or resource. If the server performing the request is connected to an internal network, this can give an attacker the means to bypass the network boundary and make requests against internal services. A forged request may perform an unintended action on behalf of the attacker, or cause information leak if redirected to an external server or if the request response is fed back to the user. It may also compromise the server making the request, if the request response is handled in an unsafe way.


fix this SSRF vulnerability, we must ensure that user input cannot arbitrarily control the destination of the outgoing HTTP request. The best way to do this is to restrict the possible values of the base endpoint (i.e., the host and protocol) to a fixed allow-list of trusted endpoints. Instead of using the value of the x-endpoint header directly, we should check it against a list of allowed endpoints (e.g., from config), and only use it if it matches. Otherwise, we should reject the request or fall back to a safe default. This validation should be performed in pages/api/proxy.ts where the endpoint is selected, before constructing the URL. The rest of the code can remain unchanged, as the taint is stopped at the source.

Steps:

  • Define an allow-list of permitted endpoints (hostnames or full URLs) in pages/api/proxy.ts.
  • When reading x-endpoint, check if its value is in the allow-list.
  • If it is, use it as the base; otherwise, use the default endpoint or reject the request.
  • Do not allow arbitrary user input to control the base URL.

The best way to implement this is to add a validation step in AddressSaveOnGas before making the fetch request. If the address is invalid, the function should return early or throw an error. This change should be made in ui/address/details/AddressSaveOnGas.tsx, specifically in the queryFn function where the fetch is performed.

The changes should be made in pages/api/media-type.ts:

  • Before making the request, parse the URL and check its hostname against an allow-list.
  • If the hostname is not allowed, log the attempt and return an error response.
  • Add any necessary imports (e.g., URL from the standard library).

References

SSRF
CWE-918

Checklist for PR author

  • I have tested these changes locally.
  • I added tests to cover any new functionality, following this guide
  • Whenever I fix a bug, I include a regression test to ensure that the bug does not reappear silently.
  • If I have added, changed, renamed, or removed an environment variable
    • I updated the list of environment variables in the documentation
    • I made the necessary changes to the validator script according to the guide
    • I added "ENVs" label to this pull request

Comment on lines +14 to +33
// SSRF protection: Only allow URLs from trusted hostnames
const allowedHostnames = [
'example.com',
'cdn.example.com',
// Add other trusted hostnames as needed
];
let parsedUrl;
try {
parsedUrl = new URL(url);
} catch (e) {
httpLogger.logger.error({ message: 'Invalid URL', url });
res.status(400).json({ type: undefined, error: 'Invalid URL' });
return;
}
if (!allowedHostnames.includes(parsedUrl.hostname)) {
httpLogger.logger.error({ message: 'Disallowed hostname', url, hostname: parsedUrl.hostname });
res.status(403).json({ type: undefined, error: 'Disallowed hostname' });
return;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main characteristic of this resource is that we do not know the domain of the server that serves the asset in advance. Therefore, we cannot determine the allowed hostname array at all.

const ALLOWED_ENDPOINTS = [
appConfig.apis.general.endpoint,
// Add other allowed endpoints here, e.g.:
// "https://api.example.com",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This resource is not used in production, so the changes are not necessary. If we want to keep them, please add all available API endpoints to the list.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I don't see how this resource can be requested using the endpoint from user input.

Comment on lines +28 to +30
// Simple Ethereum address validation (42 chars, starts with 0x, hex)
const isValidAddress = /^0x[a-fA-F0-9]{40}$/.test(address);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that if the address is invalid, the page will throw a 422 error, so this code will never execute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants