Skip to main content

App proxy request trusted without signature verification

Detects app proxy parameters read without visible signature verification.

DetailValue
Check IDAPP_PROXY_UNVERIFIED_SIGNATURE
SeverityHigh
How it runsAgent review
Applies toApps with an app proxy

app/routes/proxy.orders.tsx

import type { LoaderFunctionArgs } from "react-router";

import prisma from "../db.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url);

// Proxy parameters are only trustworthy once the HMAC is
// checked. Unverified, anyone can call the endpoint with any
// shop or customer id.
return {
orders: await prisma.order.findMany({
where: {
shop: url.searchParams.get("shop") ?? undefined,
customerId:
url.searchParams.get("logged_in_customer_id") ?? undefined,
},
}),
};
};

Verify app proxy signatures before trusting shop, customer, or path parameters.

For more detail, see Authenticate app proxies.

app/routes/proxy.orders.tsx

import type { LoaderFunctionArgs } from "react-router";

import { authenticate } from "../shopify.server";
import prisma from "../db.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
// Verifies the proxy signature and returns the shop it was
// signed for.
const { session } = await authenticate.public.appProxy(request);

if (!session) throw new Response("Unauthorized", { status: 401 });

// Verification proves the request came from Shopify, not that a
// customer is signed in: logged_in_customer_id is empty for a
// guest. The signature covers the query string, so once
// verification passes this value is trustworthy.
const url = new URL(request.url);
const customerId = url.searchParams.get("logged_in_customer_id");

if (!customerId) throw new Response("Unauthorized", { status: 401 });

return {
orders: await prisma.order.findMany({
where: { shop: session.shop, customerId },
select: { id: true, name: true, fulfillmentStatus: true },
}),
};
};


Was this page helpful?