Please Click Here to see the this article in English.
Bu makalemde şifre, güvenlik kodu, aktivasyon kodu, üye kodu, CAPTCHA kodu vb işlerde kullanılmak üzere rastgele metin üretme işlemini ve bu konuda dikkat edilmesi gereken noktalardan bahsedeceğim. Bu yazıda anlatılanlara temel tteşkil etmesi için, .Net’ te yer alan rastgele işlemlerle ilgili sınıfları konu alan “.Net Ortamında Rastgele İşlemler” adlı yazımı okumanızı tavsiye ederim.
Rastgele metin(kod) üretme işi genellikle, belirli harf, sayı vb karakterlerden oluşan bir kümeden, istenen uzunluğu ulaşılana kadar rastgele karakterler seçip bunları ard arda ekleyerek gerçekleştirilir. Kod değişik amaçlara ve güvenlik ihtiyaçlarına göre değişik kurallara göre oluşturulabilir. Örneğin, en az 2 alfanümerik karakter olsun, bir küçük bir büyük karakter olsun gibi... Bu yazıda konunun bu kısmından bahsetmeyeceğim.
Konuyu daha iyi anlayabilmemiz için amacımız rastgele “üye kodu” elde etmek olsun. Şifre ya da aktivasyon kodu yerine “Üye kodu”nu seçmemdeki sebep ise, farklı zamanlarda üreteceğimiz bu “Üye kod”larının mümkün olduğunca hatta kesinlikle birbirini tekrarlamaması gerekliliğidir. Çünkü üye kodu tekil olması gereken bir özelliktir. Duruma göre değişse de bir de üreteceğimiz üye kodlarının kestirilemez olması gerekmektedir.
Tekil üye kodu elde edebilmek için şu 3 aşamayı geçmemiz lazım
1. Evrensel Kümenin Belirlenmesi: İlk olarak rastgele kodun hangi karakterlerden oluşacağı ve ne kadar uzunlukta olacağını belirlemeliyiz. Örneğin; 10 bin üyenin olması beklenen bir sistemde üyelere üye kodunu 4 haneli bir sayı olarak verecek olursak tekil üye kodları üretilirken problem yaşarız. Kesin bir kural değilse de benim fikrime göre üye kodumuz kümesi hedeflenen üye sayısının en az 100 katı olmalıdır. Böylece hem beklenenden fazla üyemiz olduğunda sıkışmamış oluruz, hem tekil de olsalar birbirine çok yakın üye kodlarının olması ihtimali azalmış olur hem de üye kodunun restgele üretiminde tekil üye kodu bulmakta zorlanmayız.
2. Ratgele Kod Üretimi: Belirlediğimiz karakterlerden oluşan belirli bir uzunlukta rastgele bir kod üretilmesidir. Ancak bir sonraki aşama olan tekillik kontrolüne fazla iş kalmaması için üreteceğimiz kodların mümkün olduğunca birbirini tekrarlamayacak bir şekilde üretilmesi gerekmektedir.
3. Tekillik Kontrolü : İlk aşamada elde edilen kodun daha önce başka bir üyeye verilip verilmediğinin kontrol edilmesidir. Eğer kod daha önce başka bir üyeye verildiyse işleme ikinci aşamadan tekrar başlamak gerekir. Bu işlem tekillik elde edilene kadar devam eder. Tekillik kontrolü, üretilen kodun veritabanında(veya başka bir yerde) kayıtlı olup olmadığının kontolü ile gerçekleştirilir. Tekilliğin sağlanamaması düşük de olsa ihtimal dahilinde olduğu için kontrolün sonsuz bir döngünün(loop) içinde kod tekil olana kadar tekrarlanması faydalı olacaktır. (Ancak bana sorarsanız kaliteli kod yazmak adına, döngüyü sonsuz yapmak yerine örneğin 100 gibi belirli bir sınır koymalı ve 100 denemeden sonra hala tekillik sağlanamadı ise program hataya düşürülmelidir. Bu sayede hem makinenin kilitlenmesini önlenmiş olunur hem de kullanılan algoritamada bir hata varsa bunun farkedilebilmesi mümkün hale gelir. Aslına bakarsanız bu makaleyi yazmamdaki sebep başıma böyle bir şeyin gelmiş olması :)
Rastgele Kod Üretimi
Daha önce belirttiğim gibi rastgele metin elde ederken istenilen uzunluğa ulaşana kadar belirlenen kümeden rastgele bir karakter seçilir. Bu karakter seçme işlemi genellikle rastgele bir sayı kullanılarak yapılır. Örneğin harf ve rakamların bulunduğu 20 karakterden oluşan bir kümeden rastgele bir karakter seçmek için 1-20 arasında rastgele bir sayı elde edilir daha sonra kümedeki, bulunan rastgele sayının sırasında olan eleman alınır. Farkettiğiniz gibi işin en kritik kısmı kullanacağımız bu rastgele sayının üretilmesidir.
Rastgele kod üretmek için, istenen kodun uzunluğu kadar rastgele sayıya ihtiyacımız vardır. Yani üye kodunda kaç karakter olacaksa arka arkaya o kadar karakter üretmeliyiz. Bunun yanında tekilliğin sağlanamaması durumunda bir o kadar daha yeni rastgele sayıya ihtiyacımız olacaktır.
Örnek
Bu örneğimizde, 7 farklı şekilde, 20 elemandan oluşan "ABCEFKNMPRSTUYZ13579" kümesindeki karakterlerden oluşan üye kodları üretmeye çalışacağız. Üye kodunun uzunluğuna göre kaç farklı değer alabiliyorsa o kadar üye kodu üreteceğiz. Örneğin; 3 karakter uzunluğundaki üye kodu 8000 (=20*20*20) farklı değer olabileceği için, rastgele 8000 üye kodu üreteceğiz. Kullandığımız yolun başarısını görebilmek için üretilen üye kodlarının tekillik ve farklılık oranını ve üretilme süresini ölçeceğiz.
Farklılık Oranı: (Farklı üye kodu sayısı / Üretilen üye kodu sayısı)
Farklı üye kodu sayısı şu şekilde bulunur; “AB3” diye bir üye kodunun 3 kez üretildiğini varsayarsak bunu 1 farklı üye kodu olarak hesaplayacağız.
Tekillik Oranı: (Tekil üye kodu sayısı / Üretilen üye kodu sayısı)
Tekil üye kodu sayısı, sadece 1 kez üretilen yani aynı değerde başka üye kodu olmayan toplam üye kodu sayısıdır. “AB3” diye bir üye kodu 3 kez üretildiyse bu üye kodu tekil değildir bu sebeple 0 tekillik değeri vardır.
Mesela: AB3, 27K, 8UK, M8E, AB3, K6D, M8E, P3L, M8E, 63A şeklinde üretilen 10 üye kodu için;
farklı üye kodları (AB3, 27K, 8UK, M8E, K6D, P3L, 63A) olmak üzere, farklılık oranı = 7/10=0,7,
tekil üye kodları (27K, 8UK, K6D, P3L, 63A) olmak üzere, tekillik oranı = 5/10 = 0,50 olacaktır.
| Metod 1 |
Geçen Süre |
Farklılık Oranı |
Tekillik Oranı |
|
Dim sb As New StringBuilder
Dim CharArray As Char() = Chars.ToCharArray
Dim startTime As DateTime = Now
For j As Integer = 1 To IterationNumber
sb.Length = 0
For i As Integer = 1 To CodeLength
Dim Rndm As New Random
Dim CharIndex As Integer = CInt(Int((CharArray.GetUpperBound(0) * Rndm.NextDouble()) + 1))
sb.Append(CharArray(CharIndex))
Next
AddToResults(sb.ToString())
Next
|
|
187ms |
0,00225 |
0,000875 |
| Metod 2 |
Geçen Süre |
Farklılık Oranı |
Tekillik Oranı |
|
Dim sb As New StringBuilder
Dim CharArray As Char() = Chars.ToCharArray
Dim startTime As DateTime = Now
For j As Integer = 1 To IterationNumber
sb.Length = 0
Dim Rndm As New Random
For i As Integer = 1 To CodeLength
Dim CharIndex As Integer = CInt(Int((CharArray.GetUpperBound(0) * Rndm.NextDouble()) + 1))
sb.Append(CharArray(CharIndex))
Next
AddToResults(sb.ToString())
Next
|
|
93ms |
0,000375 |
0 |
| Metod 3 |
Geçen Süre |
Farklılık Oranı |
Tekillik Oranı |
|
Dim sb As New StringBuilder
Dim CharArray As Char() = Chars.ToCharArray
Dim startTime As DateTime = Now
Dim Rndm As New Random
For j As Integer = 1 To IterationNumber
sb.Length = 0
For i As Integer = 1 To CodeLength
Dim CharIndex As Integer = CInt(Int((CharArray.GetUpperBound(0) * Rndm.NextDouble) + 1))
sb.Append(CharArray(CharIndex))
Next
AddToResults(sb.ToString())
Next
|
|
15ms |
0,593125 |
0,319 |
| Metod 4 |
Geçen Süre |
Farklılık Oranı |
Tekillik Oranı |
|
Dim sb As New StringBuilder
Dim CharArray As Char() = Chars.ToCharArray
Dim startTime As DateTime = Now
For j As Integer = 1 To IterationNumber
sb.Length = 0
For i As Integer = 1 To CodeLength
Randomize()
Dim CharIndex As Integer = CInt(Int((CharArray.GetUpperBound(0) * Rnd()) + 1))
sb.Append(CharArray(CharIndex))
Next
AddToResults(sb.ToString())
Next
|
|
78ms |
0,136625 |
0,003 |
| Metod 5 |
Geçen Süre |
Farklılık Oranı |
Tekillik Oranı |
|
Dim sb As New StringBuilder
Dim CharArray As Char() = Chars.ToCharArray
Dim startTime As DateTime = Now
For j As Integer = 1 To IterationNumber
sb.Length = 0
Randomize()
For i As Integer = 1 To CodeLength
Dim CharIndex As Integer = CInt(Int((CharArray.GetUpperBound(0) * Rnd()) + 1))
sb.Append(CharArray(CharIndex))
Next
AddToResults(sb.ToString())
Next
|
|
31ms |
0,092125 |
0 |
| Metod 6 |
Geçen Süre |
Farklılık Oranı |
Tekillik Oranı |
|
Dim sb As New StringBuilder
Dim CharArray As Char() = Chars.ToCharArray
Dim startTime As DateTime = Now
Randomize()
For j As Integer = 1 To IterationNumber
sb.Length = 0
For i As Integer = 1 To CodeLength
Dim CharIndex As Integer = CInt(Int((CharArray.GetUpperBound(0) * Rnd()) + 1))
sb.Append(CharArray(CharIndex))
Next
AddToResults(sb.ToString())
Next
|
|
15ms |
0,589 |
0,30925 |
| Metod 7 |
Geçen Süre |
Farklılık Oranı |
Tekillik Oranı |
|
Dim sb As New StringBuilder
Dim CharArray As Char() = Chars.ToCharArray
Dim startTime As DateTime = Now
For j As Integer = 1 To IterationNumber
sb.Length = 0
For i As Integer = 1 To CodeLength
Dim buffer(0) As Byte
Dim rngSp As New System.Security.Cryptography.RNGCryptoServiceProvider
rngSp.GetBytes(buffer)
Randomize(Convert.ToInt32(buffer(0)))
Dim CharIndex As Integer = CInt(Int((CharArray.GetUpperBound(0) * Rnd()) + 1))
sb.Append(CharArray(CharIndex))
Next
AddToResults(sb.ToString())
Next
|
|
140ms |
0,591625 |
0,315625 |

