Upload handling
- Chunked transfer — images arrive in 512 KB chunks, which keeps each request small and avoids depending on large PHP upload limits on shared hosting.
- Randomized storage — each upload gets a random 36-character token; chunks are stored under that token with owner-only permissions (0700 directory, 0600 files).
- No original filenames — your filename is used only for display and filename forensics; nothing is stored under a user-controlled name, which removes path-traversal and overwrite risks.
- Type validation by content — after assembly, the file is validated by magic numbers, MIME detection and pixel decoding. Files that are not genuinely JPG/PNG/WebP are rejected and deleted. Extensions are never trusted.
- Size limits — 20 MB maximum, enforced client- and server-side.
Temporary processing & deletion
- Deletion after analysis — the assembled original is deleted via
unlink()immediately after the report is generated (a small thumbnail remains for the report page). - Automatic expiry — a cleanup routine runs on every request and removes anything older than 15 minutes, including abandoned partial uploads.
- Report expiry — reports (and their JSON exports) stop resolving when their token directory is removed; thumbnails refuse to serve past expiry.
- No copies elsewhere — the application does not copy uploads to any database, cloud storage or third-party processor; analysis is plain PHP on the web server itself. (Application-level retention is zero; any infrastructure-level backups are governed by the hosting provider's policies, which we do not control.)
Access controls
- Storage is not web-accessible — direct requests to
/uploads/…return 404 at the web-server level; files are served only through internal endpoints that validate the token format. - Unpredictable report URLs — report links require the random token; there is no listing, enumeration or search of reports.
- Noindex everywhere it matters — report and thumbnail responses send
X-Robots-Tag: noindexheaders and the pages carry noindex meta tags, so temporary URLs can't become search content even if someone pastes one publicly. - Rate limiting — repeated analysis of the identical file is throttled within a short window, using only a truncated hash prefix for comparison (no identifying data stored).
Application hardening
- XSS protection — metadata is attacker-controlled data. Every metadata value, filename and string rendered on a report page is HTML-escaped at output; JSON export sets nosniff and downloads as an attachment.
- No code execution in storage — the upload directory blocks PHP execution and directory listings via server configuration.
- Token and path safety — all token/path inputs are filtered to a strict allowlist (
[A-Za-z0-9_-]) before touching the filesystem. - Security headers —
X-Content-Type-Options: nosniff,Referrer-Policy,X-Frame-Optionsand a restrictivePermissions-Policyare set site-wide via.htaccess. - Minimal surface — config and environment-style files are denied by the server configuration; the production deployment contains no test or debug endpoints.
Honest boundaries
- Report links rely on link secrecy, like most web links. Don't paste a report URL anywhere you wouldn't want the report seen until it expires.
- Analysis happens on a shared-hosting web server in memory/disk during processing; we operate it with least privilege, but "temporary server processing" is inherent to any server-side analyzer.
- If you need analysis that never leaves your device at all, use the browser-side preflight (dimensions, SHA-256, entropy preview) and compare mode — they run entirely client-side.
Reporting a vulnerability
Found something? Please email hello@picturematters.in with details and reproduction steps. We take security reports seriously and will respond as quickly as we can — please allow a reasonable window for fixes before public disclosure.