prevent ddos on img

This commit is contained in:
Devin Zuczek
2026-07-08 09:53:39 -04:00
parent 1bb8389bcc
commit 390843c679
2 changed files with 16 additions and 4 deletions
+8 -4
View File
@@ -19,8 +19,12 @@ const FALLBACK_ASSET_PATH = '/DefaultProfileImage.jpg'
*/
const CACHE_CONTROL = 'public, max-age=31536000, immutable'
/** Upper bound on a requested output dimension; guards against abuse. */
const MAX_DIMENSION = 4096
/**
* Allowed output dimensions. Restricting resizes to a small fixed set caps the
* number of distinct variants an attacker can request, so they can't blow past
* the edge cache and force the (expensive) WASM resize on every hit.
*/
const ALLOWED_DIMENSIONS = new Set([128, 256, 512, 1024])
/** JPEG quality used when re-encoding a resized image. */
const RESIZE_JPEG_QUALITY = 90
@@ -33,11 +37,11 @@ interface Transform {
cropSquare: boolean
}
/** Parse a positive-integer dimension query param, or `undefined` if invalid/absent. */
/** Parse a dimension query param, or `undefined` if absent or not an allowed size. */
function parseDimension(value: string | undefined): number | undefined {
if (value === undefined) return undefined
const n = Number(value)
if (!Number.isInteger(n) || n <= 0 || n > MAX_DIMENSION) return undefined
if (!Number.isInteger(n) || !ALLOWED_DIMENSIONS.has(n)) return undefined
return n
}
@@ -196,6 +196,14 @@ describe('img endpoints', () => {
expect(new Uint8Array(await res.arrayBuffer())).toEqual(full)
})
it('ignores a ?width outside the allowed sizes and serves the original', async () => {
const full = new Uint8Array(await (await SELF.fetch(`${ORIGIN}/RecCenter.jpg`)).arrayBuffer())
// 300 isn't one of 128/256/512/1024, so it's rejected and the source served.
const res = await SELF.fetch(`${ORIGIN}/RecCenter.jpg?width=300`)
expect(res.status).toBe(200)
expect(new Uint8Array(await res.arrayBuffer())).toEqual(full)
})
it('signs the resized body with ?width and ?sig=p1', async () => {
const res = await SELF.fetch(`${ORIGIN}/RecCenter.jpg?width=512&sig=p1`)
expect(res.status).toBe(200)