AWS Hardening - I. Bölüm - S3 Güvenliği

September 02, 2021

Merhaba,

Bu yazı dizisinde AWS’in sık kullanılan servislerine siber güvenlik perspektifinden yaklaşarak, olası saldırı senaryolarını yaşanmadan önce nasıl engelleyebileceğinize dair ipuçları vermeye çalışacağım.

İlk olarak, ödül avcılarının bug bounty programlarında sıklıkla tercih ettiği ve de geçmişte bu konuyla ilgili binlerce kaynak hacklendiği için, birinci bölümde S3 servisi ile başlamak istedim. Ben uygulamalarda örnek olarak oluşturduğum awsharden1ng isimli bucket üzerinden ilerleyeceğim.

AWS S3

Öncelikle S3’ün ne olduğunu kısaca açıklayalım. S3, bulut tabanlı bir depolama servisidir. Upload ettiğiniz bir dosya veya dizini, özelleştirilmiş bir URL üzerinden verilen izinlere göre istediğiniz şekilde başkalarıyla paylaşmayı sağlar. Bunun Microsoft Azure tarafındaki karşılığı Azure Blob, Google Cloud Platform bünyesindeki karşılığı ise Google Cloud Storage olarak adlandırılır.

S3, iki ana bileşenden oluşur:

  • Bucket
  • Object

Bucket, verinin depolandığı alandır. Her bir bucket eşsiz (unique) bir isimle tanımlanır. URL yapısı:

https://bucket_adı.s3.amazonaws.com

şeklindedir. Şayet google isimli bir bucket’ı daha önce herhangi bir AWS kullanıcısı oluşturduysa, aynı ada sahip ikinci bir bucket oluşturulamaz.

Object, bucket üzerinde var olan verilerin kendisidir. Bir başka deyişle; bucket’a upload edilen nesneler S3 terminolojisinde object olarak adlandırılır. Şu ana kadar açıklanan bileşenleri tek noktada göstermek gerekirse:

URL: https://my_bucket.s3.amazonaws.com/script.sh Bucket adı: my_bucket Object adı: script.sh

Peki neden S3 kullanıyoruz? Madem amaç veri depolamaksa, S3 yerine EBS, **EFS, EC2 disklerini de kullanabiliriz öyle ya? Aslında bunun birden çok sebebi var, kullanım amacınıza göre doğru storage çözümünü kullanmak oldukça mühim. Hardening başlığından çok sapmamak adına burada detaylı bir bilgi aktarmayacağım ama kullanım senaryosuna göre S3’ü backup, restore ve arşivleme gibi konularda rahatlıkla kullanabiliyoruz. Elbette bunun haricinde bir de işin maddi boyutu var, cost management açısından baktığımızda EBS ve EFS gibi storage çözümlerinin daha pahalı olduğunu da görüyoruz.

Bu temel bilgilerden sonra tekrar hardening tarafına dönebiliriz. S3 ile alakalı kontrol edilmesi ve duruma göre düzeltilmesi önerilen birkaç yapılandırmaya değinerek, sırayla “Nasıl kontrol edebilirsiniz?” ve “Nasıl düzeltebilirsiniz?” sorularına yanıt verecek şekilde ilerleyeceğim.

Açık erişimin engellenmesi (Block Public Access)

Bu alt başlık için belki de en basit ama en çok can yakan yanlış yapılandırmaların temelinde yatan problem desek yanlış olmayacaktır.

Mevcut durumda yeni bir bucket oluşturduğunuzda artık varsayılan olarak tüm anonim erişimler engellenmektedir.

1a

Geçerli bir nedeniniz yoksa veya başkalarının erişmesinde bir problem oluşturmayacak verilerden bahsetmiyorsak, söz konusu bucket’ın açık erişiminin olması iyi bir pratik olmayacaktır.

Bucket oluşturulduktan sonra bu durum bucket sayfasında Permissions sekmesinden kontrol edilebilir. Burada tümden bir bloklama yerine ACL veya obje seviyesinde policy oluşturularak, engelleme işlemi de ayrıca özelleştirilebilir ama bu yazıda bunun detayına girmeyeceğim.

