Cegah Upload Abusive API dimulai dengan jawaban langsung: pastikan API hanya menerima permintaan upload dari klien terautentikasi, segera menolak file yang tidak valid, dan batasi frekuensi permintaan untuk mencegah penyalahgunaan. Artikel ini menguraikan lapisan autentikasi, validasi MIME serta ukuran file, pembatasan lintas sumber, serta strategi rate limit yang terintegrasi, lengkap dengan observabilitas dan pola respons error.

1. Arsitektur autentikasi dan sesi/token

Gunakan metode autentikasi yang membuat setiap upload dapat diautentikasi dengan token JWT terstruktur atau sesi server. Dalam konteks API publik, JWT lebih praktis karena tidak memerlukan penyimpanan sisi server, tetapi pastikan token menempel pada Authorization: Bearer <token> dan diverifikasi di setiap middleware.

Best practice mencakup:

  • Penandatanganan token dengan secret rotasi: simpan secret di vault (misalnya AWS Secrets Manager) dan rotasi secara berkala. Hindari hardcode nilai di kode.
  • Gunakan claim minimal: sertakan user_id, exp, dan informasi rol untuk memvalidasi hak upload.
  • Penegakan refresh token: buat endpoint refresh terpisah yang memvalidasi session panjang dan mengeluarkan token akses singkat.
  • Jika sesi diperlukan: gunakan store seperti Redis dengan cookie HTTP-only dan SameSite=strict untuk menghindari pencurian sesi.

Contoh middleware Express:

const jwt = require('jsonwebtoken');
const verifyToken = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader) return res.status(401).json({ error: 'Token dibutuhkan' });
  const token = authHeader.split(' ')[1];
  jwt.verify(token, process.env.JWT_SECRET, (err, payload) => {
    if (err) return res.status(401).json({ error: 'Token tidak valid' });
    req.user = payload;
    next();
  });
};

Middleware ini mengikat upload hanya kepada identitas yang tervalidasi.

2. Validasi upload file

Jangan biarkan file mencapai logika bisnis tanpa melakukan validasi berlapis. Terapkan validasi berikut sebelum menyimpan berkas:

  • Validasi MIME type dan ekstensi dengan whitelist yang dikelola, misalnya hanya image/jpeg atau application/pdf.
  • Batas ukuran file menggunakan parser upload seperti multer dengan limits: { fileSize: 5 * 1024 * 1024 } sehingga file oversized ditolak awal.
  • Pemeriksaan CSRF/Origin ketika API diakses melalui browser: pastikan header Origin atau Referer sesuai daftar domain tepercaya dan sertakan token CSRF untuk endpoint state-changing.
  • Jangan langsung menulis file sebelum verifikasi: simpan di temporary buffer, periksa konten, lalu pindahkan ke storage berskala.

Contoh middleware validasi Express dengan multer:

const multer = require('multer');
const upload = multer({
  limits: { fileSize: 5 * 1024 * 1024 },
  fileFilter: (req, file, cb) => {
    const allowed = ['image/jpeg', 'image/png'];
    cb(null, allowed.includes(file.mimetype));
  }
});

const originCheck = (req, res, next) => {
  const allowedOrigins = ['https://app.domain.tld'];
  const origin = req.headers.origin;
  if (origin && !allowedOrigins.includes(origin)) {
    return res.status(403).json({ error: 'Origin tidak diizinkan' });
  }
  next();
};

Dengan mekanisme ini, file yang tidak valid gagal sebelum mencapai penanganan utama.

3. Rate limit per user atau IP

Rate limit mencegah abuse walau autentikasi valid. Strategi efisien adalah memadukan antara limit per user (berdasarkan user_id) dan fallback per IP untuk klien anonimus.

Prinsip yang perlu diterapkan:

  • Gunakan store cepat seperti Redis untuk menyimpan counter per key (token atau IP) dengan TTL pendek.
  • Gunakan pendekatan sliding window agar pengguna tidak kena block mendadak ketika limit hampir habis.
  • Sertakan header respons seperti X-RateLimit-Remaining agar klien tahu sisa kuota.
  • Ketika melebihi batas, kirimkan 429 dengan penjelasan kapan dapat mencoba kembali.

Contoh integrasi dengan express-rate-limit:

const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');

const uploadLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 10,
  standardHeaders: true,
  legacyHeaders: false,
  store: new RedisStore({ client: redisClient }),
  keyGenerator: (req) => req.user?.user_id || req.ip,
  handler: (req, res) => res.status(429).json({ error: 'Terlalu banyak upload, coba lagi nanti' })
});

Perhatikan trade-off: limit sangat ketat memberi proteksi, namun bisa mengganggu pengguna sah yang mengunggah banyak file sekaligus. Monitor log untuk menyesuaikan max atau window.

4. Alur middleware upload terintegrasi

Gabungkan semua lapisan dalam satu urutan middleware agar logika tetap konsisten:

  1. Header security (CORS dan Origin check).
  2. Autentikasi token/session.
  3. Rate limit berdasarkan user/IP.
  4. CSRF token validation jika berlaku.
  5. Multer/validator untuk MIME dan ukuran.
  6. Handler bisnis menyimpan file ke storage.

Contoh route Express:

router.post('/upload',
  originCheck,
  verifyToken,
  uploadLimiter,
  upload.single('file'),
  async (req, res) => {
    if (!req.file) {
      return res.status(400).json({ error: 'File tidak diterima' });
    }
    // Simpan ke storage, catat metadata, dll.
    res.status(201).json({ message: 'Upload berhasil' });
  }
);

Setiap middleware berkontribusi pada ketahanan: jika token bermasalah atau rate limit tercapai, proses berhenti lebih awal.

5. Observability dan penanganan error

Observasi aktif membuat penyalahgunaan terdeteksi cepat:

  • Log permintaan upload yang ditolak: simpan user, IP, alasan penolakan (MIME tidak valid, CSRF gagal, rate limit). Gunakan format JSON untuk integrasi dengan log aggregator.
  • Metrics seperti jumlah upload sukses/ditolak, latensi penyimpanan, serta counter rate limit. Kirim ke Prometheus atau observability stack yang digunakan.
  • Error handling konsisten: untuk setiap penolakan kirim respons yang jelas dan status HTTP yang sesuai (401 untuk autentikasi, 403 untuk origin, 409/413 untuk validasi file, 429 untuk rate limit).

Tambahkan requestId pada header untuk mempermudah korelasi log dengan permintaan tertentu.

Kesimpulan

Menggabungkan autentikasi aman, validasi upload, dan rate limit dalam satu alur middleware membuat API upload jauh lebih tahan terhadap abuse. Tambahkan observability supaya kejadian abnormal bisa cepat dideteksi, dan berikan respons error yang transparan agar klien tahu langkah selanjutnya. Pastikan juga rahasia seperti JWT secret disimpan terpisah dan middleware saling bergantung pada data yang sama untuk menghindari kebocoran.