İçeriğe geç
academia.sh

Ders 09 / 14

Ağ Güvenliği Yapılandırması

Ağ güvenliği yapılandırması düz metin trafiğine, güvenilen sertifika kaynaklarına ve alan adına özel kurallara karar verir. Ders düz metin diye engellenen bir isteğin sunucuya kaç kopya olarak ulaştığını ve sabitlenen sertifikayla eşleşmeyen bağlantının sonucunu ölçer.

Yayın Güncelleme

Bu derste

  • Düz metin trafiği, güven çapası ve sertifika sabitlemenin yapılandırmadaki yerini ayırır
  • Alan adına özel kuralın tek bir adresi nasıl serbest bıraktığını gösterir
  • Düz metin diye engellenen isteğin sunucuya ulaşmadığını kayıp olarak ölçer
  • Aynı istek HTTPS adresine yeniden gönderilince ilk isteğin ezilen olduğunu, sabitleme uyuşmazlığının da kayıp olduğunu ayırır
İçindekiler

Önceki derste sır diskte duruyordu ve onu okunamaz yapan şey bir anahtardı. Bu derste olay aygıtın dışına çıkar: uygulama sunucuya bir istek gönderir. İstek, ağa çıkmadan önce platformun yapılandırmasından geçer ve o yapılandırma isteği engelleyebilir. Soru kursun sorusudur: gönderilen bu olay alıcıya kaç kopya olarak ulaştı? Olay istektir, alıcı sunucudur.

// Arrival.kt — gonderilen bir olayin aliciya kac kopya olarak ulastigini siniflandirir
class Signal(val key: String, val seq: Int)

class Arrival(val label: String, val sent: Signal, received: List<Signal>) {
    val copies: Int = received.count { it.seq == sent.seq }
    val verdict: String = when {
        copies > 1 -> "yinelenen"
        copies == 1 -> "birebir"
        received.any { it.key == sent.key && it.seq > sent.seq } -> "ezilen"
        else -> "kayip"
    }

    fun line() {
        println("$label: anahtar=${sent.key} | kopya=$copies -> $verdict")
    }
}

fun countArrivals(arrivals: List<Arrival>) {
    for (name in listOf("birebir", "yinelenen", "ezilen", "kayip")) {
        println("$name: ${arrivals.count { it.verdict == name }}")
    }
}

Kural sunucuya ulaşan kopyaları sayar. İsteğin neden ulaşmadığını, hangi kuralın engellediğini ve engellemenin doğru olup olmadığını bilmez.

Yapılandırmanın Üç Kararı

Ağ güvenliği yapılandırması bir XML dosyasıdır ve uygulamanın bildirim dosyasından adıyla çağrılır. Üç şeye karar verir. Birincisi düz metin trafiğidir: http:// ile başlayan bir adrese istek gidebilir mi. İkincisi güven çapasıdır: sunucunun sertifika zincirini hangi kök sertifikalara karşı doğrulayacağı. Üçüncüsü sertifika sabitlemedir (certificate pinning): belirli bir alan adı için yalnız önceden bildirilen sertifika özetleriyle eşleşen bağlantı kabul edilir.

<!-- Platform kodu: network-security-config -->
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="false">yerel.test</domain>
    </domain-config>
    <domain-config>
        <domain includeSubdomains="true">api.ornek.test</domain>
        <pin-set>
            <pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

Bu parça platform yapılandırmasıdır; çalıştırılmaz ve burada çıktı iddia etmiyoruz. Özet değeri örnek bir yer tutucudur. base-config bütün trafiğin varsayılanını yazar: düz metin kapalı, güven çapası sistemin sertifikaları. İlk domain-config tek bir adresi, yerel.test’i, düz metne açar. İkincisi api.ornek.test ve alt alan adları için sertifika özetini sabitler.

Model, platformun bu kuralını kurar; model platformun kendisi değildir. Modelde bir politika, düz metne açık adreslerin kümesini ve isteğe bağlı bir sabit sertifika özetini tutar. İstek HTTPS değilse yalnız açık adresler geçer. HTTPS ise ve politika bir özet sabitlemişse, istekteki sertifika onunla eşleşmelidir.

// Policy.kt — duz metin ve sabitleme kurallarini isteklere uygular
class Request(val signal: Signal, val url: String, val cert: String = "ortak")

