Skip to main content

Server-side request forgery risk

Investigates server-side requests whose destinations may be controlled by request input.

DetailValue
Check IDSSRF_REQUEST_FORGERY
SeverityHigh
How it runsAgent review

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() };
};

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() };
};


Was this page helpful?