Komut satırından kontrol etmek için:

aws s3api get-public-access-block --bucket awsharden1ng

komutunu girdikten sonra resimdeki şekilde bir çıktı almamız gerekir.

5a

Buradaki değerler false durumdaysa izinlerin yeniden elden geçirilmesi gerekebilir.

Aynı konsol ekranından Block all public access seçeneği ile tüm erişim kapatılabileceği gibi, yine komut satırından:

aws s3api put-public-access-block --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true --bucket awsharden1ng

komutuyla da bu işlemi sağlayabilirsiniz.

Erişim yetkileri

Yetkisiz erişim sağlanması durumlarına karşı önerilen bir diğer konu da tanımlanmış kullanıcıların grup bazında tam yetkiye sahip olmaması gerektiğidir.

Şayet ACL kontrolünde READ, WRITE, READ_ACP and WRITE_ACP yetkileri görünüyorsa, ACL’in private olarak güncellenmesi gerekebilir.

Bunun sağlaması komut satırından:

aws s3api get-bucket-acl --bucket bucket_adı

komutu ile yapılır. Spesifik bir kullanıcı için aşağıdaki gibi bir çıktı alınıyorsa ACL kısıtlamasına gidilmelidir.

{
  "Owner": {
    "DisplayName": "deniz",
    "ID": "xxx"
  },
  "Grants": [
    {
      "Grantee": {
        "Type": "Group",
        "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
      },
      "Permission": "READ"
    },
    {
      "Grantee": {
        "Type": "Group",
        "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
      },
      "Permission": "WRITE"
    },
    {
      "Grantee": {
        "Type": "Group",
        "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
      },
      "Permission": "READ_ACP"
    },
    {
      "Grantee": {
        "Type": "Group",
        "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
      },
      "Permission": "WRITE_ACP"
    }
  ]
}

ACL’i özel moda almak için:

aws s3api put-bucket-acl --bucket bucket_adı --acl private

komutu girilir.

Konsolda https://s3.console.aws.amazon.com/s3/buckets adresinden Permissions sekmesine gidip, ACL bölümünde Bucket ve Object özelinde yetkilerin durumu kontrol edilebilir. Bucket sahibi haricinde özellikle Everyone satırında yetkilendirme görünüyorsa müdahale edilmesi gerekebilir.

6a

Zira bu haklara sahip olan bir kullanıcı, S3 üzerindeki eylemlerde tam yetkiye de sahip olacaktır.

Loglamanın aktif edilmesi

Varsayılan olarak bir bucket oluşturulduğunda logging aktif edilmemektedir. İleriye yönelik auditing işlemlerinde tüm erişim bilgileri kontrol edebilmek için loglamanın kullanılması önerilir.

aws s3api get-bucket-logging --bucket bucket_adı

komutu girildiğinde bir çıktı almadıysanız, bu loglamanın hedef bucket için aktif olmadığı anlamına gelmektedir.

İlk olarak log aktarımı için gerekli ACL yetkilerini ekliyoruz.

aws s3api put-bucket-acl --bucket awsharden1ng --grant-write URI=http://acs.amazonaws.com/groups/s3/LogDelivery --grant-read-acp URI=http://acs.amazonaws.com/groups/s3/LogDelivery

Şimdi de bucket’a atamak üzere yeni bir erişim kuralı oluşturuyoruz. Kuralı örnek olarak logging.json şeklinde kaydettim.

{
  "LoggingEnabled": {
    "TargetBucket": "awsharden1ng",
    "TargetPrefix": "logs/",
    "TargetGrants": [
      {
        "Grantee": {
          "Type": "Group",
          "URI": "http://acs.amazonaws.com/groups/s3/LogDelivery"
        },
        "Permission": "WRITE"
      },
      {
        "Grantee": {
          "Type": "Group",
          "URI": "http://acs.amazonaws.com/groups/s3/LogDelivery"
        },
        "Permission": "READ_ACP"
      }
    ]
  }
}

