İçeriğe atla
HexaTransfer
Bloga dön
Sifreleme ve guvenlik

Güvenli rastgele sayı üretimi: güçlü kriptografinin temeli

Güvenli rastgele üretim şifreleme için kritiktir. crypto.getRandomValues nasıl çalışır ve zayıf rastgelelik neden güvenliği bozar.

Güvenli rastgele sayı üretimi, diğer tüm kriptografi parçalarının üzerine inşa edildiği temeli oluşturur. JavaScript'te crypto.getRandomValues(buffer), bir TypedArray'i işletim sisteminin CSPRNG'inden kriptografik açıdan güvenli rastgele baytlarla doldurur (Linux/macOS'ta /dev/urandom, Windows'ta BCryptGenRandom, iOS/macOS'ta SecRandomCopyBytes). Güvenlikle ilgili herhangi bir şey için Math.random() kullanmayın — Mulberry32 veya xorshift tarzı hız için tasarlanmış, tahmin edilemezlik için değil; birkaç örnek gözlemledikten sonra çıktısı tahmin edilebilir olan bir PRNG'dir. Zayıf RNG; AES anahtarlarını, TLS el sıkışmalarını, GCM'deki nonce benzersizliğini, belirteç tahmin edilemezliğini ve öngörülemeyen bitlere dayanan diğer tüm güvenlik primitiflerini bozar.

Rastgele ile Kriptografik Rastgele Arasındaki Fark

PRNG (sahte rastgele sayı üreteci), bir tohumdan deterministik bir akış üretir. Tohum ve algoritma verildiğinde her çıktıyı yeniden üretebilirsiniz. Oyunlar, simülasyonlar ve Monte Carlo yöntemleri için uygundur. Kriptografi için felakettir.

