API Depolama: Erişim Anahtarı, Bucket Politikası ve İmzalı URL

API depolamada 403 veya AccessDenied hatasıyla takılıp kaldıysanız, bu kılavuz tam da ihtiyacınız olan şeydir: anahtar oluşturmadan bucket politikasına ve imzalı URL'ye kadar.

7 dk Güncellendi 9 Nov 2026

SignatureDoesNotMatch gövdesiyle birlikte 403 Forbidden hatası alıyorsunuz, ancak anahtarı ve secret'ı on kez kontrol ettiniz ve doğru. Ya da daha kötüsü: kendi endpoint'inizde aws s3 ls ile her şey çalışıyor, ancak aynı istek Python kodunuzdan veya başka bir sunucudan yanıt vermiyor. Sorun neredeyse her zaman üç şeyden birindedir: isteğin imzalanma şekli, bucket politikası veya istemciye verdiğiniz adres. Bu metin tam olarak bu üçünü açıklıyor.

S3 protokolü uyumluluğu ne demek ve nerede kırılır

Bir hizmetin "S3 ile uyumlu" olduğunu söylediğimizde, bu onun endpoint'inin AWS Signature Version 4 ile imzalanmış HTTP komutlarını anladığı anlamına gelir. Bu resmi tanımdır, pazarlama iddiası değil. Pratikte bu, boto3, aws-cli, rclone veya s3cmd kullanabileceğiniz ve yalnızca endpoint'i değiştirebileceğiniz anlamına gelir.

Ancak uyumluluk ikili değildir. Pratikte kırıldığı yerler:

  • Virtual-hosted style ile path-style arasındaki fark. https://bucket.example.com/key adresi ile https://example.com/bucket/key aynı değildir. Alt alan adı için DNS wildcard ayarlanmamışsa, ilk durum TLS hatası veya NoSuchBucket ile döner.
  • Çok parçalı işlemler (Multipart Upload). Bazı uygulamalar UploadPartCopy'yi desteklemez ve yalnızca UploadPart'a sahiptir.
  • Versioning ve Object Lock. Bunlar isteğe bağlıdır ve her yerde yoktur.
  • İmza başlıkları. Bir ters proxy Host veya x-amz-date başlığını değiştirirse, imza kırılır ve aynı SignatureDoesNotMatch ortaya çıkar.

Pratik ipucu: her şeyden önce aws s3api get-bucket-location --bucket mybucket --endpoint-url https://s3.example.com ile test edin. Eğer bu yanıt veriyor ama kodunuz vermiyorsa, sorun protokol değil, SDK'nızdır.

Erişim anahtarı: secret'ı neden göremezsiniz ve nasıl saklamalısınız

Erişim anahtarının iki bölümü vardır: kullanıcı adı gibi olan bir Access Key ID ve yalnızca bir kez gösterilen bir Secret Access Key. Onu kaybederseniz, kurtarmanın yolu yoktur; yeni bir anahtar oluşturmalısınız. Bu davranış kasıtlıdır, ürün kusuru değil.

Anahtar yönetiminde üç yaygın hata:

  1. Secret'ı Dockerfile içindeki ortam değişkenine koymak. docker history ile okunabilir.
  2. Tüm hizmetler için tek bir anahtar kullanmak. Bir hizmet sızarsa, her şey sızar.
  3. Anahtarı döndürmemek. İki yıldır çalışan bir anahtar, gizli bir risktir.

Doğru model: her hizmet için minimum yetkiyle ayrı bir kullanıcı oluşturun, secret'ı bir secret manager'da veya chmod 600 izinli bir dosyada saklayın ve anahtar rotasyonunu takvime koyun. Bulut altyapısında çalışıyorsanız, örnek tabanlı IAM rolleri (Instance Role) sabit anahtardan daha iyidir, çünkü anahtar diske yazılmaz.

