Hashing – Giriş

HASHING GİRİŞ

Bu makalede hashing’e küçük bir giriş yapacağım. Hashing ile ilgili konulara diğer makalelerimde değiniyor olacağım.

Hashing metotlarının sergilediği davranışları şu şekilde özetleyebiliriz:

  • Verilen bir input için unique değer üretir
  • Bu unique değer aynı inputla daha sonra tekrar üretilebilir (tekrar üretilebilir olması veriye erişim açısından kullanışlıdır)
  • Oluşan değer, girilen input ile alakalı herhangi bir ipucu sağlamaz, birbirinden tamamen farklı gözükür.

Birçok hashing algoritması bulunmaktadır. Bunlardan SHA-256 algoritması MD5 ve SHA-1 gibi olağan hashing algoritmalarından daha çok performans gösterir.

Python’da hashing işlemleri için kullanabileceğiniz hashlib isminde bir kütüphane bulunmaktadır. hashing kütüphanesinde MD5 algoritması deprecated olduğu için güvenlik hassasiyeti bulunduran yeni projelerinizde kullanılmaması tavsiye edilir.

Burada iki kavramdan bahsetmek istiyorum:

  • Message confidentality: Yetkili kimseler dışında mesajı kimsenin okuyamaması durumudur
  • Message integrity: Yetkisiz tarafların yapılan değişikliğin fark edilmeden mesajı değiştiremeyeceği anlamına gelir.

Hemen ardından bu iki kavramla ilgili başka bir kavrama değinelim.
Message digest: Mesajın içeriğini message digest ile birleştirdiğinizde, gönderdiğiniz mesajda bir nevi parmak izi bırakmış olursunuz. İletmiş olduğunuz mesajın değiştirilip değiştirilmediğini message digest ile öğrenebilirsiniz. MD5’in açılımı da aslında “Message Digest”tir.

MD5 gibi bir message digest algoritması, girilen herhangi bir veriyi (bu boş bir string olsa dahi) sabit uzunluktaki bi sayıya dönüştürür.

  • Aynı döküman her zaman aynı digest değerini oluşturur.
  • Oluşturulan digest, girilen input ile alakalı herhangi bir bilgi veya ipucu taşımaz.

Bir insanı nasıl parmak izinden tespit edebiliyorsak bu oluşturulan digest değeri de karşılaştığınız her dosyayı tanımada kullanılır.

Bir kasabada işlenen bir suçta elinizde sadece parmak izi olsun ve bu parmak izinin kime ait olduğuna dair bilgisayarlar kayıtlarında herhangi bir veri olmasın. Bu durumda o kasabadaki herkesin parmak izini alıp karşılaştırmanız gerekir. Döküman ve dosyalardaki durum da böyledir, her dosyanın digest değeri farklıdır.

Oluşturulan hash değeriyle dosyaya geri dönemiyoruz, ama karşılaştığımız dosyanın hash değeriyle elimizdeki hash değerini karşılaştırarak aradığımız dosyayı kolayca bulabiliriz.

MD5 ile oluşturulan herhangi bir digest değeri hafızada her zaman 16 byte’lık yer kaplar. Aşağıdaki örnekte hexdigest metoduna herhangi bir değer vermedik, yani boş string değerinin hash’ini oluşturmasını istedik ve oluşturdu. MD5 ile oluşan digest bilgisine bakalım:

import hashlib
md5hasher = hashlib.md5()
md5hasher.hexdigest()
'd41d8cd98f00b204e9800998ecf8427e'

Yine çok büyük bir string’i digest ettiğimizde, yani örneğin bir kitaptaki tüm cümleleri yanyana koyup string olarak metoda verelim, yine aynı uzunlukta farklı bir hash değeri elde edeceğiz. Bu durumda çıktı olarak elimizde yine 16 byte’lık bir değer olacaktır, yani elimizde sıkıştırılmış bir değer vardır diyebiliriz. Hash değeriyle dosyayı tanıyabiliyoruz ama dosyaya tamamen ulaşamadığımız için bu durumu veri kaybı olarak nitelendirebiliriz. Aynı dosyayı tekrar digest ettiğimizde aynı hash değerine ulaşırız bu duruma ise tutarlılık olarak nitelendiririz. Yani burada hashing foksiyonlarının 3 yapısı ortaya çıkıyor:

  • Tutarlılık
  • Sıkıştırma
  • Veri kaybı