Son olarak, oluşturduğumuz kuralı hedef bucket için etkinleştiriyoruz.

aws s3api put-bucket-logging --bucket awsharden1ng --bucket-logging-status file://logging.json

Şifreleme (Encryption)

Bucket düzeyinde tüm objelerin şifrelenmiş olarak depolanması önerilir. Bu sayede upload aşamasında SSE, SSE-S3, SSE-KMS gibi şifreleme işlemleri kullanılabilir ve veri daha güvenli bir şekilde saklanır.

Varsayılan olarak bucket düzeyinde şifreleme aktif değildir.

aws s3api get-bucket-encryption --bucket awsharden1ng

komutuyla kontrol edildiğinde görüntüdeki gibi bir çıktı alınır.

8a

Şifreleme, bucket oluşturulduktan sonra da aktif edilebilir, yani oluşturma aşamasına özgür bir yöntem değildir.

Şimdi AES256 metodunu kullanarak bu bucket için encryption uygulayalım.

aws s3api put-bucket-encryption --bucket awsharden1ng --server-side-encryption-configuration '{ "Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]}'

Bu komut herhangi bir çıktı vermeyecektir. Yine de bir önceki komutla sağlamasını yapabiliriz.

9a

Bu işlem KMS kullanılarak da yapılabilir, onu da inceleyelim.

KMS ile şifreleme

Bir diğer encryption metodu da CMKs‘tir. Ek bir güvenlik katmanı yaratabilmek adına SSE-S3 yerine bu yöntemin kullanılması tavsiye edilir.

SSE-S3’ten farklı olarak json kuralını bir başka biçimde oluşturacağız. Öncelikle bir KMS‘e sahip olmanız gerekiyor. Konsoldan KMS servisine geldikten sonra yeni bir anahtar oluşturun.

Burada farklı opsiyonlar olsa da bunların tümüne değinmeyeceğim. İkinci ekranda bir alias oluşturmanız gerekecek ve sonraki ekranlarda da anahtar atamasının yapılacağı kullanıcıyı belirtmeniz gerekiyor.

10a

Anahtar oluşturulduktan sonra KMS ID‘si için ARN bilgisine ihtiyacımız olacak. Bu bilgiyle KMSMasterKeyID parametresini yazarak kural dosyasını oluşturacağız.

{
  "Rules": [
    {
      "ApplyServerSideEncryptionByDefault": {
        "KMSMasterKeyID": "arn:aws:kms:ap-south-1:xx:key/50db893b-5d7d-4b97-a2b0-xx",
        "SSEAlgorithm": "aws:kms"
      }
    }
  ]
}

Kuralı

aws s3api put-bucket-encryption --bucket awsharden1ng --server-side-encryption-configuration file://kms.json

komutuyla bucket’a atayalım.

Bu komut da aynı şekilde bir çıktı vermeyecektir. get-bucket-encryption ile sağlamasını yapalım.

11a

Lifecycle yapılandırması

Lifecycle, bir veya birden çok kuralın bir araya gelerek bucket düzeyinde farklı eylemler oluşturulmasını sağlar.

aws s3api get-bucket-lifecycle-configuration --bucket bucket_adı

komutuyla kontrol edilebilir, varsayılan olarak aktif değildir.

İsteğe göre birden çok kuralı ve eylemi içeren kompleks lifecycle yapılandırmaları gerçekleştirilebilir. Mesela aşağıdaki örneği inceleyelim.

aws s3api put-bucket-lifecycle-configuration --bucket awsharden1ng --lifecycle-configuration
'{
"Rules": [
{
"Filter": {
"Prefix": "logs/"
},
"Status": "Enabled",
"Transitions": [
{
"Days": 30,
"StorageClass": "GLACIER"
}
],
"Expiration": {
"Days": 180
},
"ID": "Test"
}
]
}'

Hedef S3’te /logs dizini altında bulunan veriler, oluşturulduktan 30 gün sonra GLACIER storage class’ına taşınır ve 180 gün sonra otomatik olarak silinir.

