aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md20
1 files changed, 20 insertions, 0 deletions
diff --git a/README.md b/README.md
index 5fff70c..9043f4b 100644
--- a/README.md
+++ b/README.md
@@ -153,6 +153,26 @@ A session fixation attack works like this: the attacker visits the site and gets
When a login fails, our error message says "Invalid username or password" rather than "Username not found" or "Wrong password". Similarly, the forgot-password form always says "If that email is registered, a reset link has been sent." This is deliberate: if the server told you *which* part was wrong, an attacker could probe the system to build a list of valid usernames or emails, then target those accounts specifically with brute-force or phishing attacks.
+### Upload security (preventing unwanted content)
+
+A classic web vulnerability is allowing users to upload arbitrary files — an attacker might upload a `.php` script disguised as an image and then request it directly to execute server-side code. Camagru prevents this with multiple layers:
+
+1. **No direct file uploads.** Users don't upload files via a traditional `<input type="file">` form submission. Instead, images (from webcam or file picker) are read client-side into a base64 data URL and sent as JSON. There's no `$_FILES` handling or `move_uploaded_file()` — so the usual file upload attack vector doesn't apply.
+
+2. **GD re-encoding.** The base64 data is decoded and passed to `imagecreatefromstring()`, which parses it as an actual image. If the data isn't a valid image (JPEG, PNG, GIF, etc.), GD rejects it and returns `false`. Even if a valid image contains embedded malicious data (e.g. PHP code hidden in EXIF metadata or a polyglot file), the image is then re-encoded through `imagejpeg()` — this produces a clean JPEG from pixel data only, stripping any non-image content.
+
+3. **Size limit.** The server rejects base64 payloads larger than 10 MB before any processing, preventing memory exhaustion from oversized uploads.
+
+4. **Server-controlled filenames.** Output filenames are generated server-side (`{user_id}_{timestamp}_{random}.jpg`) — the user has no control over the filename or extension, so they can't trick the server into creating a `.php` file.
+
+5. **Front controller architecture.** Nginx routes all requests to `index.php`. Even if an attacker somehow placed a malicious file in the uploads directory, Nginx wouldn't execute it as PHP — only the front controller entry point is configured for PHP processing.
+
+6. **Storage capacity limits.** Without limits, a malicious user could fill up the server's disk — either from one account or by creating many accounts. Two caps are enforced before any image processing begins:
+ - **Per-user:** 50 posts maximum (~3.2 MB per user at ~65 KB/image)
+ - **Site-wide:** 10,000 posts total (~650 MB on disk)
+
+ The per-user limit prevents a single account from monopolizing storage. The site-wide limit is the real safety net — it caps total disk usage regardless of how many accounts exist. Both are checked before image processing starts, so hitting the limit doesn't waste server CPU.
+
## Image format choices
- **Overlays** are PNG (alpha channel required for transparency)