Astro 排除部分路徑不檢查 CSRF Origin
Astro 預設會檢查提交表單的 Origin 是否與網站的 Origin 相同,目的是為了防止 CSRF 攻擊,但是在串第三方金流 API 的時候回傳的請求就會被擋下來,這回就來解決這個問題~
originCheck
Middleware
修改 Astro 的 既然是 Astro 會擋下來,那我們就先關掉吧:
export default defineConfig({ security: { // Change built-in origin check to custom function // @see src/middleware.ts checkOrigin: false, },})
然後在 src/middleware/originCheck.ts
新增一個 originCheck
的 middleware,裡面只新增了 EXCLUDE_PATHS
陣列,裡面放入需要排除的路徑,這樣就可以讓 Astro 在這些路徑上不檢查 CSRF 的 Origin 了。比如我們這邊就放了金流的回傳請求路徑 /checkout/done
:
/** * @reference https://github.com/withastro/astro/blob/d2d04b02773f82103db75076fcdd1f089503770a/packages/astro/src/core/app/middlewares.ts */
import { defineMiddleware } from 'astro:middleware'
/** * Content types that can be passed when sending a request via a form * * https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/enctype * @private */const FORM_CONTENT_TYPES = [ 'application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain',]
// Note: TRACE is unsupported by undici/Node.jsconst SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
// Paths that should be excluded from origin checkconst EXCLUDE_PATHS = ['/checkout/done']
/** * Returns a middleware function in charge to check the `origin` header. */export const originCheck = defineMiddleware((context, next) => { const { request, url, isPrerendered } = context // Prerendered pages should be excluded if (isPrerendered) { return next() } // Safe methods don't require origin check if (SAFE_METHODS.includes(request.method)) { return next() } // Exclude specific paths from origin check if (EXCLUDE_PATHS.includes(url.pathname)) { return next() }
const isSameOrigin = request.headers.get('origin') === url.origin
const hasContentType = request.headers.has('content-type') if (hasContentType) { const formLikeHeader = hasFormLikeHeader(request.headers.get('content-type')) if (formLikeHeader && !isSameOrigin) { return new Response(`Cross-site ${request.method} form submissions are forbidden`, { status: 403, }) } } else { if (!isSameOrigin) { return new Response(`Cross-site ${request.method} form submissions are forbidden`, { status: 403, }) } }
return next()})
function hasFormLikeHeader(contentType: string | null): boolean { if (contentType) { for (const FORM_CONTENT_TYPE of FORM_CONTENT_TYPES) { if (contentType.toLowerCase().includes(FORM_CONTENT_TYPE)) { return true } } } return false}
最後在 src/middleware/index.ts
中套用這個 middleware:
import { sequence } from 'astro:middleware'import { originCheck } from './originCheck'
export const onRequest = sequence( // 必須將 `originCheck` 放在 middleware 的第一個 originCheck, ...)
- 作者:Lucas Yang
- 文章連結:https://star-note-lucas.me/posts/astro-except-origin-check
- 版權聲明:本部落格所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明出處。