Node.js İle S3 Üzerinden Güvenli Medya Sunumu Nasıl Yapılır?

Node.js İle S3 Üzerinden Güvenli Medya Sunumu Nasıl Yapılır?
Node.js İle S3 Üzerinden Güvenli Medya Sunumu Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Projeye başlamadan önce sisteminizde Node.js'in güncel bir LTS (Long Term Support) sürümünün kurulu olduğundan emin olmalısınız. AWS S3 ile etkileşim kurmak için @aws-sdk/client-s3 kütüphanesini kullanacağız. Bu kütüphane, modüler yapısı sayesinde projenizin boyutunu optimize eder.

  • Node.js v20 veya üzeri sürüm.
  • Bir AWS hesabı ve yapılandırılmış bir S3 bucket.
  • IAM (Identity and Access Management) üzerinden oluşturulmuş erişim anahtarları (Access Key ID ve Secret Access Key).
  • Proje dizininde npm init -y komutuyla başlatılmış bir Node.js projesi.

Öncelikle gerekli paketleri projenize dahil edin:

npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner dotenv

AWS S3 İstemcisini Yapılandırma

Güvenli bir bağlantı kurmak için kimlik bilgilerini doğrudan kod içerisine yazmamalı, .env dosyası üzerinden yönetmelisiniz. Aşağıdaki kod bloğu, AWS S3 istemcisini başlatmak için gereken temel yapılandırmayı gösterir.

require('dotenv').config();
const { S3Client } = require('@aws-sdk/client-s3');

const s3Client = new S3Client({
  region: process.env.AWS_REGION,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  },
});

module.exports = { s3Client };

Bu yapılandırma, uygulamanızın S3 servisleri ile güvenli bir şekilde haberleşmesini sağlar. region bilgisi, bucket'ınızın oluşturulduğu veri merkezini temsil eder.

Güvenli Medya Yükleme Süreci

Medya dosyalarını S3'e yüklerken, dosyaların herkese açık (public) olmaması gerekir. Dosyaları "private" olarak yükleyerek, sadece yetkili kullanıcıların erişebilmesini sağlarız. Aşağıdaki fonksiyon, bir dosyayı S3'e güvenli bir şekilde yüklemek için kullanılır.

const { PutObjectCommand } = require('@aws-sdk/client-s3');
const { s3Client } = require('./s3Client');

async function uploadFile(fileName, fileContent, contentType) {
  const command = new PutObjectCommand({
    Bucket: process.env.AWS_BUCKET_NAME,
    Key: fileName,
    Body: fileContent,
    ContentType: contentType,
    ACL: 'private', // Dosya erişimini kısıtlar
  });

  return await s3Client.send(command);
}

Bu yaklaşımda ACL: 'private' parametresi, dosyanın internet üzerinden doğrudan erişilebilir olmasını engeller. Dosya, sadece S3'ün sunduğu kimlik doğrulama mekanizmaları ile görüntülenebilir.

İmzalı URL (Presigned URL) ile Erişim Sağlama

Dosyaları güvenli tutmanın en etkili yolu, ihtiyaç anında geçici süreliğine erişim linki oluşturmaktır. "Presigned URL" olarak adlandırılan bu yöntem, belirli bir süre (örneğin 60 saniye) geçerli olan özel bir URL üretir.

const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
const { GetObjectCommand } = require('@aws-sdk/client-s3');

async function getSecureMediaUrl(fileName) {
  const command = new GetObjectCommand({
    Bucket: process.env.AWS_BUCKET_NAME,
    Key: fileName,
  });

  // URL 60 saniye boyunca geçerli kalacaktır
  return await getSignedUrl(s3Client, command, { expiresIn: 60 });
}

Bu yöntem sayesinde, dosyanızın linki çalınsa bile, linkin süresi dolduğunda erişim otomatik olarak kesilecektir. Bu, özellikle premium içerik veya kullanıcıya özel belgeler için vazgeçilmez bir güvenlik katmanıdır.