class NetworkPolicy(
    private val cleartextHosts: Set<String> = emptySet(),
    private val pin: String? = null,
) {
    private fun host(url: String) = url.substringAfter("://").substringBefore("/")

    fun passes(request: Request): Boolean {
        if (!request.url.startsWith("https://")) return host(request.url) in cleartextHosts
        return pin == null || request.cert == pin
    }
}

fun rejectCleartext(policy: NetworkPolicy, requests: List<Request>): List<Signal> =
    requests.filter { policy.passes(it) }.map { it.signal }

Sunucuya ulaşan liste, rejectCleartext işlevinin dönüşüdür. Ölçümde kopya sayısını bu liste verir.

// Probe.kt — gonderilen istekler icin sunucuya ulasan listeyi alici sayar
fun probe(policy: NetworkPolicy, requests: List<Request>, sent: List<Signal>): List<Arrival> {
    val received = rejectCleartext(policy, requests)
    return sent.mapIndexed { i, s -> Arrival("olay-${i + 1}", s, received) }
}

İsteğin İki Kapısı

passes işlevi isteği iki kapıdan geçirir ve kapılar birbirinden bağımsızdır. İlk kapı adresin biçimine bakar. İstek https:// ile başlamıyorsa karar tamamen düz metin listesine bağlıdır: adres listedeyse istek geçer, değilse düşer. Bu dalda sertifikaya hiç bakılmaz, çünkü düz metin bir istekte sertifika yoktur.

İkinci kapı yalnız HTTPS istekleri için çalışır. Politika bir özet sabitlememişse istek geçer; sabitlemişse isteğin sertifikası o özetle eşleşmek zorundadır. Burada düz metin listesi bir anlam taşımaz: listedeki bir adrese HTTPS ile gidilirse, yine de sabitleme kontrol edilir.

Bu ayrımın iki sonucu vardır. Birincisi, sabitleme düz metin isteği korumaz; açık adrese giden http:// isteği hiçbir özetle karşılaştırılmaz. İkincisi, düz metin engelinin kaldırılması sabitlemeyi de kaldırmaz; iki karar ayrı ayrı yazılır ve ayrı ayrı sonuç verir. Ölçümlerde kayıp bazen birinci kapıdan, bazen ikinci kapıdan gelir ve kural ikisini ayırmaz: yalnız sunucuya hiçbir kopyanın ulaşmadığını görür.

Platformda kuralların uygulanma sırası ve alan adı eşleşmesinin ayrıntıları yapılandırma dosyasının yazımına bağlıdır; bu derste ölçülen yalnız modelin iki kapısıdır.

Hepsi HTTPS İken

Üç istek de HTTPS adresine gider. Politikada açık adres ve sabit özet yoktur.

