File upload validation
Nine uploads hit a profile picture endpoint: two real photos and seven files that should never be stored as sent. Turn on checks until every one is handled.
Goal
The profile picture endpoint accepts anything today and saves it in the public folder under the name the client sent. A test suite of nine uploads is ready: two real photos and seven files that should never be stored as they are.
Turn checks on and off and watch what the server would store. Nothing is uploaded or executed; each row is a description of a file and the decision the server makes about it.
Server checks, in order
| File | Claimed | First bytes | Size | Server |
|---|---|---|---|---|
avatar.pngA real photo | image/png | 89 50 4e 47 0d 0a 1a 0a | 182 kB | accepted Stored as an image. |
Holiday Photo.JPGA real photo | image/jpeg | ff d8 ff e0 00 10 4a 46 | 2.3 MB | accepted Stored as an image. |
shell.phpContains server-side script | image/png | 3c 3f 70 68 70 20 2f 2f | 1 kB | dangerous Stored in the web root with its own name, where the server may run it as PHP. |
avatar.php.pngContains server-side script | image/png | 3c 3f 70 68 70 20 2f 2f | 1 kB | dangerous Stored in the web root with its own name, where the server may run it as PHP. |
badge.jpgContains script text hidden in a JPEG comment segment | image/jpeg | ff d8 ff fe 00 2c 3c 3f | 48 kB | dangerous Stored in the web root with its own name, where the server may run it as PHP. |
logo.svgContains an inline <script> element | image/svg+xml | 3c 73 76 67 20 78 6d 6c | 3 kB | dangerous Served with a type the browser can sniff as a page, so the script runs on your origin. |
../../static/js/app.pngContains a path that climbs out of the upload folder | image/png | 89 50 4e 47 0d 0a 1a 0a | 40 kB | dangerous Stored at the client’s path, overwriting /static/js/app.png. |
scan.pngContains a 96 MB file meant to exhaust disk and decoder memory | image/png | 89 50 4e 47 0d 0a 1a 0a | 96 MB | dangerous Stored a 96 MB file; repeated uploads fill the disk and stall the thumbnailer. |
profile.pngContains an HTML page saved with a .png name | image/png | 3c 21 64 6f 63 74 79 70 | 2 kB | dangerous Served with a type the browser can sniff as a page, so the script runs on your origin. |
Steps
01Turn on the checks that keep both real photos working and leave none of the seven bad uploads stored in a dangerous way.
Do this in the workbench above. This step completes itself when the condition is met.
02Why does “Accept if the Content-Type header says image/*” stop nothing here?
03Which upload passes the extension and first-byte checks but still carries script? Give its filename.
04Why is SVG left off the allowlist for profile pictures?
05What does storing under a random name outside the web root prevent?