Skip to content

Commit adf630e

Browse files
committed
feat(rsc): allow host or forwarded
1 parent 7a49d14 commit adf630e

File tree

1 file changed

+42
-11
lines changed

1 file changed

+42
-11
lines changed

packages/next/src/server/app-render/action-handler.ts

+42-11
Original file line numberDiff line numberDiff line change
@@ -517,24 +517,37 @@ export async function handleAction({
517517
// This might be an old browser that doesn't send `host` header. We ignore
518518
// this case.
519519
warning = 'Missing `origin` header from a forwarded Server Actions request.'
520-
} else if (!host || originDomain !== host.value) {
520+
} else if (
521+
!host ||
522+
// ensure that the host header matches the origin header or forwarded host header
523+
originDomain !== hostHeader ||
524+
originDomain !== forwardedHostHeader
525+
) {
521526
// If the customer sets a list of allowed origins, we'll allow the request.
522527
// These are considered safe but might be different from forwarded host set
523528
// by the infra (i.e. reverse proxies).
524529
if (isCsrfOriginAllowed(originDomain, serverActions?.allowedOrigins)) {
525530
// Ignore it
526531
} else {
527532
if (host) {
528-
// This seems to be an CSRF attack. We should not proceed the action.
529-
console.error(
530-
`\`${
531-
host.type
532-
}\` header with value \`${limitUntrustedHeaderValueForLogs(
533-
host.value
534-
)}\` does not match \`origin\` header with value \`${limitUntrustedHeaderValueForLogs(
535-
originDomain
536-
)}\` from a forwarded Server Actions request. Aborting the action.`
537-
)
533+
// This seems to be an CSRF attack. We should not proceed the action
534+
// We have have either the `x-forwarded-host` or `host` header, but neither match the `origin` header.
535+
if (hostHeader && originDomain !== hostHeader) {
536+
logCSRFError({
537+
originDomain,
538+
headerType: HostType.Host,
539+
headerValue: hostHeader,
540+
})
541+
} else if (
542+
forwardedHostHeader &&
543+
originDomain !== forwardedHostHeader
544+
) {
545+
logCSRFError({
546+
originDomain,
547+
headerType: HostType.XForwardedHost,
548+
headerValue: forwardedHostHeader,
549+
})
550+
}
538551
} else {
539552
// This is an attack. We should not proceed the action.
540553
console.error(
@@ -1065,3 +1078,21 @@ function getActionModIdOrError(
10651078
)
10661079
}
10671080
}
1081+
1082+
function logCSRFError({
1083+
originDomain,
1084+
headerType,
1085+
headerValue,
1086+
}: {
1087+
originDomain: string
1088+
headerType: HostType
1089+
headerValue: string
1090+
}) {
1091+
console.error(
1092+
`\`${headerType}\` header with value \`${limitUntrustedHeaderValueForLogs(
1093+
headerValue
1094+
)}\` does not match \`origin\` header with value \`${limitUntrustedHeaderValueForLogs(
1095+
originDomain
1096+
)}\` from a forwarded Server Actions request. Aborting the action.`
1097+
)
1098+
}

0 commit comments

Comments
 (0)