mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
fix: checkOrigin
headers check (#12632)
* Merge commit from fork * fix: enforce check origin logic * address feedback * --amend
This commit is contained in:
parent
839979d24f
commit
e7d14c374b
3 changed files with 57 additions and 15 deletions
5
.changeset/swift-pandas-serve.md
Normal file
5
.changeset/swift-pandas-serve.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes an issue where the `checkOrigin` feature wasn't correctly checking the `content-type` header
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
Loading…
Reference in a new issue