2 puan yazan GN⁺ 2023-10-24 | 1 yorum | WhatsApp'ta paylaş
  • Base64 kodlama, ikili veriyi ASCII metne dönüştürerek depolama ve aktarım sırasında verinin yanlış yorumlanma olasılığını azaltan bir yöntemdir
  • Şifreleme değil, gösterim biçimini değiştirme olduğundan kodlanmış veri, orijinal metne ya da dosya verisine kolayca geri döndürülebilir
  • 64 karakter 6 bit ile ifade edilebildiği için bir Base64 karakteri 6 bit veri taşır ve 3 bayt (24 bit) dört Base64 karakterine dönüştürülür
  • HTML içindeki Data URLs, e-postada ikili veri aktarımı ve ham ikilinin sorun çıkarabildiği metin odaklı ağlar ya da URL'ler gibi ortamlarda kullanışlıdır
  • Ruby, C#, PHP, JavaScript ve terminaldeki base64 komutu dahil olmak üzere birçok dil ve araç kodlama ve kod çözme işlevi sunar

Base64 neyi değiştirir

  • Base64 kodlama, ikili veriyi metne, daha spesifik olarak ASCII metne dönüştürür
  • Sonuç yalnızca aşağıdaki 64 karakteri kullanır
    • A-Z
    • a-z
    • 0-9
    • +
    • /
  • Bu karakter kümesi, <, >, \n gibi karakterlerin eski bilgisayarlar veya programlar tarafından yanlış yorumlandığı durumları önlemek için kullanılan güvenli bir karakter kümesidir
  • "Ruby on Rails" ifadesi Base64 ile kodlandığında UnVieSBvbiBSYWlscw== olur
  • Base64 şifreleme değildir
    • Kodlanmış veri, orijinal metne kolayca geri döndürülebilir
    • Veriyi gizlemez, yalnızca verinin gösterimini değiştirir

Base64'in kullanıldığı durumlar

  • Data URLs, görsel gibi dosya verilerinin doğrudan HTML içine yerleştirilmesine izin verir ve bunun için Base64 ile kodlanmış metin kullanır
  • Örnek biçim data:[<mime type>][;charset=<charset>][;base64],<encoded data> şeklindedir
  • E-postada, sunucuların satır sonlarını değiştirebildiği ortamlarda bile ikili veriyi güvenle taşımak için Base64 uzun süredir kullanılmaktadır
  • Görsel verisini doğrudan HTML kaynağına yerleştirirken < ve > gibi karakterlerin etiket olarak yorumlanmaması için kodlama gerekir
  • Metin veya US-ASCII verisini işlemek üzere tasarlanmış ağlar üzerinden ikili veriyi depolarken ya da aktarırken de kullanılabilir
  • URL içine yerleştirilmesi zor karakterler içeren veriyi iletirken de Base64 kullanılabilir
  • Base ailesindeki kodlamalar, nesnelerin metin editörleriyle ele alınabilmesini sağladığı için birçok uygulamada kullanılır

Kodlama algoritması

  • Base64 kodlama şu sırayla ilerler
    • Metni ikili gösterime dönüştürür
    • Bitleri 6'şar bitlik gruplara böler
    • Her 6 bitlik grubu 0'dan 63'e kadar bir ondalık sayıya dönüştürür
    • Bu sayıyı Base64 alfabesindeki karşılık gelen karaktere çevirir
  • Son grubun bitleri eksikse = veya == padding olarak eklenebilir
  • 64 karakteri ifade etmek için 6 bit gerekir
    • 2^6 = 64
    • Tek bir Base64 değeri 6 bit veriyi temsil eder
  • Bir bayt 8 bittir ve 8 ile 6'nın en yakın ortak katı 24'tür
    • 24 bit, 3 bayttır
    • 24 bit, dört adet 6 bitlik Base64 değeriyle ifade edilir

“Akshay” kodlama örneği

  • "Akshay", her karakter ASCII sayısına çevrilip ardından ikiliye dönüştürüldüğünde şu şekilde olur
    • 01000001 01101011 01110011 01101000 01100001 01111001
  • Bu veri 6 bitlik gruplara ayrıldığında sonuç şöyledir
    • 010000 010110 101101 110011 011010 000110 000101 111001
  • Her grup ondalık sayıya çevrildiğinde şu değerler elde edilir
    • 16 22 45 51 26 6 5 57
  • Base64 alfabesine dönüştürüldüğünde şu karakterler ortaya çıkar
    • Q W t z a G F 5
  • Dolayısıyla "Akshay" ifadesinin Base64 karşılığı QWtzaGF5 olur
  • Aynı şekilde görsel, PDF, metin ve video gibi dosyalar da ikiliye çevrilip ardından Base64 ile kodlanarak ASCII metin olarak saklanabilir veya aktarılabilir