Medya Sunumu Yöntemlerinin Karşılaştırılması

Yöntem Güvenlik Performans Kullanım Durumu
Public Bucket Çok Düşük Yüksek Genel web sitesi görselleri
CloudFront + OAC Yüksek Çok Yüksek Büyük ölçekli medya dağıtımı
Presigned URL Çok Yüksek Orta Özel dosyalar, raporlar, videolar

Kritik Güvenlik Uyarısı: S3 bucket'larınızın "Block Public Access" ayarlarının her zaman aktif olduğundan emin olun. Dosyaları asla herkese açık (public-read) olarak yüklemeyin. İmzalı URL'leri sunucu tarafında oluşturun ve asla istemci tarafında (frontend) AWS anahtarlarınızı barındırmayın.

Hata Yönetimi ve Debug İpuçları

S3 ile çalışırken en sık karşılaşılan hatalar kimlik doğrulama sorunları ve yanlış bucket isimleridir. try-catch bloklarını kullanarak bu hataları yakalamak ve kullanıcıya anlamlı mesajlar döndürmek önemlidir.

try {
  const url = await getSecureMediaUrl('profil-resmi.jpg');
  console.log('Erişim URL:', url);
} catch (err) {
  console.error('S3 Erişimi başarısız:', err.message);
}

Eğer "Access Denied" hatası alıyorsanız, IAM kullanıcınızın s3:GetObject yetkisinin ilgili bucket için tanımlı olup olmadığını AWS konsolundan kontrol edin.

Sıkça Sorulan Sorular

İmzalı URL'lerin süresi neden kısa tutulmalı?

Güvenlik prensibi olarak, bir yetkilendirme linkinin geçerlilik süresi ne kadar kısa olursa, saldırganların bu linki kötüye kullanma olasılığı o kadar düşer. Genellikle 1-5 dakika arası idealdir.

Dosya yüklerken dosya adlarını neden değiştirmeliyim?

Kullanıcıların yüklediği dosya adlarında boşluk veya Türkçe karakter bulunması S3 tarafında sorun yaratabilir. Ayrıca, dosya isimlerini uuid kullanarak benzersiz yapmak, çakışmaları önler.

CloudFront kullanmak zorunda mıyım?

Zorunlu değilsiniz ancak medya dosyalarınızın dünya genelinde hızlı yüklenmesini istiyorsanız, CloudFront (CDN) kullanmak performans açısından büyük avantaj sağlar.

S3 dışında alternatif var mı?

Evet, MinIO veya DigitalOcean Spaces gibi S3 uyumlu servisleri aynı kod yapısı ile kullanabilirsiniz. Sadece endpoint ayarını değiştirmeniz yeterlidir.

Node.js üzerinde büyük dosyaları nasıl yüklerim?

Büyük dosyalar için Upload sınıfını (multipart upload) kullanmalısınız. Bu, dosyanın parçalar halinde yüklenmesini sağlayarak bağlantı kopmalarında veri kaybını önler.

Yasal Sorumluluk Reddi: Bu rehberdeki kodlar eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce, AWS IAM politikalarınızı "Least Privilege" (En az yetkilendirme) prensibine göre yapılandırın ve tüm API anahtarlarınızı güvenli bir ortamda (AWS Secrets Manager gibi) saklayın.

Performans Optimizasyonu: Multi-part Upload ve Akış Yönetimi

Büyük medya dosyalarıyla çalışırken, standart yükleme yöntemleri ağ kararsızlıklarına karşı savunmasız kalabilir. Node.js üzerinde @aws-sdk/lib-storage kütüphanesini kullanarak, dosyaları parçalara ayırıp paralel bir şekilde S3'e yüklemek, hem hız hem de güvenilirlik açısından en iyi yaklaşımdır. Bu yöntem, özellikle 100MB üzerindeki video dosyaları için kritik öneme sahiptir.

const { Upload } = require("@aws-sdk/lib-storage");
const { S3Client } = require("@aws-sdk/client-s3");
const fs = require("fs");