Nesne kilidi (Object Lock)

MFA gereksiniminde olabileceği gibi, yetkisiz erişim sağlanması durumunda saldırganı en azından bir noktaya kadar engelleyebilecek bir diğer önlem de nesnelerin kilitlenmesidir. Kullanıcının tanımladığı koruma süresi (retention period) geçmeden hedef nesneler hiçbir şekilde silinemez.

Burada iki farklı kullanım metodu vardır:

  • Retention period: Kullanıcı süreyi belirtir, bu süreden önce hedef verilerin üzerine yazma işlemi yapılamaz ve veriler root yetkisi olsa dahi silinemez.
  • Legal hold: Bir önceki kullanımdan farkı, burada bir süre belirtilmemesidir. Yani kullanıcı bu opsiyonu kaldırmadığı sürece veriler silinemez.

Kilitleme opsiyonu varsayılan olarak pasif durumdadır. Aşağıdaki komutla kontrol edilebilir.

aws s3api get-object-lock-configuration --bucket awsharden1ng --query 'ObjectLockConfiguration.ObjectLockEnabled'

Not: Kilitleme özelliği, bucket oluşturulduktan sonra aktif edilemez. Sadece bucket oluşturma aşamasında aktif edilebilir. Konsoldan yapılabileceği gibi komut satırından da halledilebilir.

Konsol ekranındayken Advanced Settings sekmesinden aktif edilebilir.

12a

Komut satırından ise:

aws s3api create-bucket --bucket bucket_adı --region ap-south-1 --object-lock-enabled-for-bucket --create-bucket-configuration LocationConstraint=ap-south-1

şeklinde kilitleme opsiyonu ile yeni bir bucket oluşturulabilir.

Aktarım hızlandırması (Transfer Acceleration)

Bu konu doğrudan bir güvenlik katmanı sağlamasa da, veri aktarımlarının %500‘e kadar daha hızlı yapılabilmesi adına tavsiye edilebilir. Bu opsiyon ile AWS’in CloudFront için dağıtılmış Edge ağı kullanılır. Elbette bu özellik ekstra bir masraf oluşturacaktır, kullanmadan önce belirli bir veri boyutunun faturalandırmasını incelemeniz gereklidir.

aws s3api put-bucket-accelerate-configuration --bucket awsharden1ng --accelerate-configuration Status=Enabled

komutuyla aktif hale getirildikten sonra

aws s3api get-bucket-accelerate-configuration --bucket awsharden1ng --query 'Status'

komutuyla Enabled çıktısının alınması gerekir. Aktif durumda olmadığında null veya Suspended olarak görülecektir.

SSL ile güvenli aktarım

Veri transferi işlemlerinde bir diğer önlem de SSL kullanılmasıdır. Bunun için yine bir json kuralı oluşturulur.

{
  "Version": "2012-10-17",
  "Id": "S3Secure",
  "Statement": [
    {
      "Sid": "ForceSSLOnlyAccess",
      "Effect": "Deny",
      "Principal": { "AWS": "*" },
      "Action": "s3:*",
      "Condition": {
        "Bool": { "aws:SecureTransport": false }
      },
      "Resource":"arn:aws:s3:::awsharden1ng/*"
    }
  ]
}

Bu sayede veri transferi SSL protokol kullanımına zorlanacaktır. Kuralı aktif edelim:

aws s3api put-bucket-policy --bucket awsharden1ng --policy file://ssl.json

get-bucket-policy ile kontrol edildiğinde görüntüdeki gibi bir çıktı alınmalıdır:

aws s3api get-bucket-policy --bucket awsharden1ng

13a

Server-Side Encryption uygulanması

Hassas verilerin gizliliğinin önemindeki konulardan biri de SSE‘dir.

SSE-S3 ve CMKs ile birer örnek göstermiştim. Burada da kural dosyaları oluşturularak aynı şekilde bucket’a atanır, sadece içerik farklıdır.

