VYPR
Moderate severityNVD Advisory· Published Dec 18, 2024· Updated Dec 18, 2024

Bypass of CSRF Middleware in Astro

CVE-2024-56140

Description

Astro is a web framework for content-driven websites. In affected versions a bug in Astro’s CSRF-protection middleware allows requests to bypass CSRF checks. When the security.checkOrigin configuration option is set to true, Astro middleware will perform a CSRF check. However, a vulnerability exists that can bypass this security. A semicolon-delimited parameter is allowed after the type in Content-Type. Web browsers will treat a Content-Type such as application/x-www-form-urlencoded; abc as a simple request and will not perform preflight validation. In this case, CSRF is not blocked as expected. Additionally, the Content-Type header is not required for a request. This issue has been addressed in version 4.16.17 and all users are advised to upgrade. There are no known workarounds for this vulnerability.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
astronpm
< 4.16.174.16.17

Affected products

1

Patches

1
e7d14c374b9d

fix: `checkOrigin` headers check (#12632)

https://github.com/withastro/astroEmanuele StoppaDec 5, 2024via ghsa
3 files changed · +57 15
  • .changeset/swift-pandas-serve.md+5 0 added
    @@ -0,0 +1,5 @@
    +---
    +'astro': patch
    +---
    +
    +Fixes an issue where the `checkOrigin` feature wasn't correctly checking the `content-type` header
    
  • packages/astro/src/core/app/middlewares.ts+36 15 modified
    @@ -21,22 +21,43 @@ const FORM_CONTENT_TYPES = [
     export function createOriginCheckMiddleware(): MiddlewareHandler {
     	return defineMiddleware((context, next) => {
     		const { request, url } = context;
    -		const contentType = request.headers.get('content-type');
    -		if (contentType) {
    -			if (FORM_CONTENT_TYPES.includes(contentType.toLowerCase())) {
    -				const forbidden =
    -					(request.method === 'POST' ||
    -						request.method === 'PUT' ||
    -						request.method === 'PATCH' ||
    -						request.method === 'DELETE') &&
    -					request.headers.get('origin') !== url.origin;
    -				if (forbidden) {
    -					return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
    -						status: 403,
    -					});
    -				}
    +		if (request.method === "GET") {
    +			return next();
    +		}
    +		const sameOrigin =
    +			(request.method === 'POST' ||
    +				request.method === 'PUT' ||
    +				request.method === 'PATCH' ||
    +				request.method === 'DELETE') &&
    +			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 && !sameOrigin) {
    +				return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
    +					status: 403,
    +				});
    +			}
    +		} else {
    +			if (!sameOrigin) {
    +				return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
    +					status: 403,
    +				});
     			}
     		}
    -		return next();
    +
    +		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;
    +}
    
  • packages/astro/test/csrf-protection.test.js+16 0 modified
    @@ -46,6 +46,22 @@ describe('CSRF origin check', () => {
     		});
     		response = await app.render(request);
     		assert.equal(response.status, 403);
    +
    +		request = new Request('http://example.com/api/', {
    +			headers: { origin: 'http://loreum.com', 'content-type': 'application/x-www-form-urlencoded; some-other-value' },
    +			method: 'POST',
    +		});
    +		response = await app.render(request);
    +		assert.equal(response.status, 403);
    +
    +		request = new Request('http://example.com/api/', {
    +			headers: { origin: 'http://loreum.com', },
    +			method: 'POST',
    +			credentials: 'include',
    +			body: new Blob(["a=b"],{})
    +		});
    +		response = await app.render(request);
    +		assert.equal(response.status, 403);
     	});
     
     	it("return 403 when the origin doesn't match and calling a PUT", async () => {
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.