Değerlendirme
Metod 1’ de Random nesnesi tohum değeri verilmeden ve her harften önce yeniden oluşturulmaktadır. Tohum sistem saatinden kullanılmakta olduğu için takip eden birçok harf birbirleri ile aynıdır. Bu sebeple farklı ve tekil üye kodu çok azdır.
Metod 2’ de Random nesnesi tohum değeri verilmeden ve her üye kodundan önce yeniden oluşturulmaktadır. Yine Metod1’ de bahsedilen aynı ya da çok yakın tohum değeri kullanımından dolayı üye kodlarındaki harfler farklı olsa da peşpeşe üretilen bir çok üye kodu birbirleri ile aynıdır. Bu sebeple farklı ve tekil üye kodu metod1’ dekinden bile azdır. Hatta tekil üye kodu yok denebilir.
Metod 3’ te Random nesnesi ilk başta bir kere oluşturumaktadır. Daha sonra her harf için aynı nesne kullanılmaktadır. Tek Random nesnesinin kullanımı bir serinin takip edilmesine sebep olduğu için harfler ve üye kodları birbirinden farklıdır yani farklı ve tekil üye kodu fazladır. Ancak dikkat ettiyseniz bu durumda bile farklı ve tekil üye kodu çok küçüktür.
Metod 1, 2 ve 3’ te hep Random nesnesi kullanılmış ama farklı sayılarda oluşturulmuştur. Bir nesnenin oluşturulması zaman ve kaynak isteyen bir durum olduğu için dikkat ederseniz geçen süre ve oluşturulan nesne sayısı birbiri ile paralel. Yani Metod1 Metod2’ den Metod2 de Metod3’ ten daha uzun dürmüştür.
Metod4’ te Random sınıfı yerine VbMath sınıfı kullanılmıştır. Metod1’ dekine benzer şekilde tohum her harften önce Randomize çağırılarak atanmaktadır. Randomize’ ın tohum değeri ayarlaması mevcut tohumla da ilişkili olduğu için bu metod metod1’ e nazaran biraz daha başarılıdr. Ancak yine de farklılık ve tekillik oranı 1’ den çok daha küçüktür.
Metod5’ te Randomize her harf değil de her üye kodundan önce çağırılmaktadır. Bu sebeple de üye kodlarındaki harfler değilse de üye kodları birbirleri ile aynıdır. Yine Metod2’ de olduğu gibi farklı ve tekil üye kodu çok azdır. Hatta tekil üye kodu yok denebilir.
Metod6’ da Randomize en başta bir kere çağırılır daha sonra Rnd belirli bir seriyi takip eder. Bu da düzgün dağılımlı rastgele sayıların üretilmesine sebep olur. Bu nedenle farklı ve tekil üye kodu sayısı çoktur. Ancak yine de oranlar 1’ in çok altındadır.
Metod7’ de is değşik bir yol uygulanmaıştır. Burada Metod4’ te uygulanan yol uygulanmıştır. Tek fark olarak Randomize’ a RNGCryptıServiceProvider sınıfının GetBytes metodundan gelen bir sayı parametre olarak verilmiştir. Yani tohum sistem saatinden üretilmemiştir. Bu durumda farklı ve tekil üye kodu sayısı Metod3 ve Metod6’ daki değerlere çok yakındır. Ancak RNGCryptıServiceProvider’ ın her harften önce tekrar oluşturulduğunu unutmamak lazım. Bu da gösteriyor ki RNGCryptıServiceProvider sistem saatinden bağımsız ve nizami dağılımlı rastgele sayılar üretebilmektedir. Ayrıca sayılar sistem saatinden bağımsız olduğu için kestirilmeleri de imkansız denebilir.
Metod 4, 5, 6, 7’ de Randomize metodu kullanılmıştır. Ancak değişik sıklıkta çağırılmıştır. Aynen ilk 3 metod için Random nesnesinde olduğu gibi Randomize’ ın çağırılma sayısı geçen süre miktarını artırmıştır. Metod7’ de Randomize, Metod4’ deki gibi her harften önce çağırıldığı gibi üstüne bir de her harften önce RNGCryptıServiceProvider nesnesi oluşturulmuştur. Bu da geçen sürenin daha da artmasına sebep olmuştur.
Unutmadan bir de şu değerlendirmeyi yapayım. Dikkat ettiyseniz bu 7 yoldan en başarılı olanı bile farklılık oranında 0,6 tekillik oranında ise 0,32 civarlarında kaldı. İşte bu da yazının başlarında “Evrensel Kümenin Belirlenmesi” aşamasında bahsettiğim hedeflenen üye sayısından çok daha fazla üye kodu ihtimalinin oluşturulması fikrimi destekliyor.
Sonuç
Yukarıdaki örnekte yer alan 7 farklı yolla inşallah rastgele sayı ve metin(üye kodu, şifre vb) üretimi hakkında geniş bir bilgiye sahip olmuş olduk. Burada gösterilmeyen daha birçok metod olsa bile olayın özünü anladığımız için rastgele işler elimizden kaçmaz artık diye umuyorum.
Bu makalede bahsedilen örneği, ekteki uygulamadan daha detaylı inceleyebilirsiniz.
RandomCodeGenerator.zip (79,56 kb)