// Main.kt — https istekleri engele takilmaz
fun main() {
    val sent = listOf(Signal("siparis.41", 1), Signal("siparis.42", 2), Signal("stok.7", 3))
    val requests = listOf(
        Request(sent[0], "https://api.ornek.test/siparis"),
        Request(sent[1], "https://api.ornek.test/siparis"),
        Request(sent[2], "https://api.ornek.test/stok"),
    )
    val arrivals = probe(NetworkPolicy(), requests, sent)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
olay-1: anahtar=siparis.41 | kopya=1 -> birebir
olay-2: anahtar=siparis.42 | kopya=1 -> birebir
olay-3: anahtar=stok.7 | kopya=1 -> birebir
birebir: 3
yinelenen: 0
ezilen: 0
kayip: 0

Üç isteğin üçü sunucuya birer kopya olarak ulaştı. Düz metin kapalı olduğunda HTTPS trafiği bu engele takılmaz.

Düz Metin Diye Engellenen İstek

Dört istekten ikisi http:// ile başlar. Bunlardan biri, açık adres kümesindeki yerel.test’e gider; öteki gitmez.

// Main.kt — duz metin istegi acik adres disinda engellenir
fun main() {
    val sent = listOf(
        Signal("siparis.41", 1),
        Signal("siparis.42", 2),
        Signal("stok.7", 3),
        Signal("kur.3", 4),
    )
    val requests = listOf(
        Request(sent[0], "http://api.ornek.test/siparis"),
        Request(sent[1], "https://api.ornek.test/siparis"),
        Request(sent[2], "http://yerel.test/stok"),
        Request(sent[3], "http://kur.ornek.test/kur"),
    )
    val arrivals = probe(NetworkPolicy(setOf("yerel.test")), requests, sent)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
olay-1: anahtar=siparis.41 | kopya=0 -> kayip
olay-2: anahtar=siparis.42 | kopya=1 -> birebir
olay-3: anahtar=stok.7 | kopya=1 -> birebir
olay-4: anahtar=kur.3 | kopya=0 -> kayip
birebir: 2
yinelenen: 0
ezilen: 0
kayip: 2

Engellenen iki istek sunucuya hiç ulaşmadı. Bu dersin teslimi: kayıp. İstek yazıldı, gönderildi, ama ağa çıkmadan önce platformun yapılandırması onu durdurdu. yerel.test adresi alan adına özel kuralla açıldığı için o istek birebir ulaştı. Kural http:// ile https:// arasındaki farkı değil, yalnız sonucu görür.

Yeniden Deneme Kopya Bırakırsa

Bir ağ istemcisi zaman aşımına uğrayan isteği yeniden gönderebilir. İstek iki kez HTTPS ile gider ve ikisi de yapılandırmadan geçer.

// Main.kt — yeniden denenen istek iki kez gecerse sunucu iki kopya alir
fun main() {
    val sent = listOf(Signal("siparis.41", 1), Signal("siparis.42", 2), Signal("stok.7", 3))
    val requests = listOf(
        Request(sent[0], "https://api.ornek.test/siparis"),
        Request(sent[1], "https://api.ornek.test/siparis"),
        Request(sent[1], "https://api.ornek.test/siparis"),
        Request(sent[2], "http://api.ornek.test/stok"),
    )
    val arrivals = probe(NetworkPolicy(), requests, sent)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
olay-1: anahtar=siparis.41 | kopya=1 -> birebir
olay-2: anahtar=siparis.42 | kopya=2 -> yinelenen
olay-3: anahtar=stok.7 | kopya=0 -> kayip
birebir: 1
yinelenen: 1
ezilen: 0
kayip: 1

Yapılandırma yinelenen isteği ayıklamaz: ikisi de aynı kuralları sağladığı için ikisi de geçer. Yinelenmeyi önlemek istemcinin ya da sunucunun işidir. Üçüncü olayın kayıp olması ayrı bir şeydir: o istek düz metin olduğu için hiç geçmedi ve yeniden denenmedi. Aynı ölçümde bir olay yinelenen, bir olay kayıp çıkabilir.

Açık Adresin Kapsamı

Düz metne açılan adres, adın yazıldığı kadarını kapsar. XML örneğinde yerel.test için includeSubdomains="false" yazılıdır; yani yalnız bu ad serbesttir, altındaki adlar değildir. Modelde karşılığı cleartextHosts kümesinin tam eşleşmesidir: küme yerel.test içeriyorsa tablo.yerel.test içermez.

Üç istek vardır. İlki açık adresin kendisine düz metinle gider. İkincisi aynı adın bir alt adına düz metinle gider. Üçüncüsü o alt adrese HTTPS ile gider.

// Main.kt — acik adres alt adlari kapsamaz, https ise yine gecer
fun main() {
    val sent = listOf(Signal("tablo.1", 1), Signal("tablo.2", 2), Signal("tablo.3", 3))
    val requests = listOf(
        Request(sent[0], "http://yerel.test/tablo"),
        Request(sent[1], "http://tablo.yerel.test/tablo"),
        Request(sent[2], "https://tablo.yerel.test/tablo"),
    )
    val arrivals = probe(NetworkPolicy(setOf("yerel.test")), requests, sent)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
olay-1: anahtar=tablo.1 | kopya=1 -> birebir
olay-2: anahtar=tablo.2 | kopya=0 -> kayip
olay-3: anahtar=tablo.3 | kopya=1 -> birebir
birebir: 2
yinelenen: 0
ezilen: 0
kayip: 1

Açık adresin kendisi geçti, alt adı geçmedi, HTTPS ise adres ne olursa olsun düz metin kuralına takılmadı. Geliştirme ortamında bir adresi açıp alt adlarının da açıldığını varsaymak bu yüzden sık görülen bir yanlıştır. Ölçüm bunu üç satırda gösterir: kayıp yalnız ikinci istektedir.

GD1. Model, yeniden denemeyi aynı isteğin listede iki kez bulunması olarak kurar. Platformda yeniden deneme istemcinin ara katmanında olur ve yapılandırma onu ayrıca görmez.

GD2. Model, sabitlemeyi tek bir özetle ve bütün HTTPS adresleri için kurar. Platformda sabitleme alan adı başına ve birden çok özetle yazılır.

Sınırlayıcı Ölçüm

Politika bir özet sabitlemiştir. Dört gönderim vardır. Birinci istek düz metin diye engellenir. Uygulama aynı isteği, aynı anahtarla, HTTPS adresine yeniden gönderir. Üçüncü istek HTTPS’tir ama sunucunun sertifikası sabitlenen özetle eşleşmez.

// Main.kt — yeniden gonderilen istek ilkini ezer, sabitleme uyusmazligi kayiptir
fun main() {
    val sent = listOf(
        Signal("siparis.41", 1),
        Signal("siparis.42", 2),
        Signal("stok.7", 3),
        Signal("siparis.41", 4),
    )
    val requests = listOf(
        Request(sent[0], "http://api.ornek.test/siparis"),
        Request(sent[1], "https://api.ornek.test/siparis", "sabit-a"),
        Request(sent[2], "https://api.ornek.test/stok", "baska"),
        Request(sent[3], "https://api.ornek.test/siparis", "sabit-a"),
    )
    val arrivals = probe(NetworkPolicy(pin = "sabit-a"), requests, sent)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
olay-1: anahtar=siparis.41 | kopya=0 -> ezilen
olay-2: anahtar=siparis.42 | kopya=1 -> birebir
olay-3: anahtar=stok.7 | kopya=0 -> kayip
olay-4: anahtar=siparis.41 | kopya=1 -> birebir
birebir: 2
yinelenen: 0
ezilen: 1
kayip: 1

İlk istek ağa çıkmadı; ama aynı anahtarla daha büyük bir seq, yani yeniden gönderilen istek sunucuya ulaştı. Kural bu yüzden ilk isteği kayıp değil ezilen sayar. stok.7 ise sabitlenen sertifikayla eşleşmediği için kayıptır ve aynı anahtarla ulaşan başka bir istek olmadığından ezilen de değildir. Birinci isteğin nedeni düz metin, üçüncünün nedeni sabitleme uyuşmazlığıdır. Kural bu iki nedeni ayırmaz: ikisi de kopya yok demektir. Neden yalnız yapılandırmaya ve günlüğe bakınca öğrenilir.

GD3. Model, sertifika uyuşmazlığını istekteki bir etiketle kurar. Platformda uyuşmazlık, el sıkışma sırasında zincirin doğrulanamaması olarak görünür ve istek hiç gönderilmeden bağlantı hatası verir.

İstemcinin Yapacakları

Engel yapılandırma düzeyinde olduğu için sorumluluk iki yere dağılır. İsteği gönderen kod, istek ağa çıkmadan düşmüş olabileceğini bilmelidir. Yapılandırmayı yazan kişi ise hangi adreslerin açık olduğunu belgelemelidir.

  • Engeli ağ hatasından ayırmak. Düz metin engeli ile bağlantının kopması istemciye farklı hatalar olarak görünür. Kod ikisini aynı yeniden deneme döngüsüne sokarsa engellenen bir isteği boşuna tekrarlar; model bunu gösterir, çünkü engellenen istek kaç kez denenirse denensin kopya sıfırdır.
  • Yeniden denemeyi tekilleştirmek. Yapılandırma kopyaları ayıklamadığı için isteğe bir tekrar kimliği eklemek istemcinin işidir. Sunucu aynı kimliği iki kez görürse ikincisini yok sayar. Bu durumda teslim yine yinelenendir, ama sonuç yinelenmez: kimlik, teslimin kaç kez olduğunu değil, sonucun kaç kez işlendiğini sınırlar.
  • Sabitlemeyi yenilemeye hazırlamak. Model tek bir özet kurar. Sunucunun sertifikası değişirse, özet güncellenene kadar bütün HTTPS istekleri kayıp olur. Gerçek yapılandırmada birden çok özet yazılabilir, ama hangi özetlerin yazılacağı ve ne zaman değişeceği bu dersin ölçtüğü şey değildir.
  • Hata mesajını kullanıcıya göstermemek. Yapılandırma nedeniyle düşen bir istek için kullanıcıya gösterilecek metin ağın kapalı olduğunu söylememelidir. Ağ açık, istek yapılandırmadan dönmüştür.

Sabitlemenin Bedeli

Sabitleme güvenliği artıran bir karardır, ama bedeli ölçümde görülür. Sabit özet, sunucunun şu anki sertifikasına bağlanır ve sertifika değiştiğinde istemci ile sunucu artık aynı şeyi beklemez. Sınırlayıcı ölçümde bu, tek bir isteğin kaybı olarak göründü; gerçek bir uygulamada aynı alan adına giden her HTTPS isteği aynı kapıya takılır ve kayıp tek istekle sınırlı kalmaz.

Bedel iki biçimde yönetilir. Birincisi, yeni sertifikanın özeti, sertifika değişmeden önce yapılandırmaya eklenir; böylece eski ve yeni sertifika bir süre birlikte kabul edilir. İkincisi, uygulamanın güncellenmesi için geçecek sürenin hesaba katılmasıdır: yapılandırma uygulamayla birlikte gider, sunucudaki sertifika ise hemen değişebilir. Kullanıcı eski sürümde kalırsa, yeni sertifika geldiği an eski sürümdeki bütün sabitli istekler kayıp olur. Bu derste model tek özet kurduğu için yalnız bu ikinci hâli ölçer. Sonuç şudur: sabitleme güvenlik kazandırır, ama sertifika yenileme takvimini yapılandırma takvimine bağlar. Bu bağ, yayın planında sertifika değişikliğinin uygulama sürümünden önce hazırlanmasını gerektirir; aksi hâlde yeni sertifika geldiği gün eski sürümlerdeki sabitli istekler sessizce düşer.

Yaygın Yanılgılar

HTTPS olan her istek sunucuya ulaşır. Sabitleme varsa ulaşmaz. Sınırlayıcı ölçümde stok.7 HTTPS ile gitti, ama sertifikası sabitlenen özetle eşleşmediği için kayıp oldu.

Yapılandırma tekrarları önler. Önlemez. Aynı isteğin iki kopyası aynı kuralları sağladığında ikisi de geçer. Yinelenen ölçümü bunu gösterdi: yapılandırma yalnız izin verir ya da vermez, kaç kez geçtiğini saymaz.

Bir adresi açmak onun alt adlarını da açar. Yukarıdaki ölçüm bunu çürüttü: açık adres tam eşleşmedir. Alt adları kapsamak ayrı bir ayardır ve bu dersin modeli onu kurmaz.

Sabitleme düz metin isteği de korur. Korumaz. Düz metin istekte sertifika yoktur; model bu dalda özete hiç bakmaz, istek yalnız adres listesine göre geçer ya da düşer.

Engellenen istek en azından bir yerde kayıtlıdır. Sunucu tarafında kayıt yoktur, çünkü istek sunucuya hiç gitmedi. Kayıp olay yalnız istemcinin kendi günlüğünde iz bırakır; bu yüzden istemci günlüğü bu tür kaybın tek kaynağıdır.

Özet

  • Bu dersin teslimi: kayıp. Düz metin diye engellenen istek sunucuya hiç ulaşmaz.
  • Yapılandırma düz metin trafiğine, güven çapasına ve sertifika sabitlemeye karar verir; alan adına özel kural tek bir adresi serbest bırakabilir.
  • Dört istekten ikisi düz metin olduğunda iki istek birebir, ikisi kayıp ulaşır: birebir 2, kayıp 2.
  • Yeniden denenen istek iki kez geçerse sunucu iki kopya alır; yapılandırma yinelenmeyi ayıklamaz.
  • Aynı anahtarlı istek HTTPS’e yeniden gönderilince ilk istek ezilen olur. Sabitleme uyuşmazlığı kayıptır; iki neden tek kategoridir.

Sonraki Adım

İstek artık ağa çıkabiliyor. Ama sunucu isteği kabul etmek için bir erişim belirteci ister ve belirteç süresi dolunca yenilenmesi gerekir. Sonraki derste eşzamanlı iki istek aynı anda yetkisiz yanıt alır ve ikisi de yenileme başlatır; sunucuya aynı yenileme olayı iki kopya ulaşır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat