Skip to content
>_ TrueFileSize.com
··7 min read

Image Metadata Privacy Risks — EXIF Data Leaks and How to Strip Them

Every photo you take contains hidden metadata — GPS coordinates, camera model, exact timestamp, software used, and sometimes your name. When users upload photos to your app, you may be storing and serving this private data. Here's what's exposed and how to strip it.

What EXIF Data Contains

| Field | Example | Privacy Risk | |-------|---------|-------------| | GPS Coordinates | 37.7749, -122.4194 | Exact location where photo was taken | | Date/Time | 2025-04-28 14:30:22 | When the photo was taken | | Camera Model | iPhone 15 Pro Max | Device identification | | Serial Number | ABCD1234567 | Unique device tracking | | Software | Adobe Photoshop 2025 | Editing software used | | Author/Copyright | John Doe | Real name exposure | | Thumbnail | Embedded JPEG | May contain original un-cropped image |

Real-World Privacy Incidents

  • John McAfee (2012): EXIF GPS data in a photo posted by Vice magazine revealed his location in Guatemala, leading to his arrest
  • Anonymous users on 4chan: Multiple users deanonymized via EXIF GPS data in uploaded photos
  • Cat photos on social media: Home addresses exposed via embedded GPS coordinates

How to View EXIF Data

# ExifTool (most comprehensive)
exiftool photo.jpg

# ImageMagick
identify -verbose photo.jpg | grep -i gps

# Python
from PIL import Image
from PIL.ExifTags import TAGS
img = Image.open('photo.jpg')
exif = img._getexif()
for tag_id, value in exif.items():
    print(f"{TAGS.get(tag_id, tag_id)}: {value}")

How to Strip Metadata

Node.js (sharp) — Recommended for Web Apps

import sharp from 'sharp';

// Strip ALL metadata during processing
await sharp('user-upload.jpg')
  .rotate()                    // Auto-rotate from EXIF orientation first
  .resize(2000, 2000, { fit: 'inside', withoutEnlargement: true })
  .jpeg({ quality: 85 })      // Re-encode — strips all EXIF
  .toFile('clean-output.jpg');

// Or minimal — just strip metadata
await sharp('input.jpg')
  .rotate()
  .toFile('stripped.jpg');
// sharp strips EXIF by default unless you pass { withMetadata: true }

Python (Pillow)

from PIL import Image

def strip_exif(input_path, output_path):
    img = Image.open(input_path)
    # Create new image without EXIF
    data = list(img.getdata())
    clean = Image.new(img.mode, img.size)
    clean.putdata(data)
    clean.save(output_path, quality=85)

strip_exif('photo.jpg', 'clean.jpg')

CLI (ExifTool)

# Strip all metadata (in-place)
exiftool -all= photo.jpg

# Strip all metadata, keep orientation
exiftool -all= -TagsFromFile @ -Orientation photo.jpg

# Batch strip entire directory
exiftool -all= -r ./uploads/

CLI (ImageMagick)

mogrify -strip photo.jpg

What Your App Should Do

// Express upload handler — ALWAYS strip metadata
import multer from 'multer';
import sharp from 'sharp';

app.post('/upload/avatar', upload.single('avatar'), async (req, res) => {
  // 1. Validate file type (magic bytes)
  // 2. Strip metadata + resize
  const cleanPath = `uploads/${crypto.randomUUID()}.jpg`;
  await sharp(req.file.path)
    .rotate()
    .resize(400, 400, { fit: 'cover' })
    .jpeg({ quality: 85 })
    .toFile(cleanPath);

  // 3. Delete original (with EXIF intact)
  fs.unlinkSync(req.file.path);

  // 4. Serve the clean version
  res.json({ url: `/avatars/${path.basename(cleanPath)}` });
});

Which Platforms Strip EXIF?

| Platform | Strips GPS | Strips All EXIF | Notes | |----------|-----------|----------------|-------| | Facebook | Yes | Mostly | Keeps some metadata for internal use | | Instagram | Yes | Mostly | Re-encodes images | | Twitter/X | Yes | Yes | Strips everything | | WhatsApp | Yes | Yes | Heavy re-compression | | iCloud Photos | No | No | Preserves all EXIF | | Google Photos | Partial | No | Preserves for user, strips for sharing | | Your app | You decide | You must implement | Default is to keep everything |

Test with Sample Images

Download images with various metadata profiles:

| Test | File | What to Check | |------|------|--------------| | Standard JPEG | sample-500kb.jpg | Check EXIF with exiftool | | Transparent PNG | transparent-logo.png | PNG may have tEXt chunks | | Large photo | sample-5mb.jpg | More EXIF data typically |

Run exiftool <file> before and after your stripping pipeline to verify.

OWASP Reference

  • OWASP: Metadata Privacy
  • GDPR: EXIF GPS data is personal data under Article 4 — stripping is a compliance requirement

See also: File Upload Security Checklist · Image Processing Pipeline Testing