CSPRNG (kriptografik açıdan güvenli PRNG), gerçek bir entropi kaynağından tohumlanır (termal gürültü, kesme zamanlaması, Intel'in RDSEED gibi donanım RNG talimatları) ve tasarımı, çıktının gerçek rastgelelikten hesaplamalı olarak ayırt edilemez olmasını sağlar; geçmiş çıktı gözlemlenmesi gelecek çıktıyı tahmin etmeye yardımcı olmaz.

JavaScript her ikisini sağlar. Math.random() bir PRNG'dir. crypto.getRandomValues() ise işletim sisteminin CSPRNG'i üzerindeki bir sarıcıdır. Bir satır kod farkı, büyük güvenlik farkı.

Kanonik Doğru Kullanım

// 256 bit AES anahtarı değerinde rastgele bayt üret
const keyBytes = crypto.getRandomValues(new Uint8Array(32));

// 96 bit GCM nonce üret
const nonce = crypto.getRandomValues(new Uint8Array(12));

// 128 bit salt üret
const salt = crypto.getRandomValues(new Uint8Array(16));

// URL güvenli rastgele belirteç üret
const tokenBytes = crypto.getRandomValues(new Uint8Array(32));
const token = btoa(String.fromCharCode(...tokenBytes))
  .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

crypto.getRandomValues() senkrondur, tamponu yerinde doldurur, tamponu döndürür. Tek bir çağrıda maksimum istek boyutu 65.536 bayttır (engellemeyi önlemek için spesifikasyon tarafından getirilen kota). Daha fazla rastgele materyal için tekrar çağırın.

Node.js Eşdeğerleri

const { randomBytes, randomFillSync, webcrypto } = require('crypto');

const keyBytes = randomBytes(32);  // Buffer döndürür
// Veya Web Crypto uyumlu
const nonce = webcrypto.getRandomValues(new Uint8Array(12));

Node'un randomBytes'ı, Web Crypto'nun aynı temel CSPRNG'den çeker. Hangisi kod stilinize uyuyorsa onu kullanın. Her iki ortamda çalışan izomorfik kod için webcrypto.getRandomValues, tarayıcıyla tam olarak eşleşir.

Math.random Neden Başarısız Olur

V8 (Chrome/Node), SpiderMonkey (Firefox) ve JavaScriptCore (Safari) Math.random()'ı kriptografik garantisi olmayan hızlı bir PRNG olarak uygular. V8 bir xorshift128+ varyantı kullanır. Araştırmacılar, yaklaşık 5 çıktı gözemledikten sonra saldırganın iç durumu kurtarıp gelecekteki tüm çıktıları tahmin edebildiğini göstermiştir. 2015'te Mike Pound ve meslektaşları V8'in Math.random durumunu gerçek dünya güvenlik açığı ödüllerinde tersine mühendislik yaptı.

Oturum belirteçleri, parola sıfırlama bağlantıları, şifreleme nonce'ları veya paylaşım kimlikleri üretmek için Math.random() kullanırsanız, bunlardan birkaçını gözlemleyen saldırganlar geri kalanlarını tahmin edebilir. Bu teorik değil; denetimlerde yaygın bir hata sınıfıdır.

Yaygın Yanlış Kullanımlar

Kütüphane PRNG'yi Math.random() ile tohumlama:

// YANLIŞ
const seed = Math.floor(Math.random() * 2**32);

Tohumdan sonra hiçbir şey tohumdan daha rastgele olamaz. Bunun yerine crypto.getRandomValues(new Uint32Array(1))[0] kullanın.

Entropi olarak Date.now() kullanmak: Zaman, dar pencereler içinde tahmin edilebilirdir. Küçük rastgele faktörle birleştirilmiş olsa bile zaman damgaları saldırganlar için yeterli bit sızdırır.

Kaynakları XOR yaparak kendiniz karıştırmak: Yapmayın. İşletim sistemi CSPRNG'leri zaten her faydalı entropi kaynağını karıştırır. Kendi karıştırmanızı eklemek genellikle entropiyi artırmak yerine azaltır.

Aralık üretirken modülo önyargısı: randomBytes[0] % 10, 256'nın 10'un katı olmadığı için 0-9 üzerinde düzgün dağılımlı değildir. Bir aralıkta düzgün rastgele tamsayılar için reddiye örneklemesi kullanın:

function randomInt(max) {
  const range = new Uint32Array(1);
  const threshold = 2**32 - (2**32 % max);
  do {
    crypto.getRandomValues(range);
  } while (range[0] >= threshold);
  return range[0] % max;
}

Entropi Kaynakları ve Önyükleme Zamanı Endişeleri

Linux'ta /dev/urandom, erken önyükleme sonrasında her zaman güvenlidir. Donanım RNG olmayan sistemlerin ilk birkaç saniyesinde çekirdek havuzu yetersiz tohumlanmış olabilir. Bu, bir yamanın entropi karıştırmayı kaldırıp yalnızca işlem kimliğini tohum olarak bıraktığı 2008 Debian OpenSSL hatasında istismar edildi. Bu pencerede üretilen anahtarların yalnızca 2^15 olası değeri vardı — saniyeler içinde numaralandırıldı.

Modern sistemler çekirdek CSPRNG'yi şunlardan tohumlar: x86-64'te RDSEED (mevcut olduğunda), ARMv8.5-A RNG talimatları, çeşitli çevre birimlerinden termal gürültü, kesme zamanlaması, etkileşimliyse klavye/fare. Intel Ice Lake veya AMD Zen 3+'ı içeren sunucularda CSPRNG, önyüklemeden mikrosaniyeler içinde tohumlanır.

Docker konteynerleri için: ana bilgisayarın /dev/urandom'u varsayılan olarak iletilir. Herhangi bir işlem gerekmez. Sunucusuz (AWS Lambda, Cloudflare Workers) için çalışma zamanı, çağrı başına entropi tohumlamayı yönetir.

Oturum Belirteçleri ve Paylaşım Kimlikleri

Dosya aktarım hizmeti için şunlara rastgele tanımlayıcılar üretirsiniz:

  • URL'lerdeki dosya kimlikleri (saldırganlar geçerli kimlikleri tahmin edememeli)
  • Parola korumalı bağlantılar için paylaşım belirteçleri
  • CSRF belirteçleri
  • Şifreleme anahtarları (dosya başına AES anahtarları)
  • GCM için nonce'lar

Minimum uzunluk: çakışma ve tahmin edilemezlik için 128 bit (16 bayt), anahtarlar için 256 bit (32 bayt). Base64url kodlama yaklaşık yüzde 33 uzunluk ekler; onaltılık yüzde 100 ekler.

32 baytlık base64url kodlamalı belirteç 43 karakter uzunluğundadır ve 2^256'da pratikte çakışmadan muaftır.

Zayıf RNG Testi

RNG'nizin bozuk veya zayıf olduğunun işaretleri:

  • Farklı isteklerle üretilen özdeş belirteçler (büyük olması gereken alanda çakışma)
  • Çıktı görsel testleri geçer ama dieharder veya PractRand istatistiksel pil testlerinde başarısız olur
  • Süreç yeniden başlatmadan sonra tohum yeniden kullanımı — her dağıtım aynı başlangıç durumunu kullanır
  • Üretilen anahtarlar örüntülere düşer (örn. ilk 4 bayt değişiyor ama son 28'i aynı)

Üretimde bunları, bir şey felaket boyutunda yanlış gitmediği sürece muhtemelen görmezsiniz. Başarısızlık modu genellikle sessizdir: saldırılar 2^256'lık arama alanında pratik hâle gelir.

Denetim: kod tabanındaki her Math.random() çağrısı gözden geçirilmelidir. Kaynak ağacınızda Math.random için grep, iyi bir haftalık hijyen kontrolüdür. Güvenlikle ilgili herhangi bir çağrı noktasını crypto.getRandomValues'a dönüştürmek dakikalar alır ve gerçek güvenlik açıklarını önler.

Rastgele Dizeler ve UUID'ler

İnsan tarafından görülebilen tanımlayıcılar için crypto.randomUUID(), standart biçimde bir v4 UUID döndürür (122 bit rastgelelik):

const id = crypto.randomUUID();
// "f47ac10b-58cc-4372-a567-0e02b2c3d479"

Chrome 92+, Firefox 95+, Safari 15.4+, Node 14.17+'da desteklenmektedir. Veritabanı birincil anahtarları, API istek kimlikleri ve güvenlik açısından kritik olmayan benzersiz tanımlayıcılar için uygundur. Özel biçimler veya daha yüksek entropi gerektiren her şey için açık getRandomValues kullanın.

Sunucularda: Özel RNG Havuzlarından Kaçının

Bazı sunucu çerçeveleri, sistem CSPRNG'ini uygulama düzeyindeki entropi ile "karıştırdığını" iddia eden kendi rastgele havuzlarını sunar. Buna şüpheyle yaklaşın. Özel karıştırma, çekirdeğin çıktısını nadiren iyileştirir; hatalı olduğunda entropi sessizce azalabilir.

Node veya büyük bir çalışma zamanındaysanız, yerleşik crypto.randomBytes doğru ve hızlıdır. Onu üçüncü taraf karıştırıcılarla değiştirmeyin.

Özet

Dosya aktarım uygulamasındaki her kriptografi parçası, öngörülemeyen rastgele baytlara bağlıdır. AES anahtarları, GCM nonce'ları, PBKDF2 salt'ları, paylaşım belirteçleri, CSRF karşıtı belirteçler, oturum kimlikleri — hepsinin aynı primitive'e ihtiyacı vardır: tarayıcılarda crypto.getRandomValues(), Node'da crypto.randomBytes() veya webcrypto.getRandomValues. Bunları kullanın. Asla Math.random(). Asla zaman damgası. Asla kendi karıştırıcınız.

HexaTransfer, dosya başına her AES anahtarını, nonce'unu ve URL tanımlayıcısını istemcide crypto.getRandomValues()'den türetir. Sunucu taraflı paylaşım kimlikleri crypto.randomBytes'tan gelir. Tek API, tutarlı davranış; sisteme yanlışlıkla tahmin edilebilir bit sızdırmanın yolu yok.

Primitive sıkıcıdır tam olarak sıkıcı olması gerektiği için. Sıkıcı, doğru ve her yerde mevcut — kripto temellerinin tam olarak böyle olması gerekir.

hexatransfer.com'da deneyin — ücretsiz, hesap gerekmez, 10 GB'a kadar.

Uçtan uca şifreleme ile büyük dosyaları güvenle gönderin

Uçtan uca şifreleme ile 10 GB'a kadar dosya ücretsiz aktarın. Hesap gerekmez. Dosyalarınız yüklenmeden önce tarayıcınızda şifrelenir — başka kimse okuyamaz.

Dosya gönder