Fakat bu üçünün olması, her hash fonksiyonunun güvenli olduğu anlamına gelmez. Bunun için hash algoritmalarının şu 3 farklı özelliğe daha sahip olması gerekmektedir:

  • Preimage resistance
  • Second-preimage resistance
  • Collision resistance

Bunları sırayla açalım:

Preimage resistance: Bir hash değerini bi arkadaşınıza verin ve arkadaşınız bu digest değerinini üretmek için hangi input değerini kullandığınızı bilmesin. Burda arkadaşınız olağanüstü bir çaba harcamadan sizin digest değerini oluşturmak için kullandığınız input değerindeki bir karakteri dahi bulamaz. Hashing hesaplamalarını göz önüne alırsak imkansız miktarda bi iş yüküne denk gelir.

Second-preimage resistance: Preimage resistance hakkında bir hash değerini üreten herhangi bir input değerini bulmanın ne kadar zor olduğundan bahsetmiştik. Second-preimage resistance’ta ise bu hash değerini üreten bir input değerini biliyoruz diyelim, bu input değerinden yola çıkarak denemek isteseniz bile aynı hash değerini üreten farklı input değerleri bulmak yine çok zor olacak ve bizi yine brute-force kullanmaya itecektir.

Collision resistance: Aynı hash değerini üreten herhangi iki input değeri bulmanın ne kadar zor olduğu anlamını belirtir. Bir hash algoritması burada dirençliyse bilinçli olarak seçtiğimiz iki tane sayının aynı hash değerini üretmemesi gerekir. Elimizdeki input değerinde herhangi bir karakteri değiştirdiğimizde oluşan değer, bir önceki değerden bambaşka bir hâl halmakta ve aynı değeri üreten başka bir çıktıyı bulmak için bizi yine tek seçenek olan brute-force’a yönlendirmektedir.
bob: 9f9d51bc70ef21ca5c14f307980a29d8
cob: 386685f06beecb9f35db2e22da429ec9
gibi. Burda bir çıktıyı diğerine bağlayacak herhangi bi örüntü de göremiyoruz. Buna da birçok kriptoğrafik algoritmada ortak olan “avalanche özelliği” diyoruz, yani input değerinde yapılan herhangi küçük bi değişiklik yine output değerinde çok büyük ve tahmin edilemez bi değişiklik yaratmalıdır.

MD5 Hakkında

Araştırmacılar MD5 algoritmasının preimage resistance’ını kıracak teorik bir yol buldular aslında. MD5’te bir hash değerine karşılık gelen herhangi bir input değerini bulmak için normalde 2^128 kere brute-force denemeniz gerekir ama araştırmacılar bu sayıyı 2^123’e indirmeyi başardılar. Teorik olarak sayı küçülse de aslında pratikte bu sayı hâlâ çok büyük bir sayı yani uygulanabilir değil.

Fakat collision resistance göz önüne alındığında MD5 algoritmasının çok kırılgan olduğu gözlemlenebilir. Aynı MD5 hash değerine sahip iki tane input değeri kolaylıkla üretilebilmektedir. MD5’in bu hassasiyeti ile TLS protokolünde kullanmak için pratik olarak sahte sertifika elde edilebilmektedir.

Bu ikisini göz önüne aldığımızda MD5’in collision resistance’ının düşük olduğunu ama second-preimage resistance’ının yüksek olduğunu söyleyebiliriz. MD5’in preimage resistance’larını kırmaya yeltenen kimse olmamasına karşın yine de birçok kişinin görüşü MD5’in yine de bu resistance’larda güvenlik açığı olduğu yönünde.

Son olarak; kriptografi’de neredeyse her durumda raw byte string kullanılmalıdır. Python2’de unicode string ve byte string kavramları ayrı ele alındığını biliyoruz. Yazdığınız kodlarda kriptografik algoritmalar kullanırken her zaman byte kullanmaya dikkat ediniz.

Kaynak: Practical Cryptography in Python: Learning Correct Cryptography by Example