Dillerde ve araçlarda kullanım

  • Ruby, Base64 modülüyle kodlama ve kod çözme işlemlerini yapar
    • Base64.encode64("Ruby on Rails")
    • Base64.decode64(encoded)
  • C#, bir dizeyi bayt dizisine dönüştürdükten sonra Convert.ToBase64String ile kodlar ve System.Convert.FromBase64String ile çözer
  • PHP, üst düzey base64_encode ve base64_decode fonksiyonlarını sunar
  • JavaScript, btoa() ile kodlar ve atob() ile çözer
  • Terminalde de base64 komutuyla kodlama ve kod çözme yapılabilir
    • echo "akshay" | base64 komutu YWtzaGF5Cg== çıktısını üretir
    • echo "YWtzaGF5Cg==" | base64 -d komutu akshay çıktısını üretir

1 yorum

 
GN⁺ 2023-10-24
Hacker News yorumları
  • Burada metni şifrelemediğini vurguladığınız için teşekkürler. Pek çok junior geliştirici, şifrelemenin geri alınabilmesi için gizli bir değer gerektirdiği, hashingin geri alınamaz olduğu ve encodingin her zaman kolayca geri çevrilebildiği farkını çok geç öğrenip sıkıntı yaşıyor.
    Çıktı rastgele görünse bile entropinin girdinin entropisiyle aynı olduğunu bilmek de önemli. Yani parolanızı daha güçlü yapmak için Base64 ile encode etmemelisiniz.

    • Biraz laf cambazlığı gibi ve asıl konuyla pek ilgisi yok ama bir parolayı Base64 ile encode etmek onu güçlendirebilir de. Parola gücü yalnızca entropi meselesi değildir; yüksek entropi sadece en etkili yöntemdir.
      Parola tamamen rastgele üretilmişse Base64 encoding hiçbir işe yaramaz. Ama parola, sözlük kelimeleri ya da hatırlaması kolay kurallar gibi düşük entropili bir şemayla oluşturulmuşsa, saldırganın akıllı parola kırıcıya Base64 encoding kuralını da hesaba katacak şekilde ayar vermesi gerekir; bu da her denemeye fazladan bir işlem eklemek kadar bir güç katar.
      Elbette böyle parola şemaları kullanmamak gerekir. “correct horse battery staple” türü parolaların yeterli olduğunu düşünüyorum.
    • Bilgisayar bilimi mezunlarının bu farkları zaten bildiğini varsayarım. Kodlamaya ilgi duyan biri de bu kavramları bir öğleden sonra içinde öğrenebilir.
    • Bununla bağlantılı olarak her zaman vurgulanmaya değer bir nokta da hash’in mutlaka kriptografik olarak güvenli olmadığıdır.
      Hashing’in güvenlik dışında da pek çok amacı vardır; bu yüzden hash kütüphaneleri de çeşitlidir. Güvenlik veya kripto ile ilgili amaçlarla hash kullanacaksanız, o amaca göre tasarlanmış bir hash kullanmalısınız. CRC hash hızlıdır, ama kullanıcı parolaları için iyi bir seçenek değildir.
  • Base64’ün ilginç yanlarından biri, herhangi bir string ile başlayıp encode etmeyi tekrar ederseniz sonucun baş kısmının giderek bir sabit noktaya yakınsamasıdır. Bunu Bash ile de görebilirsiniz.
    10 yıldan da uzun süre önce tesadüfen keşfedip şifreli bir şey gibi tweetlemiştim [1]; biri bu konuda bir blog yazısı yazdı ve burada da paylaştı ama pek tartışma olmadı [2]. Başka biri Reddit /r/compsci’ye gönderince orada blog yazısını düzelten verimli bir tartışma çıktı [3]. Blog şu an yayında değil ama Internet Archive’da bir kopyası duruyor [4].
    [1] https://twitter.com/p4bl0/status/298900842076045312
    [2] https://news.ycombinator.com/item?id=5181256
    [3] https://www.reddit.com/r/compsci/comments/18234a/the_base64_...
    [4] https://web.archive.org/web/20130315082932/http://fmota.eu/b...

  • Bash ile encode ederken -n seçeneğini kullanmalısınız: $ echo -n "abcde" |base64
    -n olmazsa echo string’in sonuna bir satır sonu karakteri ekler ve o karakter de encode edilir.

  • base64URL de var; URL’ler için güvenli olan farklı ASCII karakterleri kullanarak encode eder. Bazı geliştiriciler BASE64URL’ye doğrudan base64 diyebiliyor; bu da bilmeyenler için sorun yaratabilir.
    https://datatracker.ietf.org/doc/html/rfc4648#section-5

    • base64url’nin sorunu, ~ ve . karakterlerinin harf olmaması; bu yüzden encode edilmiş değere çift tıklayınca tamamı seçilmiyor. Kopyala-yapıştır yapmak istediğiniz birçok durumda gereksiz sürtünme yaratıyor.
      Base62 encoding (0-9A-Za-z), base64url kadar neredeyse verimli ve URL güvenliğini korurken kopyala-yapıştırı daha kolay hale getirir. İnsanların okurken yaşayacağı belirsizliği azaltmak istiyorsanız Base58’e inebilirsiniz; ama genelde BaseXX encoding yapacak kadar uzun değerlerde kopyala-yapıştır yaygın olduğundan bu büyük bir sorun değildir.
      https://en.wikipedia.org/wiki/Base62
    • Base64url genellikle padding’i de atlar.
      Padding’li Base64 string’leri her zaman 4’ün katı uzunluktadır; bu yüzden 4’ün katı olmayan bir string alırsanız aslında ne kadar padding olması gerektiğini ve son 3 byte’ın nasıl decode edileceğini anlayabilirsiniz.
      Bu yüzden Base64’te baştan == padding’ine neden ihtiyaç duyulduğu biraz kafamı karıştırıyor.
  • Taban dönüşümü konusu ne zaman açılsa, keyfi taban dönüştürücümün reklamını utanmadan yapıyorum: https://convert.zamicol.com
    “useful alphabets” altındaki base64, tabana göre tekrarlı bölme yapan “doğal” sayı tabanı; RFC’deki “bucket” dönüşüm yöntemi ise extras altında.

  • Bir şeyi encode ettiyseniz ve insanların bunu elle yazması gerekiyorsa https://en.wikipedia.org/wiki/Base32 öneririm.
    Kötü fontlar yüzünden l mi 1 mi, o mu O mu 0 mı diye karıştırmak kadar sinir bozucu çok az şey var.

  • Biraz daha titiz konuşursak, Base64’ün ikili veriyi ASCII karakterlerinin tamamına değil, ASCII’nin bir alt kümesine encode ettiğini söylemek daha doğru olur.
    ASCII’de 128 kod noktası vardır; bunların 95’i yazdırılabilir karakter, 33’ü kontrol karakteridir. Base64 ise bunların yalnızca 64’ünü, padding dahil edilirse 65’ini kullanır.

  • Yazıda = / == padding’in amacı ayrıntılı ele alınmamış; 6 bitlik gruplara tam bölünmeyen verilerin nasıl işlendiği de örnekle gösterilmemiş
    Kabaca anladığımı sanıyorum ama kesin bilmek istiyorum. = ne zaman, == ne zaman kullanılır; her zaman eklenir mi yoksa eklenmediği durumlar da var mı; "5byte" gibi bir string’in artan bitleri tam olarak nasıl işlenir; decode ederken nelere dikkat etmek gerekir—kısa ve eksiksiz yanıtlayabilirsen iyi olurdu

    • İki soru birbiriyle bağlantılı
      Bir Base64 karakteri 6 biti temsil ettiğinden, 3 baytlık bir veri bloğu Base64’te 4 karakterlik bir bloğa karşılık gelir. Bu yüzden Base64 verisini 4’er karakter halinde işlemek pratiktir
      =, encode edilmiş string uzunluğunu 4’ün katına tamamlamak için gerektiğinde 0, 1 veya 2 tane eklenen padding’dir. Örneğin "543210""543210==", "6543210""6543210="; "76543210" için padding gerekmez. Padding olarak 3 tane = gerekmez, çünkü 1 bayt veri bile en az 2 Base64 karakteri gerektirir
      Artan bitler 0 ile doldurulabilir; decoder da tam 1 bayt oluşturacak kadar bit olmadığını görüp bunları atabilir. Günümüzde çoğu durumda padding kesin zorunluluktan çok bir konvansiyon gibidir. Wikipedia maddesi oldukça ayrıntılı: https://en.wikipedia.org/wiki/Base64
    • Padding yalnızca encode edilmiş verileri birbirine eklerken veya streaming yaparken, yani encode akışının ortasına padding karakteri girdiği durumlarda gereklidir
      Bir stream’in, dosyanın veya string’in sonundaki padding karakterleri, zaten işlenen uzunluktan çıkarılabildiği için katı anlamda şart değildir
      Ancak padding’i ele alma biçimi epey inceliklidir ve bu farklar yüzünden ilginç implementasyon varyasyonları ortaya çıkmıştır: https://eprint.iacr.org/2022/361.pdf
    • Yazıya göre bir Base64 rakamı verinin 6 bitini temsil eder. Bayt 8 bittir; 8 ile 6’nın en yakın ortak katı 24 olduğundan, 24 bit, yani 3 bayt, 6 bitlik 4 Base64 rakamıyla ifade edilebilir
      Sonuçta 24 bitlik birimler halinde encode ediliyor. Veri bittiğinde kalan 24 bitlik kısım A ile değil = ile doldurulur; çünkü A, veri olarak 000000 anlamına gelir. Ben de anlamak için tamamını iki kez okudum
  • Benim Base64 encoder shader’ım burada: https://github.com/Rezmason/excel_97_egg/blob/main/glsl/base...
    GLSL’de yaklaşık 13 satıra kadar indirdim: https://github.com/Rezmason/excel_97_egg/blob/main/glsl/base...
    Bir yan projenin Cursed Mode’unda kullanılıyor; WebGL framebuffer’ını Base64 ile encode edilmiş 640x480 piksel indeks renkli BMP olarak saniyede yaklaşık 15 kez render ediyor: https://rezmason.github.io/excel_97_egg/?cursed=1

  • Derine inmeye başlayınca ek olarak ilginç ayrıntılar var; bu ayrıntıların varyasyonları da şaşırtıcı derecede çok
    Girdi verisinin uzunluğu tam olarak 3 baytın katı değilse, son 1 baytı veya 2 baytı encode etmek için 2 ya da 3 Base64 karakteri kullanılır. Bir Base64 karakteri 6 bit olduğundan, 8 bit veya 16 biti ifade etmek için 12 bit ya da 18 bit kullanılır; bunun sonucunda hiçbir şeyi encode etmeyen fazladan 4 bit veya 2 bit oluşur
    RFC, encoder’ın bu bitleri 0 olarak ayarlamasını şart koşar; ancak decoder için yalnızca bu bitleri 0 olmayan girdileri reddedebileceği söylenir. Pratikte varsayılan olarak reddeden implementasyon neredeyse yok; bildiğim kadarıyla yalnızca Ruby, Rust ve Go bu tür girdilerde hata verecek şekilde ayarlanabiliyor. Python’da validate seçeneği var ama bu bitleri doğrulamıyor
    Bir diğer büyük fark da boşlukların ve Base64 olmayan karakterlerin nasıl işlendiği. Python dahil şaşırtıcı derecede çok implementasyon, girdideki rastgele karakterleri sessizce yok sayıyor. Alfabeyi yanlış seçerseniz bu sorun yaratıyor; örneğin Python’da base64.standard_b64decode(base64.urlsafe_b64encode(b'\xFF\xFE\xFD\xFC')) hata vermeden sessizce yanlış çıktı üretiyor
    Ruby’nin Base64 encoder’ının her 60 karakterde bir satır sonu eklemesi de ilginç. PEM dışında bu kadar kısa satır isteyen standart bir encoding yok; PEM de tam olarak 64 karakterlik satırlar istediğinden bu epey tuhaf bir tercih
    Programlama dilleri ile bazı JavaScript kütüphaneleri arasındaki farkları özetleyen bir yazı yazdım [1]; ayrıca JS’e daha iyi bir Base64 ekleme çalışması da yürütüyorum [2]
    [1] https://gist.github.com/bakkot/16cae276209da91b652c2cb3f612a...
    [2] https://github.com/tc39/proposal-arraybuffer-base64