const uploadLargeFile = async (filePath, bucketName, key) => {
  const parallelUploads3 = new Upload({
    client: new S3Client({ region: "eu-central-1" }),
    params: { 
      Bucket: bucketName, 
      Key: key, 
      Body: fs.createReadStream(filePath) 
    },
    queueSize: 4, // Aynı anda kaç parçanın yükleneceği
    partSize: 1024 * 1024 * 5, // 5MB parçalar halinde
    leavePartsOnError: false,
  });

  parallelUploads3.on("httpUploadProgress", (progress) => {
    console.log(`Yükleme durumu: ${progress.loaded} / ${progress.total}`);
  });

  await parallelUploads3.done();
};

Üretim Ortamında Güvenlik: IAM Politikalarını Sıkılaştırma

Uygulamanızın S3 bucket'ına erişimini sadece belirli işlemlerle kısıtlamak, olası bir sızıntı durumunda hasarı minimize eder. "Least Privilege" prensibi gereği, Node.js uygulamanıza atadığınız IAM kullanıcısı veya rolü için aşağıdaki gibi kısıtlayıcı bir politika tanımlamalısınız.

İşlem Kaynak Amaç
s3:PutObject arn:aws:s3:::medya-bucket/uploads/* Sadece belirli klasöre yükleme
s3:GetObject arn:aws:s3:::medya-bucket/public/* Sadece halka açık medya okuma

Politika dosyanızda Condition bloğu kullanarak, sadece belirli IP adreslerinden gelen isteklere izin verebilir veya s3:x-amz-acl başlığını zorunlu kılarak dosyaların yanlışlıkla herkese açık (public) yüklenmesini engelleyebilirsiniz.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject"],
      "Resource": "arn:aws:s3:::proje-medya-bucket/uploads/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "private"
        }
      }
    }
  ]
}

İleri Seviye Hata Ayıklama (Debugging) İpuçları

Node.js uygulamalarında S3 ile ilgili sorunları çözmek için AWS SDK'nın sunduğu loglama mekanizmasını aktif etmek, ağ katmanındaki hataları görmenizi sağlar. Özellikle 403 Forbidden veya 400 Bad Request hatalarında, SDK'nın döndürdüğü requestId değerini AWS Support ile paylaşmak çözüm sürecini hızlandırır.

  • Request ID Takibi: Her hata nesnesinde bulunan $metadata.requestId değerini loglayın.
  • Middleware Kullanımı: İstek ve yanıtları izlemek için SDK'ya bir middlewareStack ekleyerek tüm trafiği konsola dökebilirsiniz.
  • Network Timeout: S3 bağlantılarınızda requestTimeout süresini ihtiyaca göre optimize edin; çok düşük değerler büyük dosyalarda zaman aşımına neden olur.

Bu yapılandırmalar, uygulamanızın sadece çalışmasını değil, aynı zamanda ölçeklenebilir ve izlenebilir bir yapıda kalmasını sağlar.

Sonuç

Node.js ile S3 üzerinden güvenli medya sunumu yapmak, uygulamanızın mimarisini daha profesyonel ve güvenli hale getirir. Bu rehberde, S3 istemcisinin kurulumundan, güvenli dosya yüklemeye ve imzalı URL'ler ile erişim yönetimine kadar kritik adımları tamamladık. Bir sonraki adım olarak, AWS CloudFront entegrasyonunu inceleyerek medya dosyalarınızı dünyanın farklı noktalarındaki kullanıcılara çok daha düşük gecikme süreleriyle sunmayı hedefleyebilirsiniz.

Bu yazıya tepkinizi paylaşın:
Deniz Aydın

On yıldır dijital yayıncılıkta pratik çözüm rehberleri hazırlıyorum. Karmaşık süreçleri herkesin anlayabileceği adım adım yönergelere dönüştürme konusunda uzmanım.

Yorumlar (0)

Yorum Yaz