{
  "Version": "2012-10-17",
  "Id": "PutSSEObjPolicy",
  "Statement": [
    {
      "Sid": "DenyIncorrectEncryptionHeader",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::cc-client-data/*",
      "Condition": {
        "StringNotEquals": {
          "s3:x-amz-server-side-encryption": "AES256"
        }
      }
    },
    {
      "Sid": "DenyUnEncryptedObjectUploads",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::cc-client-data/*",
      "Condition": {
        "Null": {
          "s3:x-amz-server-side-encryption": "true"
        }
      }
    }
  ]
}

Mesela buradaki iki kuralla şifreleme uygulanmamış verilerin hedef bucket’a upload edilmesi engellenebilir. Dosya oluşturulduktan sonra:

aws s3api put-bucket-policy --bucket bucket_adı --policy file://kural_dosyası.json

komutuyla SSE aktif edilir.

Multi-Factor Authentication

İlk olarak MFA ile başlayalım. Neden MFA öneriliyor? Çünkü saldırganın bir şekilde API veya konsol üzerinde full erişim sağlaması durumunda tüm S3 verisi tehlikeye girebilir, böyle bir senaryoda ek bir güvenlik katmanı olarak MFA kullanıldığında saldırgan verileri yok etmeye çalışsa bile belirlemiş olduğunuz numaraya onay kodu geleceği için, en azından veri kaybı önlenmiş olacaktır.

İlk olarak MFA’in aktif olup olmadığını kontrol edelim.

1-) https://s3.console.aws.amazon.com/s3 adresine gidin. 2-) Kontrol etmek istediğiniz bucket’a tıklayın. 3-) Properties sekmesine gelin.

Bucket Versioning bölümünde Multi-factor authentication (MFA) delete opsiyonu Enabled olarak görünmelidir.

2a

Resimde görüldüğü gibi bu örnekte şu an devre dışı durumda, aktif hale getirelim.

S3 için MFA, konsol üzerinden aktifleştirilememektedir, bunun için AWS CLI veya API üzerinden ilerlemek zorundayız. Ayrıca ‘Versioning’ özelliğinin de aktif olması gerekmektedir, bu işlemi hem konsol hem de CLI üzerinden yapabilirsiniz.

İlk olarak S3 sahibi olan kullanıcı hesabının bir MFA’e sahip olması gerekmektedir.

https://console.aws.amazon.com/iam/home#/users adresinden Security credentials sekmesine gelin. Assigned MFA device satırından Managee tıklayın.

4a

Bu noktada MFA tipini seçmeniz istenecek. İkinci ve üçüncü opsiyon donanımsal MFA çözümleri, şayet U2F veya Gemalto token destekleyen bir cihazınız varsa bu opsiyonlarla da ilerleyebilirsiniz. Nitekim bir donanıma sahip olmak zorunda de değilsiniz. İlk seçenek mobil cihazınızda kullanabileceğiniz Authenticator uygulaması üzerinden bu işlemi yapmanıza olanak sağlamaktadır.

QR kodunu uygulamanızla okuttuktan sonra peşpeşe gelecek 6 haneli kodu sırasıyla kutucuklara girdikten sonra işlemi tamamlayabilirsiniz.

Önce versiyonlama ve MFA özelliklerinin durumunu CLI ile kontrol edelim.

aws s3api get-bucket-versioning --bucket awsharden1ng

Sadece resimdeki gibi bir çıktı alınması durumunda, bu versiyonlamanın aktif, fakat MFA’nın pasif olması anlamına gelir.

3a

aws s3api put-bucket-versioning --bucket awsharden1ng --versioning-configuration '{"MFADelete":"Enabled","Status":"Enabled"}' --mfa 'arn:aws:iam::xxx:mfa/deniz token'

komutuyla MFA etkinleştirilebilir.

Not: —mfa bölümünde ARN olarak az önce oluşturduğumuz MFA bilgisinin girildiğine dikkat edin.

İkinci bölümde görüşmek üzere.



Written by Deniz Parlak