Kodlama, hash'leme ve şifreleme üç ayrı şeydir
Karmakarışık görünen bir dize, korunan bir dize değildir. Okunmaz çıktı üreten üç ayrı işlem vardır ve gerçek güvenlik hataları da bunların birbirine karıştırılmasından doğar.
| Geri alınabilir mi? | Anahtar gerekir mi? | Ne işe yarar | |
|---|---|---|---|
| Kodlama | Evet, herkes tarafından | Hayır | Baytları, onları ham haliyle taşıyamayan bir kanaldan geçirmek |
| Hash'leme | Hayır | Hayır | Parmak izleri, bütünlük denetimleri, arama anahtarları |
| Şifreleme | Evet, anahtarla | Evet | Gizlilik |
Kodlama bir alfabe değişikliğidir. Base64, yüzde kodlama, onaltılık gösterim ve HTML varlıkları aynı bilgiyi farklı yazar ki akışın ilerisindeki bir ayrıştırıcı ona takılmasın. İşin içinde bir sır yoktur, dolayısıyla saklanacak bir şey de yoktur. Sezar kaydırması da buraya girer: Caesar Cipher aracı 25 kaydırmanın hepsini bir anda deneyerek çözer, bu da sabit bir yerine koymanın ne kadar koruduğunun adil bir özetidir. Hash'leme, herhangi bir girdiyi sabit uzunlukta bir özete eşler ve tersine çalıştırılamaz. Üçünün içinde gizlilik sağlayan tek şey şifrelemedir ve şifrelemenin işin içinde her zaman bir anahtarı vardır. Anahtarı gösteremiyorsanız ortada şifrelenen bir şey yoktur.
Base64 ve bedeli
Base64, üç baytı (24 bit) altı bitlik dört gruba böler ve her grubu 64 karakterlik bir alfabeden tek bir karakter olarak yazar. Üç bayta dört karakter, çıktının üçte bir büyük olması demektir. Bu en çok dosya gömerken önem taşır: File to Base64 aracı bir data URL üretir, dolayısıyla 300 KB'lik bir görsel HTML'inize kabaca 400 KB olarak iner ve her sayfa görüntülemede yeniden indirilir.
Girdi üç baytın katı değilse son grup = ile doldurulur:
"abc" -> YWJj (3 bytes, no padding)
"ab" -> YWI= (2 bytes, one =)
"a" -> YQ== (1 byte, two =)
Standart alfabe + ve / ile biter; bu bir URL'de sorundur, çünkü form verisinde + boşluk olarak çözülür ve / bir yol ayırıcısıdır. Bu yüzden ikinci bir alfabe vardır: Base64url, + yerine -, / yerine _ kullanır ve genellikle dolguyu atar. Bir URL'den kopyalanan bir tokenın, standart alfabeye ayarlı bir çözücüde bazen başarısız olmasının nedeni budur. Base64 Encoder ikisini de karşılar ve Unicode'u doğru işler ki naif uygulamalar çoğu zaman bunu beceremez: metnin Base64 adımından önce UTF-8 ile kodlanması gerekir.
Bunların hiçbiri bir güvenlik önlemi değildir. Bir ürünün URL'de "şifrelenmiş" tanımlayıcı dediği bir değer, çoğu zaman herkesin saniyeler içinde okuyup değiştirip yeniden kodlayabileceği düz bir tam sayının Base64 hali olur. Aynısı bir JSON Web Token'ın yükü için de geçerlidir: JWT Decoder onu anahtarsız okur, çünkü açılacak bir şey yoktur.
Yüzde kodlama ve hangi işlevin kullanılacağı
Yüzde kodlama bir baytı % ve iki onaltılık basamakla değiştirir. Karakterler üzerinde değil baytlar üzerinde çalışır, dolayısıyla ASCII dışı metin önce UTF-8 ile kodlanır: é karakteri %C3%A9 olur.
Ayrılmamış karakterlerin (A-Z, a-z, 0-9, -, ., _, ~) hiçbir zaman kodlanması gerekmez. Ayrılmış karakterler (: / ? # [ ] @ ! $ & ' ( ) * + , ; =) bir URL'ye yapısını veren ayraçlardır ve birinin kodlanması gerekip gerekmediği, onun ayraç olarak mı yoksa veri olarak mı kullanıldığına bağlıdır. İki JavaScript işlevi arasındaki fark da budur:
encodeURI("https://ex.com/s?q=cats & dogs")
// https://ex.com/s?q=cats%20&%20dogs the & still splits the query
encodeURIComponent("cats & dogs")
// cats%20%26%20dogs safe as a single value
encodeURI, geçerli hale getirmek istediğiniz bütün bir URL içindir, bu yüzden ayraçlara dokunmaz. encodeURIComponent ise bir yol parçasına ya da sorgu parametresine girecek tek bir değer içindir, bu yüzden onları kodlar. Eklediğiniz her şey için ikincisini kullanın ve onun yine de !, ', (, ) ve * karakterlerine dokunmadığını unutmayın.
Bir de artı işareti var. application/x-www-form-urlencoded olarak gönderilen form gövdeleri boşluğu + olarak kodlar ve bu gelenek sorgu dizelerine sızmıştır, dolayısıyla çoğu sunucu sorgudaki + işaretini boşluk olarak çözer. Gerçek bir artının %2B olarak gönderilmesi gerekir; alex+billing@example.com adresinin bu kadar sık alex billing@example.com olarak, etiketi yok edilmiş biçimde varmasının nedeni de budur. Buna karşılık bir yol parçasında + yalnızca artıdır ve boşluk %20 olur. Bir değer bir gidiş dönüşten sağ çıkıp bir sonrakinde kırıldığında URL Encoder bunu görünür kılar.
Kaçış, değerin nereye düştüğüne bağlıdır
Bir dizenin tek bir "web için kaçırılmış" biçimi yoktur. Kararı bağlam verir.
| Bağlam | Doğru işlem |
|---|---|
| HTML metni | &, < ve > için varlıklar |
| Tırnaklı öznitelik | & için ve onu saran tırnak hangisiyse onun için varlıklar; ayrıca özniteliği her zaman tırnaklayın |
<script> içi | Varlıklar değil, JavaScript dize kaçışı |
href ya da src içindeki URL | Değeri yüzde kodlayın, öznitelik kaçışı uygulayın, sonra şemayı denetleyin |
| CSS değeri | CSS kaçışı; url() ifadesini asla kullanıcı girdisinden kurmayın |
İnsanları en çok script durumu yanıltır. Script içeriğinde varlık çözümü yapılmaz, dolayısıyla oraya yazılan bir varlık olduğu gibi kalır; HTML ayrıştırıcısı da </script dizisini nerede görürse orada bloğu kapatır, tırnaklı bir dizenin içinde olsun olmasın:
<script>var s = "</script>";</script> <!-- block ends early -->
<script>var s = "<\/script>";</script> <!-- correct -->
URL'ler için de tek başına kaçış yetmez. javascript: ile başlayan bir değer ne kadar özenle kodlanmış olursa olsun tıklandığında çalışır, dolayısıyla şemayı izin verilenler listesine karşı doğrulayın. HTML Entities aracı metin ve öznitelik durumlarını kapsar ve varlıkları geri çözer; bu da bir API bir şeyi &amp; haline iki kez kaçırdığında sık duyulan bir ihtiyaçtır.
Hash'leme: hangi algoritma ve ne için
| Algoritma | Özet | Durum |
|---|---|---|
| MD5 | 128 bit | Kırık. Çakışma üretmek çocuk oyuncağı |
| SHA-1 | 160 bit | Kırık. Seçilmiş önekli çakışmalar hem uygulanabilir hem ucuz |
| SHA-256, SHA-512 | 256, 512 bit | Bütünlük ve imzalar için uygun |
| bcrypt, scrypt, Argon2 | değişken | Yalnızca parolalar |
MD5 ve SHA-1 çakışma direncinde çuvallar; yani bir saldırgan aynı özete sahip iki farklı girdi kurabilir. Bir hash'in bir belgenin yerine geçtiği hiçbir yerde kullanılamazlar: imzalar, sertifikalar, içerik adresleme, saldırganın sağladığı bir şeyin yinelenenlerini ayıklamak. İkisi de ön görüntü açısından kırık değildir, dolayısıyla eski bir MD5 sağlama toplamı kazara oluşan bozulmayı hâlâ yakalar, ama ikisinin de yeni işlerde yeri yoktur. SHA-256 kullanın; Hash Generator bunu metin için olduğu kadar dosyalar için de hesaplar.
Parolalar başka bir sorundur ve hızlı bir hash tam da hızlı olduğu için yanlış araçtır. Sıradan donanım saniyede milyarlarca SHA-256 işlemi yapar, dolayısıyla sızmış bir SHA-256 parola hash'i tablosu, sızmış bir parola tablosudur. bcrypt, scrypt ve Argon2id bilerek yavaş ve ayarlanabilirdir; donanım geliştikçe yükselttiğiniz bir iş faktörleri (son ikisinde ayrıca bir bellek maliyeti) vardır ve her biri çıktısında benzersiz bir rastgele tuz saklar.
Bir hash aynı zamanda bir imza değildir. Bir iletinin anahtar sahibi birinden geldiğini kanıtlamak için, sırrı ve iletiyi yan yana yapıştırıp hash'lemek yerine HMAC-SHA-256 kullanın; ilki uzunluk uzatma saldırısına açıktır.
Bir hash'in yapamadığı
Bir hash hesapla geri alınamaz. Olası girdiler kümesi tek tek sayılabilecek kadar küçük olduğunda aramayla geri alınabilir: dört haneli her PIN'i hash'leyin, on bin özetin hepsi anında elinizde olur; aynısı sızmış bir listedeki her posta kodu, telefon numarası ya da adres için de geçerlidir. Bir tanımlayıcıyı hash'lemek onu anonimleştirmez.
Her kayıtla birlikte saklanan ve girdiye karıştırılan benzersiz rastgele bir değer olan tuzlama, tek bir hedefi tahmin etmeyi zorlaştırmaz, ama saldırganı her kayda ayrı ayrı saldırmaya zorlar ve önceden hesaplanmış tabloları değersiz kılar. Düşük entropili değerlerde asıl işe yarayan kısım, veritabanının dışında tutulan gizli bir anahtar olan biberdir.
Bilinmeyen bir dizeyi tanımak
| Biçim | Belirtisi | Örnek |
|---|---|---|
| Onaltılık | Yalnızca 0-9a-f, çift uzunluk. 32 karakter MD5, 40 SHA-1, 64 SHA-256 demektir | 5d41402abc4b2a76b9719d911017c592 |
| Base64 | + ve / içeren karışık harf durumu, uzunluk 4'ün katı, sonu = ya da == olabilir | SGVsbG8sIHdvcmxkIQ== |
| Base64url | Aynısı, ama - ve _ ile ve genellikle dolgusuz | eyJzdWIiOiIxMjM0NSJ9 |
| Yüzde kodlanmış | % ve ardından iki onaltılık basamak | a%20b%26c |
| JWT | Noktalarla ayrılmış üç Base64url parçası, eyJ ile başlar | eyJhbGciOi... |
| bcrypt | Tam olarak 60 karakter, $2a$, $2b$ ya da $2y$ ile ve bir maliyetle başlar | $2b$12$... |
| Argon2 | $argon2id$v=19$m=...,t=...,p=...$salt$hash |
eyJ ezberlemeye değer: {" ifadesinin Base64 halidir, dolayısıyla böyle başlayan her parça, token olsun olmasın, kodlanmış bir JSON nesnesidir.
Çözmenin başarılı olması tahminin doğru olduğunun kanıtı değildir, çünkü Base64 her girdiyi bayta çevirir. Sonucun makul bir metin olduğunu ya da bilinen bir dosya imzasıyla başladığını denetleyin: iVBORw0KGgo ile başlayan Base64 bir PNG'dir, /9j/ bir JPEG, JVBERi0 bir PDF, UEsDB ise bir zip'tir. Baytlar hiçbir şeye benzemiyorsa çıktıyı Text to Binary aracından geçirip kodları doğrudan okuyun ya da değerin Base64 ile kodlanmadan önce yüzde kodlanıp kodlanmadığına bakın; bu ikili sarmalama yaygındır.