2014-07-01 41 views
0

我已經用我自己的實現(下文)回答了,在那裏我會感謝如果您可以檢查數學和邏輯,但是我意識到還有其他可能性。在PHP中爲URL生成隨機字符


我試圖生成32個隨機字符用於註冊URL。

新帳戶部分由員工創建(設置名稱/電子郵件),並將純文本電子郵件發送給新用戶,以便他們確認其電子郵件地址並設置其密碼。

爲了保留[A-Za-z0-9]個字符,我相信這會創建一個基本的62系統,只需要6位以下的內容來存儲......這只是超過190位的熵?或者190.53428193238?因爲這是一個安全功能,我不相信uniqid()單獨是一個好主意,因爲這是基於當前microtime。

我不相信使用加密或哈希的用戶ID或電子郵件地址是一個很好的解決方案(碰撞,低熵,大概是由一個單一的關鍵保護)。

+0

如果你不喜歡「+ 「和」/「,你可以用」 - 「和」_「來代替它們 - 這就是Base64的另一種風格,叫做」base64url「。有關更多信息,請參見[Wikipedia](http://en.wikipedia.org/wiki/Base64)。 –

+0

@ Anton-Samsonov,好點,儘管我仍然可以放下下劃線字符(或者使用句號),因爲我有幾個用戶將其視爲空間的實例(推測鏈接也使用了下劃線隱藏該字符的字體樣式)...但原則上,熵是190(ish)比特是否正確? (不是真的很重要,更重要的是檢查數學部分)。 –

回答

1

這工作,與PHP 7.0 random_bytes()函數:

<?php 

function random_key($length, $safe = false) { 

    if ($safe !== false) { 
     $bad_words = array_map('trim', file('/path/to/bad-words.txt', FILE_IGNORE_NEW_LINES)); 
    } else { 
     $bad_words = NULL; 
    } 

    $j = 0; 

    do { 

     $bytes = (ceil($length/4) * 3); // Must be divisible by 3, otherwise base64 encoding introduces padding characters, and the last character biases towards "0 4 8 A E I M Q U Y c g k o s w". 
     $bytes = ($bytes * 2); // Get even more, because some characters will be dropped. 

     $key = random_bytes($bytes); 
     $key = base64_encode($key); 
     $key = str_replace(array('0', 'O', 'I', 'l', '/', '+'), '', $key); // Make URL safe (base58), and drop similar looking characters (no substitutions, as we don't want to bias certain characters) 
     $key = substr($key, 0, $length); 

     if (preg_match('/[^a-zA-Z0-9]/', $key)) { 
      exit_with_error('Invalid characters detected in key "' . $key . '"'); 
     } 

     $valid = (strlen($key) == $length); 

     if ($bad_words) { 
      foreach ($bad_words as $bad_word) { 
       if (stripos($key, $bad_word) !== false) { 
        $valid = false; 
        break; 
       } 
      } 
     } 

     if ($valid) { 
      return $key; 
     } 

    } while ($j++ < 10); 

    exit_with_error('Cannot generate a safe key after 10 attempts.'); 

} 

?> 

這段代碼顯示了base64_encode()功能如何偏置到某些字符:

<?php 

$characters = []; 

for ($k = 0; $k < 500000; $k++) { 

    $key = base64_encode(random_bytes(32)); // 32 bytes results in "=" padding; try changing to 30 to fix. 

    foreach (str_split($key) as $c) { 
     if (!isset($characters[$c])) { 
      $characters[$c] = 0; 
     } 
     $characters[$c]++; 
    } 

} 

$characters = array_filter($characters, function($value) { 
     return ($value > 343750); // ((((33/3)*4)*500000)/64) = 343750, everything else is about ~327000 
    }); 

ksort($characters, SORT_STRING); 

print_r($characters); 

?> 
+0

不要擔心從現有解決方案中發明自己的PRNG,因爲機會很高,與普通的'mcrypt_create_iv'或'openssl_random_pseudo_bytes'相比,您可能會收到較差的結果。此外,許多密碼級PRNG已經採用強哈希來提高隨機性;你確定你沒有通過低級散列函數來輸出結果嗎? –

+0

我必須承認,我從史蒂夫吉布森和他的SQRL實現中獲得了靈感......他也認爲單一來源可能不好,但是sha256是一個很好的哈希算法...但這可能是錯誤的......並且這些隨機數生成器的二進制輸出需要轉換爲URL安全(這是此問題的第二部分)。 –

+0

只需要補充一點,openssl_random_pseudo_bytes()的第二個參數會告訴你它是否是一個很好的源,舊的系統可能是false,而mcrypt_create_iv()也可能返回不是那麼隨機的數據。 Windows 5.3之前的Windows系統。 –