İnce bir nokta: birçok kişi anahtarı koda sabit kodlamadığı ve .env içine koyduğu için güvende olduğunu düşünür. Ancak .env git'e commit edilmişse, aynı felakettir. git log --all --full-history -- .env ile kontrol edin.

Bucket politikası: policy ve ACL arasındaki fark ve hangisini seçmelisiniz

İki erişim kontrol mekanizması vardır: Bucket Policy (bucket'ın kendisine yapıştırılan bir JSON belgesi) ve ACL (nesne düzeyinde erişim listesi). Tavsiyem açıktır: ACL'yi kapatın ve yalnızca Bucket Policy kullanın. ACL, AWS'de pratikte kullanımdan kaldırılmıştır ve ölçekte yönetimi kâbustur. ACL'ye yalnızca bir durumda başvurun: policy'yi anlamayan ve yalnızca ACL gönderen eski bir istemciniz olduğunda.

Belirli bir klasörü herkese açık okumak için minimum bir policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadAssets",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::mybucket/assets/*"
    }
  ]
}

Bu belgede iki şeye dikkat edin. Birincisi, Principal: "*" yani internetteki herkes. İkincisi, assets/* yani yalnızca o klasör, tüm bucket değil. Yıldızı mybucket/* olarak değiştirirseniz, tüm özel dosyalar da herkese açık olur. Veri sızıntılarında gördüğümüz tam olarak budur.

Ve bir uyarı: s3:ListBucket ile s3:GetObject farklıdır. Yalnızca GetObject verirseniz, kimse dosya listesini göremez ama dosya adını bilirse indirir. Hassas dosyalar için bu yeterli değildir.

İmzalı URL: bucket'ı herkese açık yapmadan nasıl geçici erişim verirsiniz

Presigned URL, secret key'inizle imzalanmış ve sınırlı bir süre geçerli olan bir adrestir. Bu şekilde bucket özel kalır ama kullanıcı belirli bir dosyayı indirebilir.

AWS CLI ile:

aws s3 presign s3://mybucket/report.pdf \
  --expires-in 3600 \
  --endpoint-url https://s3.example.com

3600 sayısı saniyedir, yani bir saat. AWS Signature V4'te maksimum değer yedi gündür (604800 saniye). Daha fazlasını istiyorsanız, CloudFront Signed URL gibi başka bir mekanizma kullanmalısınız.

Burada hata yapıyorlar: imzalı URL'yi veritabanına kaydediyorlar ve süresi dolduktan sonra kullanıcı Request has expired hatası alıyor ve ekip hizmetin bozuk olduğunu düşünüyor. İmzalı URL geçici bir tokendır, kalıcı bir bağlantı değil. Onu anında oluşturun ve gönderin.

İkinci nokta: imza, istek başlıklarına ve parametrelerine duyarlıdır. URL'yi kopyalayıp bir parametre eklerseniz, imza kırılır. Ayrıca URL'yi oluşturan sunucunun saati ile depolama hizmetinin saati arasında 15 dakikadan fazla fark varsa, imza reddedilir. timedatectl status veya chronyc tracking ile saati kontrol edin.

Genel URL, imzalı URL ve proxy arasında seçim

YöntemUygun olduğu durumÖdediğiniz bedel
Genel bucketSite statik dosyaları, görsellerDosya adını tahmin eden herkes indirir
Presigned URLGeçici indirme, tarayıcıdan yüklemeAnında oluşturulmalı; önbelleğe alınamaz
Sunucu tarafından proxyTam kontrol, ayrıntılı logSunucunuzun bant genişliği tüketilir

Genel statik dosyalar için, genel bucket ile birlikte bir CDN en iyi seçimdir, çünkü sunucu bant genişliği maliyetini ortadan kaldırır ve TTFB'yi düşürür. Diğer her şey için presigned URL. Proxy'yi yalnızca ayrıntılı log veya watermark gerektiğinde seçin.

Trafik yükü artıyorsa ve kodu değiştirmeden depolamanın önüne bir önbellek katmanı koymak istiyorsanız, içerik dağıtım ağı mantıklı bir seçenektir. Ve tüm bu akışı kendi altyapınızda kurmak istiyorsanız, nesne depolama başlangıç noktasıdır.

Pratikte karşılaşacağınız birkaç sayı ve ayar

  • Normal bir PUT isteğinde bir nesnenin maksimum boyutu genellikle 5 gigabayttır. Daha büyüğü için Multipart gereklidir.
  • Her Multipart parçası (son parça hariç) en az 5 megabayttır. Daha küçük gönderirseniz, EntityTooSmall hatası alırsınız.
  • Maksimum parça sayısı 10000'dir. 5 megabaytlık parçalarla pratik üst sınır yaklaşık 50 gigabayt olur; daha büyük dosya için parça boyutunu artırın.
  • İmza için izin verilen saat farkı 15 dakikadır.
  • aws s3 cp varsayılan olarak 8 megabayt üzeri dosyalar için kendisi Multipart yapar.

İnce bir tuzak: Multipart'ı yarıda bırakırsanız, tamamlanmamış parçalar bucket'ta kalır ve nesne listesinde görünmeden depolama maliyeti oluşturur. aws s3api list-multipart-uploads --bucket mybucket ile kontrol edin ve bunları otomatik temizlemek için bir lifecycle rule koyun.

Kubernetes üzerinde çalışıyorsanız ve depolamayı volume olarak bağlamak istiyorsanız, yönetilen Kubernetes bunu kolaylaştırır. İsteklerin hata oranını ve latency'sini izlemek için bulut izleme ve uyarı kılavuzuna bakın; 403 oranı üzerine bir uyarı, kullanıcı şikayetinden çok daha önce policy sorununu ortaya çıkarır.

Sık sorulan sorular

Doğru anahtara rağmen neden SignatureDoesNotMatch hatası alıyorum?

Vakaların yüzde doksanında, sistem saatiniz depolama hizmetiyle farklıdır. V4 imzası zamana duyarlıdır ve 15 dakikadan fazla fark reddedilmeye neden olur. timedatectl status ile saati kontrol edin ve NTP'yi etkinleştirin.

Saat doğruysa, sonraki olasılık bir ters proxy'nin Host veya x-amz-* başlığını değiştirmesidir. İmza tam olarak bu başlıklar üzerinden hesaplanır.

Presigned URL ile genel bağlantı arasındaki fark nedir?

Genel bağlantı herkes için ve her zaman çalışır. İmzalı URL yalnızca belirlediğiniz süre boyunca geçerlidir ve sonrasında Request has expired döndürür. Hassas dosyalar için her zaman presigned kullanın.

Signature V4'te maksimum geçerlilik süresi yedi gündür. Bundan fazlası için CDN imza mekanizmasını kullanmalısınız.

Bucket'ta ACL'yi etkin tutmalı mıyım?

Hayır, yalnızca ACL gönderen eski bir istemciniz yoksa. Bucket Policy daha okunabilir, sürümlenebilir ve denetlenebilirdir. ACL her nesne düzeyinde ayrı ayrı uygulanır ve ölçekte yönetimi neredeyse imkânsız hale gelir.

Neden tamamlanmamış dosyalar bucket'ımda yer kaplıyor?

Çünkü yarıda bırakılmış bir Multipart Upload var. Bu parçalar nesne listesinde görünmez ama depolanır. list-multipart-uploads ile onları bulun ve birkaç gün sonra otomatik silmek için bir lifecycle rule koyun.

Sonraki adım: her hizmet için ayrı bir anahtar oluşturun, ACL'yi kapatın ve izleme sisteminizde 403 hata oranı üzerine bir uyarı ayarlayın. Bu üç iş, burada yazdığım sorunların çoğunu ortadan kaldırır.

Bu sayfa yardımcı oldu mu?