Server-side request forgery risk
Investigates server-side requests whose destinations may be controlled by request input.
| Detail | Value |
|---|---|
| Check ID | SSRF_REQUEST_FORGERY |
| Severity | High |
| How it runs | Agent review |
Anchor to Vulnerable exampleVulnerable example
app/routes/app.import.tsx
import type { ActionFunctionArgs } from "react-router";
import { authenticate } from "../shopify.server";
export const action = async ({ request }: ActionFunctionArgs) => {
await authenticate.admin(request);
const feedUrl = String((await request.formData()).get("feedUrl"));
// The server will fetch whatever it is given, including
// localhost, private ranges, and the cloud metadata endpoint.
return { feed: await (await fetch(feedUrl)).json() };
};
Anchor to How to fixHow to fix
Allowlist destinations and reject private, local, and unsafe network targets.
app/routes/app.import.tsx
import type { ActionFunctionArgs } from "react-router";
import { authenticate } from "../shopify.server";
const ALLOWED_HOSTS = new Set(["feeds.example-partner.com"]);
export const action = async ({ request }: ActionFunctionArgs) => {
await authenticate.admin(request);
const formData = await request.formData();
const feedUrl = new URL(String(formData.get("feedUrl")));
// Allowlist scheme and host, and re-check after redirects: a 302
// to 169.254.169.254 defeats a check made only on the first URL.
if (
feedUrl.protocol !== "https:" ||
!ALLOWED_HOSTS.has(feedUrl.hostname)
) {
throw new Response("Unsupported feed host", { status: 400 });
}
const response = await fetch(feedUrl, { redirect: "error" });
return { feed: await response.json() };
};
shopify app security check, the command that runs this check- All app security checks
Was this page helpful?