Vektor Serangan pada Pipeline Upload Gambar

Endpoint upload gambar sering menjadi target eksploitasi karena kompleksitas parser berkas biner. Penyerang memanfaatkan kelemahan ini melalui berkas polyglot (berkas valid sebagai gambar sekaligus script PHP/JS), decompression bomb (pixel flood), dan kebocoran data sensitif via metadata EXIF/XMP (seperti koordinat GPS dan model perangkat).

Selain itu, manipulasi chunk metadata seperti profil warna ICC (International Color Consortium) atau chunk gamma (gAMA dan sRGB pada format PNG) dapat memicu parsing desynchronization. Perbedaan cara parser HTTP gateway, image processor, dan browser menginterpretasikan header warna memungkinkan injeksi payload biner tersembunyi yang lolos dari filter tanpa terdeteksi.

Arsitektur Pemrosesan Gambar Aman

Pola paling aman memproses gambar dari pengguna adalah zero-trust byte stream. Gambar tidak boleh disimpan langsung ke storage publik atau disajikan langsung ke browser tanpa normalisasi total.

  1. Magic Number Validation: Cek header signature biner, bukan header Content-Type HTTP atau ekstensi berkas.
  2. Metadata Stripping & Gamma Sanitization: Hapus seluruh metadata non-kritis dan standarisasi profil warna ke sRGB standard gamma.
  3. Re-encoding ke Kanvas Baru: Decode raw pixel dan encode ulang ke format target untuk merusak integritas payload polyglot.
  4. Worker Sandboxing & Resource Limiting: Batasi konsumsi CPU, memori, dan dimensi piksel sebelum library C mendekompresi payload.

Validasi Awal: Magic Bytes dan Header Parsing

Gunakan pustaka berbasis buffer scanning ringan sebelum menyerahkan stream ke decoder gambar penuh. Contoh validasi magic number menggunakan pustaka file-type di Node.js:

import { fileTypeFromBuffer } from 'file-type';

const ALLOWED_MIME = new Set(['image/jpeg', 'image/png', 'image/webp']);

export async function validateImageStream(buffer) {
  // Hanya membaca offset byte awal tanpa decode piksel penuh
  const type = await fileTypeFromBuffer(buffer);
  
  if (!type || !ALLOWED_MIME.has(type.mime)) {
    throw new Error('Format berkas tidak didukung atau magic byte tidak valid.');
  }

  return type;
}

Normalisasi Gamma dan Strip Metadata Menggunakan Sharp (Libvips)

Libvips mengeksekusi decoding dengan pipeline streaming berbasis memori rendah. Menggunakan library sharp di Node.js, metadata sensitif dan profil ICC kustom dapat disanitasi sekaligus menormalkan ruang warna ke sRGB linear-correct secara deterministik.

import sharp from 'sharp';

const MAX_PIXELS = 4096 * 4096; // 16 Megapiksel ceiling

export async function sanitizeAndReencode(inputBuffer) {
  const pipeline = sharp(inputBuffer, {
    failOnError: true,
    limitInputPixels: MAX_PIXELS // Cegah decompression bombs
  });

  const metadata = await pipeline.metadata();

  // Validasi batas dimensi logis
  if (!metadata.width || !metadata.height || metadata.width > 4096 || metadata.height > 4096) {
    throw new Error('Dimensi gambar melebihi batas yang diizinkan.');
  }

  return await pipeline
    .rotate() // Normalisasi orientasi EXIF sebelum dibuang
    .toColorspace('srgb') // Konversi profil warna non-standar ke sRGB standard gamma
    // Strip seluruh EXIF, XMP, IPTC, dan ICC chunk manipulatif
    .withMetadata({
      strip: true
    })
    // Re-encode ke format bersih; struktur biner lama sepenuhnya dibuang
    .webp({ quality: 85, effort: 4 })
    .toBuffer();
}

Mengapa Re-encoding Mematikan Polyglot Exploits?

Polyglot file mengandalkan parser toleran yang membaca komentar berkas atau payload di luar marker End-of-File (EOF). Saat Sharp/Libvips memproses buffer:

  • Hanya stream piksel valid yang di-decode ke memory buffer.
  • Struktur berkas lama dibuang sepenuhnya.
  • Buffer piksel di-encode ulang menjadi struktur WebP/JPEG baru yang hanya memuat data piksel dan header standar.
  • Seluruh payload arbitrer yang diselipkan pada komentar JPEG, chunk tEXt PNG, atau trailer data otomatis musnah.

Isolasi Proses: Sandboxing Worker

Meskipun Libvips relatif aman, vulnerability level C (seperti buffer overflow) tetap dapat terjadi pada zero-day library libjpeg-turbo atau libpng. Pisahkan pemrosesan gambar ke dalam isolated worker atau container terpisah dengan limitasi cgroups OS:

# Contoh limitasi container worker gambar via docker-compose
services:
  image-processor:
    image: image-worker:latest
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp:size=128M,exec,nodev,nosuid

Trade-offs dan Pertimbangan Teknis

Melakukan re-encoding dan normalisasi warna memiliki konsekuensi teknis:

  • Color Shift: Mengonversi profil warna khusus (misal: Adobe RGB atau Display P3) ke standar sRGB dapat menghasilkan sedikit pergeseran saturasi warna. Ini adalah trade-off yang wajar demi kestabilan rendering di web.
  • Beban CPU: Operasi re-encoding membutuhkan alokasi siklus CPU tinggi. Selalu pasang queue (seperti Redis BullMQ) dan rate limiter pada endpoint upload untuk meredam lonjakan beban processing serentak.