validate admin form origins behind proxy chain
This commit is contained in:
1 parent
fe6846ea0f
commit
3ccbae2471
2 files changed
+18
-5
No files matched your search
@@ -10,11 +10,9 @@ export default defineConfig({
|
||||
adapter: node({ mode: "standalone" }),
|
||||
integrations: [preact()],
|
||||
security: {
|
||||
checkOrigin: true,
|
||||
allowedDomains: [
|
||||
{ hostname: "recipes.uuard.com", protocol: "https" },
|
||||
{ hostname: "recipes-admin.uuard.com", protocol: "https" },
|
||||
],
|
||||
// Origin validation is performed in application middleware because the
|
||||
// edge and K3s proxy chain terminates TLS before the Node request.
|
||||
checkOrigin: false,
|
||||
},
|
||||
vite: { server: { fs: { allow: ["."] } } },
|
||||
});
|
||||
@@ -2,9 +2,24 @@ import { defineMiddleware } from "astro:middleware";
|
||||
import { readOnlyMode } from "../lib/runtime";
|
||||
|
||||
const safeMethods = new Set(["GET", "HEAD", "OPTIONS"]);
|
||||
const formContentTypes = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
|
||||
const productionWriteOrigin = "https://recipes-admin.uuard.com";
|
||||
|
||||
function isForbiddenFormSubmission(request: Request, url: URL) {
|
||||
if (safeMethods.has(request.method)) return false;
|
||||
const contentType = request.headers.get("content-type")?.toLowerCase();
|
||||
if (contentType && !formContentTypes.some((value) => contentType.includes(value))) return false;
|
||||
const origin = request.headers.get("origin");
|
||||
if (origin === productionWriteOrigin) return false;
|
||||
const localDevelopment = url.hostname === "localhost" || url.hostname === "127.0.0.1";
|
||||
return !localDevelopment || origin !== url.origin;
|
||||
}
|
||||
|
||||
export const onRequest = defineMiddleware(async ({ request, url, redirect }, next) => {
|
||||
if (url.pathname === "/") return redirect("/app/", 302);
|
||||
if (isForbiddenFormSubmission(request, url)) {
|
||||
return new Response(`Cross-site ${request.method} form submissions are forbidden`, { status: 403 });
|
||||
}
|
||||
if (!readOnlyMode) return next();
|
||||
if (!safeMethods.has(request.method)) {
|
||||
return new Response("This Formulation instance is read-only.", { status: 405, headers: { Allow: "GET, HEAD, OPTIONS" } });
|
||||
|
||||
Reference in new issue
Block a user