PHP Conference Japan 2024

sha1

(PHP 4 >= 4.3.0, PHP 5, PHP 7, PHP 8)

sha1計算字串的 sha1 雜湊值

警告

由於此雜湊演算法的快速特性,不建議使用此函式來保護密碼。 有關詳細資訊和最佳實務,請參閱密碼雜湊常見問題

描述

sha1(字串 $string, 布林值 $binary = false): 字串

使用» 美國安全雜湊演算法 1計算 string 的 sha1 雜湊值。

參數

string

輸入字串。

binary

如果選用的 binary 設定為 true,則 sha1 摘要會改為以長度為 20 的原始二進制格式傳回,否則傳回值為 40 個字元的十六進制數字。

傳回值

以字串形式傳回 sha1 雜湊值。

範例

範例 1 sha1() 範例

<?php
$str
= 'apple';

if (
sha1($str) === 'd0be2dc421be4fcd0172e5afceea3970e2f3d940') {
echo
"您想要綠色或紅色的蘋果嗎?";
}
?>

參見

新增註解

使用者貢獻的註解 31 則註解

124
nathan
16 年前
下面關於雙重雜湊密碼的建議並不是個好主意。您最好在雜湊之前將可變的鹽值新增到密碼中(例如使用者名稱或其他每個帳戶都不同的欄位)。

雙重雜湊比常規雜湊 *更* 不安全。您實際的做法是取得一些輸入 $passwd,將其轉換為僅包含字元 [0-9][A-F] 的恰好 32 個字元的字串,然後雜湊 *該字串*。您只是 *大幅* 提高了雜湊衝突的機率(即,我猜出一個短語的機率,該短語會雜湊為與您的密碼相同的值)。

sha1(md5($pass)) 更沒道理,因為您輸入了 128 位元的資訊來產生 256 位元的雜湊值,因此 50% 的結果資料是多餘的。您根本沒有提高安全性。
7
Helpful Harry
19 年前
看看這些隨機化的 sha1 密碼儲存函式,它們會輸出一個 50 個字元的字串,前 40 個字元是以最後 10 個字元為基礎的 sha1 輸出 - 這些字元是隨機種子

要編碼密碼,請使用密碼執行 pw_encode,它每次都會傳回不同的偽隨機字串 - 儲存此值。

要檢查密碼,請使用密碼嘗試和儲存的值執行 pw_check,如果相符則傳回 true,否則傳回 false

這些函式消除了在密碼清單上執行字典比對的麻煩問題

<?php

function pw_encode($password)
{
for (
$i = 1; $i <= 10; $i++)
$seed .= substr('0123456789abcdef', rand(0,15), 1);
return
sha1($seed.$password.$seed).$seed;
}

function
pw_check($password, $stored_value)
{
if (
strlen($stored_value) != 50)
return
FALSE;
$stored_seed = substr($stored_value,40,10);
if (
sha1($stored_seed.$password.$stored_seed).$stored_seed == $stored_value)
return
TRUE;
else
return
FALSE;
}

?>
4
bobm at hp dot com
21 年前
若要在 PHP5 之前達成原始二進制格式,您可以執行此操作...

$raw = pack("H*", sha1($str));

問候,

Bob Mader
4
marcin at marcinwolny dot net
11 年前
請記住,MD5 的安全性低於 SHA1。
較舊的 CPU 可以比 SHA1 快兩倍以上計算 MD5。GPU 在平行計算中可以比 SHA1 快 3 倍以上處理 MD5!

兩個 Radeon 79xx 系列 GPU 可以在大約 6 秒內計算出 6 個字元小寫 MD5 密碼的彩虹表!

來源:http://www.codinghorror.com/blog/2012/04/speed-hashing.html
4
Andre D
16 年前
這是我之前發佈的 getDigestNotation() 函式的更好版本。(第一個版本在引數檢查中存在錯誤。)

<?php
function getDigestNotation($rawDigest, $bitsPerCharacter, $chars = NULL)
{
if (
$chars === NULL || strlen($chars) < 2) {
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,';
}

if (
$bitsPerCharacter < 1) {
// $bitsPerCharacter 必須至少為 1
$bitsPerCharacter = 1;

} elseif (
strlen($chars) < pow(2, $bitsPerCharacter)) {
// $chars 的字元長度對於 $bitsPerCharacter 而言太小
// 將 $bitsPerCharacter 設定為 $chars 長度所允許的最大值
$bitsPerCharacter = 1;
$minCharLength = 2;

while (
strlen($chars) >= ($minCharLength *= 2)) {
$bitsPerCharacter++;
}

unset(
$minCharLength);
}

$bytes = unpack('C*', $rawDigest);
$byteCount = count($bytes);

$out = '';
$byte = array_shift($bytes);
$bitsRead = 0;

for (
$i = 0; $i < $byteCount * 8 / $bitsPerCharacter; $i++) {

if (
$bitsRead + $bitsPerCharacter > 8) {
// 此位元組中剩餘的位元不足以用於目前的字元
// 取得剩餘位元並取得下一個位元組
$oldBits = $byte - ($byte >> 8 - $bitsRead << 8 - $bitsRead);

if (
count($bytes) == 0) {
// 最後的位元;比對最後一個字元並結束迴圈
$out .= $chars[$oldBits];
break;
}

$oldBitCount = 8 - $bitsRead;
$byte = array_shift($bytes);
$bitsRead = 0;

} else {
$oldBitCount = 0;
}

// 從這個位元組中只讀取需要的位元
$bits = $byte >> 8 - ($bitsRead + ($bitsPerCharacter - $oldBitCount));
$bits = $bits - ($bits >> $bitsPerCharacter - $oldBitCount << $bitsPerCharacter - $oldBitCount);
$bitsRead += $bitsPerCharacter - $oldBitCount;

if (
$oldBitCount > 0) {
// 位元來自不同的位元組,將 $oldBits 加入 $bits
$bits = ($oldBits << $bitsPerCharacter - $oldBitCount) | $bits;
}

$out .= $chars[$bits];
}

return
$out;
}
?>
2
novum123 at ribbonbazaar dot com
18 年前
就字典攻擊而言,我想出了以下函數

<?php
function twistSTR($array){
$twisted="";
$array_strlen=array();

foreach (
$array as $element){
$array_strlen[]=strlen($element);
}

for (
$i=0; $i<max($array_strlen); $i++){
foreach (
$array as $element){
if (
$i<strlen($element)){
$twisted=$twisted.$element{$i};
}
}
}

return
$twisted;
}
?>

twistSTR 函數基本上接受一個字串陣列輸入,並在所有其他字串中交替每個字串的每個字元。例如

<?php
echo twistSTR(array("this","and","that"));//輸出:tathnhidast
?>

它可以以下列方式應用

<?php
if ($un===$_POST["username"] && $pwd===sha1(twistSTR(array($salt,$_POST["password"])))){
?>

反向工程實際輸出並不是非常困難,但話說回來,這不是重點。重點是當密碼被輸入到其中一個資料庫時,它們會輸入例如「thisandthat」,而不是「tathnhidast」。
3
ranko84 at gmail dot com
15 年前
小更新...,更像是對晦澀函數的修正,取代
<?php
if ($keepLength != NULL)
{
if (
$hSLength != 0)
{
$hPassHash = substr($hPassHash, $hLPosition, -$hRPosition);
}
}
?>

改為

<?php
if ($keepLength != NULL)
{
if (
$hSLength != 0)
{
if (
$hRPosition == 0)
{
$hPassHash = substr($hPassHash, $hLPosition);
}
else
{
$hPassHash = substr($hPassHash, $hLPosition, -$hRPosition);
}
}
}
?>

我收到了一些解釋如何使用的請求,所以這可能有點長。

問題
1. 在大多數使用雜湊和鹽的解決方案中,您必須在資料庫中額外增加一行,說明該雜湊資料的鹽(最好是隨機的)。如果攻擊者設法取得您的資料庫,他們將取得雜湊資料和鹽,這些資料會與一般資料一起使用以使其模糊不清,然後破解該雜湊資料將與您沒有為其新增任何鹽的情況相同。
2. 我偶然發現一些函數會雜湊資料,然後將鹽輸入到雜湊中的隨機位置並儲存在資料庫中,但是它們仍然必須寫下用於擾亂鹽的隨機參數,以便在驗證資料時可以重複使用它。取得簡單的資料庫備份在這裡沒有太大幫助,但是如果他們設法取得模糊函數,他們可以輕鬆地看到什麼是鹽,什麼是雜湊。

解決方案
1. 當您可以在雜湊中輸入鹽時,為什麼要使用額外的行來儲存鹽。我不確定攻擊者如何判斷他們面對的雜湊類型,但我認為它與雜湊長度有關。在這種情況下,為什麼要讓攻擊者更容易工作,並在資料庫中儲存 data_hash+salt,他們可以僅根據它的長度就假設其中有鹽。
$keepLength 背後的理由。如果設定為 1,雜湊資料加上鹽的 strlen 將等於雜湊資料的 strlen,這會讓攻擊者認為沒有鹽。
如果將 $keepLength 設為 NULL,則最終結果的 strlen 將為 strlen(used_hash_algorithm)+$hSLength。
$minhPass 是為了保留足夠的空間給必須雜湊的字串,因此使用此函數的人不會因為將鹽長度 ($hSLength) 設定得太高而意外刪除它,例如...如果您將其設定為 30000,它將保持正常運作。

2. 如果您仔細思考,在建立輸入時,常數但可變的值將是輸入的相同資料。
如果我們嘗試雜湊密碼,且使用者 A 的密碼為「notme」,密碼的 strlen 等於 5,如果我們使用該函數的預設參數,且 $keepLength 設定為 1,則流程將為
產生隨機鹽值,將其雜湊,將雜湊後鹽值的前 5 個字元加在純文字密碼的開頭,將雜湊後鹽值的最後 5 個字元加在純文字密碼的結尾,然後再雜湊。將雜湊後密碼的前 5 個字元替換為雜湊後鹽值的前 5 個字元,對雜湊後密碼的最後 5 個字元也做同樣處理,然後返回雜湊後的密碼。
如果字串長度超過 10 個字元,函數會使用簡單的數學運算將其縮減為小於 10 的數字,嗯... 小於 $hSLength 中指定的數字。
好處是每次使用者輸入正確的密碼,其長度都相同,所以不需要將長度寫在任何地方。

所以最終達成的目的是什麼?
1. 攻擊者可能不知道雜湊有加鹽,而且你的資料庫中也沒有額外的一列說明「此雜湊的鹽值是這個」。
2. 即使他發現有加鹽,他也不知道哪個是雜湊後的密碼,哪個是鹽值。
3. 如果他設法存取了這個隱晦的函數,唯一可能幫到他的是 $hSLength 的值。如果 $hSLength 設定為 10,他必須破解 10 種雜湊字串的變體,因為他不知道他嘗試破解的使用者密碼有多長。
例如,第一種變體是沒有最後 10 個字元的雜湊密碼,第二種變體是沒有第一個字元和最後 9 個字元的雜湊密碼...
4. 即使他有足夠的能力破解所有 10 種變體,他可能得到的字串長度不一定與原始使用者的密碼長度完全相同,在這種情況下,攻擊者再次失敗。
2
mark at dot BANSPAM dot pronexus dot nl
20 年前
正在尋找一個簡單的函數來實作 HMAC-SHA1,但又不想使用整個 PEAR Message 函式庫嗎?

// 根據 RFC2104 計算 HMAC-SHA1
// http://www.ietf.org/rfc/rfc2104.txt
function hmacsha1($key,$data) {
$blocksize=64;
$hashfunc='sha1';
if (strlen($key)>$blocksize)
$key=pack('H*', $hashfunc($key));
$key=str_pad($key,$blocksize,chr(0x00));
$ipad=str_repeat(chr(0x36),$blocksize);
$opad=str_repeat(chr(0x5c),$blocksize);
$hmac = pack(
'H*',$hashfunc(
($key^$opad).pack(
'H*',$hashfunc(
($key^$ipad).$data
)
)
)
);
return bin2hex($hmac);
}

它對於客戶端驗證非常有用。另請參閱 http://cookies.lcs.mit.edu/pubs/webauth:tr.pdf
您可以選擇將 $hashfunc 更改為 'md5' 來將其轉換為 HMAC-MD5 函數 ;-)
如果您想要原始或 base64 輸出,而不是十六進位,只需更改最後的 return 行。

乾杯,
Mark

p.s. "$hmac =" 行以前是一行,但我必須將其切開才能在此處顯示;)
2
Dan
19 年前
我注意到網站現在開始要求密碼必須達到一定長度,並且必須包含至少一個非字母數字字元。這本身就使得字典攻擊變得有些無用。我的網站也要求這樣。它使用 md5,並將一個網站代碼附加到 md5 中。包含該網站金鑰的 include 檔案位於公開資料夾之外。我希望我已盡力阻止壞人入侵。
4
Gregory Boshoff
18 年前
請注意,sha1 演算法已經被破解,政府機構不再使用它。

從 PHP 5.1.2 開始,可以使用一組新的雜湊函數。

https://php.dev.org.tw/manual/en/function.hash.php

新的 hash() 函數支援一系列新的雜湊方法。

echo hash('sha256', 'The quick brown fox jumped over the lazy dog.');

建議開發人員開始使用更強大的 sha-2 雜湊方法(例如 sha256、sha384、sha512 或更好的方法)來為他們的應用程式進行未來的防護。

從 PHP 5.1.2 開始,hash_algos() 會返回一個系統特定或已註冊的 PHP 可用雜湊演算法方法陣列。

print_r(hash_algos());
1
Anonymous
15 年前
另一種解決方案是在雜湊中直接包含鹽值的加鹽雜湊,同時保持結果的長度不變。如果要產生雜湊,請在不使用第二個參數的情況下呼叫此函數。如果要根據雜湊檢查密碼,請使用雜湊作為第二個參數。在這種情況下,如果成功,函數會返回雜湊本身,如果失敗,則返回布林值 false。您也可以將雜湊演算法指定為第三個參數(否則將使用 SHA-1)。

<?php
function __hash($password, $obscured = NULL, $algorithm = "sha1")
{
// 是否使用使用者指定的演算法
$mode = in_array($algorithm, hash_algos());
// 產生隨機鹽值
$salt = uniqid(mt_rand(), true);
// 將其雜湊
$salt = $mode ? hash($algorithm, $salt) : sha1($salt);
// 取得長度
$slen = strlen($salt);
// 計算我們要使用的實際鹽值長度
// 雜湊的 1/8 到 1/4,密碼越短,產生的鹽值越長
$slen = max($slen >> 3, ($slen >> 2) - strlen($password));
// 如果我們要根據雜湊檢查密碼,則從雜湊中擷取實際的鹽值,否則只需將我們已有的鹽值裁剪為適當的大小
$salt = $obscured ? __harvest($obscured, $slen, $password) : substr($salt, 0, $slen);
// 雜湊密碼 - 這可能是不必要的
$hash = $mode ? hash($algorithm, $password) : sha1($password);
// 將鹽值放入其中
$hash = __scramble($hash, $salt, $password);
// 然後再次雜湊
$hash = $mode ? hash($algorithm, $hash) : sha1($hash);
// 裁剪結果,以便我們可以新增鹽值並保持相同的長度
$hash = substr($hash, $slen);
// ... 這樣做
$hash = __scramble($hash, $salt, $password);
// 然後返回結果
return $obscured && $obscured !== $hash ? false : $hash;
}
?>

它使用一個隨機的、可變長度的鹽值,取決於密碼的長度。函數 __scramble() 和 __harvest() 分別用於將鹽值放入雜湊中或將其取出。您可以編寫自己的函數,當然,結果的強度很大程度上取決於它們。它們可以相對簡單,但仍然相當安全。

<?php
function __scramble($hash, $salt, $password)
{
return
substr($hash, 0, strlen($password)) . $salt . substr($hash, strlen($password));
}

function
__harvest($obscured, $slen, $password)
{
return
substr($obscured, min(strlen($password), strlen($obscured) - $slen), $slen);
}
?>

或者它們可以非常複雜(我最喜歡的那種)

<?php
function __scramble($hash, $salt, $password)
{
$k = strlen($password); $j = $k = $k > 0 ? $k : 1; $p = 0; $index = array(); $out = ""; $m = 0;
for (
$i = 0; $i < strlen($salt); $i++)
{
$c = substr($password, $p, 1);
$j = pow($j + ($c !== false ? ord($c) : 0), 2) % (strlen($hash) + strlen($salt));
while (
array_key_exists($j, $index))
$j = ++$j % (strlen($hash) + strlen($salt));
$index[$j] = $i;
$p = ++$p % $k;
}
for (
$i = 0; $i < strlen($hash) + strlen($salt); $i++)
$out .= array_key_exists($i, $index) ? $salt[$index[$i]] : $hash[$m++];
return
$out;
}

function
__harvest($obscured, $slen, $password)
{
$k = strlen($password); $j = $k = $k > 0 ? $k : 1; $p = 0; $index = array(); $out = "";
for (
$i = 0; $i < $slen; $i++)
{
$c = substr($password, $p, 1);
$j = pow($j + ($c !== false ? ord($c) : 0), 2) % strlen($obscured);
while (
in_array($j, $index))
$j = ++$j % strlen($obscured);
$index[$i] = $j;
$p = ++$p % $k;
}
for (
$i = 0; $i < $slen; $i++)
$out .= $obscured[$index[$i]];
return
$out;
}
?>
1
sinatosk at gmail dot com
20 年前
這是一個 SHA1 函式,可以完全獨立運作。適用於使用 PHP 4.3.0 以下版本的用戶。它的運作方式與 PHP5 相同(能夠回傳原始輸出)。

<?php

/*
** Date modified: 1st October 2004 20:09 GMT
*
** PHP implementation of the Secure Hash Algorithm ( SHA-1 )
*
** This code is available under the GNU Lesser General Public License:
** https://gnu.dev.org.tw/licenses/lgpl.txt
*
** Based on the PHP implementation by Marcus Campbell
** http://www.tecknik.net/sha-1/
*
** This is a slightly modified version by me Jerome Clarke ( sinatosk@gmail.com )
** because I feel more comfortable with this
*/

function sha1_str2blks_SHA1($str)
{
$strlen_str = strlen($str);

$nblk = (($strlen_str + 8) >> 6) + 1;

for (
$i=0; $i < $nblk * 16; $i++) $blks[$i] = 0;

for (
$i=0; $i < $strlen_str; $i++)
{
$blks[$i >> 2] |= ord(substr($str, $i, 1)) << (24 - ($i % 4) * 8);
}

$blks[$i >> 2] |= 0x80 << (24 - ($i % 4) * 8);
$blks[$nblk * 16 - 1] = $strlen_str * 8;

return
$blks;
}

function
sha1_safe_add($x, $y)
{
$lsw = ($x & 0xFFFF) + ($y & 0xFFFF);
$msw = ($x >> 16) + ($y >> 16) + ($lsw >> 16);

return (
$msw << 16) | ($lsw & 0xFFFF);
}

function
sha1_rol($num, $cnt)
{
return (
$num << $cnt) | sha1_zeroFill($num, 32 - $cnt);
}

function
sha1_zeroFill($a, $b)
{
$bin = decbin($a);

$strlen_bin = strlen($bin);

$bin = $strlen_bin < $b ? 0 : substr($bin, 0, $strlen_bin - $b);

for (
$i=0; $i < $b; $i++) $bin = '0'.$bin;

return
bindec($bin);
}

function
sha1_ft($t, $b, $c, $d)
{
if (
$t < 20) return ($b & $c) | ((~$b) & $d);
if (
$t < 40) return $b ^ $c ^ $d;
if (
$t < 60) return ($b & $c) | ($b & $d) | ($c & $d);

return
$b ^ $c ^ $d;
}

function
sha1_kt($t)
{
if (
$t < 20) return 1518500249;
if (
$t < 40) return 1859775393;
if (
$t < 60) return -1894007588;

return -
899497514;
}

function
sha1($str, $raw_output=FALSE)
{
if (
$raw_output === TRUE ) return pack('H*', sha1($str, FALSE));

$x = sha1_str2blks_SHA1($str);
$a = 1732584193;
$b = -271733879;
$c = -1732584194;
$d = 271733878;
$e = -1009589776;

$x_count = count($x);

for (
$i = 0; $i < $x_count; $i += 16)
{
$olda = $a;
$oldb = $b;
$oldc = $c;
$oldd = $d;
$olde = $e;

for (
$j = 0; $j < 80; $j++)
{
$w[$j] = ($j < 16) ? $x[$i + $j] : sha1_rol($w[$j - 3] ^ $w[$j - 8] ^ $w[$j - 14] ^ $w[$j - 16], 1);

$t = sha1_safe_add(sha1_safe_add(sha1_rol($a, 5), sha1_ft($j, $b, $c, $d)), sha1_safe_add(sha1_safe_add($e, $w[$j]), sha1_kt($j)));
$e = $d;
$d = $c;
$c = sha1_rol($b, 30);
$b = $a;
$a = $t;
}

$a = sha1_safe_add($a, $olda);
$b = sha1_safe_add($b, $oldb);
$c = sha1_safe_add($c, $oldc);
$d = sha1_safe_add($d, $oldd);
$e = sha1_safe_add($e, $olde);
}

return
sprintf('%08x%08x%08x%08x%08x', $a, $b, $c, $d, $e);
}

?>
1
rsemirag at yahoo dot com
20 年前
如果您正在努力為 LDAP (PHP < 5.0) 產生 SHA 編碼的密碼,您最終需要的是這個:

$userpassword = base64_encode(pack("H*", sha1($pass)));

我是在 OpenLDAP 常見問題中找到這個的(非常感謝 Google 和 Ace),雖然我使用的是 iPlanet LDAP 伺服器。

Ray Semiraglio
0
hkmaly
7 年前
注意:在您產生使用 sha1 和密碼來防止他人竄改訊息的想法之前,請閱讀維基百科上的「長度擴展攻擊」和「雜湊訊息驗證碼」頁面。簡而言之,單純的建構方式可能非常不安全。如果可用,請使用 hash_hmac,或正確地重新實作 HMAC,不要使用捷徑,就像 mark 在 dot BANSPAM dot pronexus dot nl 的評論中已顯示的那樣。
0
php at REMOVEMEkennel17 dot co dot uk
19 年前
應該注意的是,sha1("") 不會回傳空字串。這表示如果您正在執行一個不需要使用者設定密碼的系統,則以下程式碼將無法如預期般運作

<?php
if ($StoredPassword == sha1($NewPassword))
// 密碼正確
?>

如果 $StoredPassword 和 $NewPassword 都為空白,則應將密碼視為正確,但由於 sha1("") != "",它將被視為錯誤。若要獲得正確的行為,您需要使用

<?php
if (($StoredPassword == "" && $NewPassword == "") || ($StoredPassword == sha1($NewPassword)))
// 密碼正確
?>

(注意:我使用自訂的 IsBlank() 函式而不是與空字串比較,因此 NULL 值也會被匹配。)

作為參考,以下是一些透過 sha1() 處理的特殊值。請注意,sha1("") == sha1(NULL) == sha1(false),並且 sha1(0) != sha1(false)

"" -> da39a3ee5e6b4b0d3255bfef95601890afd80709
NULL -> da39a3ee5e6b4b0d3255bfef95601890afd80709
0 -> b6589fc6ab0dc82cf12099d1c2d40ab994e8410c
1 -> 356a192b7913b04c54574d18c28d46e6395428ab
false -> da39a3ee5e6b4b0d3255bfef95601890afd80709
true -> 356a192b7913b04c54574d18c28d46e6395428ab
0
WTM
19 年前
實際上,Helpful Harry 的文章除了最簡單的破解嘗試之外,不會提高您的安全性。由於隨機種子附加在密碼雜湊的末尾,如果您竊取了雜湊密碼,就會竊取種子。

這表示您可以編寫一個簡單的 PHP 程式來從迴圈中呼叫 Harry 包含的 pw_check 函式,並向其輸入字典單字或隨機字元。

當然,如果您修改程式以更複雜的方式使用種子,「他們」就必須知道新函式的運作方式。但話又說回來,如果有人可以竊取您的密碼資料庫,他們也可能竊取您的網站程式碼(或猜到它)。
-1
mgcummings at yahoo dot com
13 年前
我想我可能會為其他試圖找出如何僅使用 PHP 生成類似 MySQL5 PASSWORD() 雜湊的人節省一些時間。

$hash = '*' . sha1(sha1($pass), TRUE));
-1
erling dot westenvik at gmail dot com
18 年前
關於 REMOVEMEkennel17 dot co dot uk 下方關於 php 的註記

「為了獲得正確的行為」這句話,或許改為「為了獲得想要的(但不建議的)行為」會更好。

務必遵守函數預期的資料類型:sha1 預期輸入為字串,並在結束時返回字串。NULL、TRUE 和 FALSE 都不是字串資料類型。字串 "" 和 "any" 一樣都是字串。如果遵循「sha1("") 應該返回 ""」的「邏輯」,那麼 sha1("a") 應該返回什麼? "b"?"c"?

一個允許空白密碼的驗證系統根本不是驗證系統。你所描述的只是一種告知應用程式你想要在特定上下文中查看資料的方式,例如按使用者名稱排序等等。請為此目的建立其他工具,並將驗證系統留給它應該做的事情:授予使用者訪問受限資料的權限,並阻止其他使用者看到相同的資料。

不要以明文儲存密碼,而是要加鹽並加密它們。這樣做的話,即使使用空白「密碼」,<?php $sStoredPwd === sha1($sStoredSalt . $_POST["sTypedPwd"]); ?> 也是完全合理的。除了使用者本人之外,即使是程式設計師,也不應該知道密碼或能夠猜到密碼。如果使用者忘記密碼,必須產生新的密碼。

問候,
Erling
-1
Andr D
16 年前
有時你會需要可讀的表示法(例如十六進位)和原始二進位格式的摘要。有時你又會需要十六進位以外的表示法。

下面的 getDigestNotation() 函數會接收一個二進位字串,並以 2、4、8、16、32 或 64 進位表示法返回。它可以與 sha1()、md5()、hash() 或任何可以輸出原始二進位字串的函數一起使用。

它的運作方式類似於 php.ini 設定選項 session.hash_bits_per_character。

你可以指定每個位置要使用的字元,或是使用預設值,預設值會符合 session.hash_bits_per_character (0-9, a-z, A-Z, "-", ",")。每個字元使用的實用位元範圍 ($bitsPerCharacter) 是 1 到 6;你可以使用更多,但你必須提供你自己的基礎字元字串 ($chars),其長度至少為 pow(2, $bitsPerCharacter) 個字元。因此,即使每個字元使用 7 個位元,你也需要為 $chars 指定一個長度為 128 個字元的值,這超出了可列印 ASCII 字元的數量。

輸出的基數與 $bitsPerCharacter 的值有關,如下所示
1:二進位 (base-2)
2:四進位 (base-4)
3:八進位 (base-8)
4:十六進位 (base-16)
5:三十二進位 (base-32)
6:六十四進位 (base-64)

<?php
$raw
= sha1(uniqid(mt_rand(), TRUE), TRUE);

echo
getDigestNotation($raw, 6);

function
getDigestNotation($rawDigest, $bitsPerCharacter, $chars = NULL)
{
if (
$chars === NULL || strlen($chars) < 2) {
$chars '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,';
}

if (
$bitsPerCharacter < 1) {
// $bitsPerCharacter 必須至少為 1
$bitsPerCharacter = 1;

} elseif (
strlen($chars) < pow(2, $bitsPerCharacter)) {
// $chars 的字元長度對於 $bitsPerCharacter 而言太小
// 將 $bitsPerCharacter 設定為 $chars 長度允許的最大值
$bitsPerCharacter = 1;

do {
$bitsPerCharacter++;
} while (
strlen($chars) > pow(2, $bitsPerCharacter));
}

$bytes = unpack('C*', $rawDigest);
$byteCount = count($bytes);

$out = '';
$byte = array_shift($bytes);
$bitsRead = 0;

for (
$i = 0; $i < $byteCount * 8 / $bitsPerCharacter; $i++) {

if (
$bitsRead + $bitsPerCharacter > 8) {
// 這個位元組中沒有足夠的位元來表示當前字元
// 取得剩餘的位元並取得下一個位元組
$oldBits = $byte - ($byte >> 8 - $bitsRead << 8 - $bitsRead);

if (
count($bytes) == 0) {
// 最後的位元;比對最後的字元並退出迴圈
$out .= $chars[$oldBits];
break;
}

$oldBitCount = 8 - $bitsRead;
$byte = array_shift($bytes);
$bitsRead = 0;

} else {
$oldBitCount = 0;
}

// 只從這個位元組中讀取需要的位元
$bits = $byte >> 8 - ($bitsRead + ($bitsPerCharacter - $oldBitCount));
$bits = $bits - ($bits >> $bitsPerCharacter - $oldBitCount << $bitsPerCharacter - $oldBitCount);
$bitsRead += $bitsPerCharacter - $oldBitCount;

if (
$oldBitCount > 0) {
// 位元來自不同的位元組,將 $oldBits 加到 $bits
$bits = ($oldBits << $bitsPerCharacter - $oldBitCount) | $bits;
}

$out .= $chars[$bits];
}

return
$out;
}
?>

最後,根據摘要的長度,最後一個字元可能剩下的位元少於 $bitsPerCharacter,因此最後一個字元會比較小。當 session.hash_bits_per_character 使用 5 或 6 時,PHP 的 session ID 產生器也會發生相同的情況。
-1
ranko84 at gmail dot com
16 年前
感謝您的回饋。我希望這樣能解決問題。
我認為我沒有完全理解這句話「在這種情況下,你會需要鹽值與使用者名稱和密碼一起儲存在資料庫中。」意思是說,您指的是先前的方法、這個方法還是這個函數。
鹽值已經與使用者名稱、密碼或任何您決定雜湊的字串一起儲存在資料庫中。這個函數只是根據使用者輸入的字串(密碼)長度來打亂它,以便攻擊者難以找出鹽值是什麼以及雜湊值是什麼,即使攻擊者懷疑有鹽值($keepLength 背後的原因,或定義 $hSLength 您可以將其設定為 24,讓攻擊者相信他面對的是 sha256,而不是 sha1)。

<?php
function obscure ($hString, $hDecode = NULL, $hSLength = 10, $keepLength = NULL, $minhPass = 10, $hMethod = sha1)
{
if (
$hDecode == NULL)
{
for (
$i = 0; $i<16; $i++)
{

$hSalt = rand(33, 255);
$hRandomSalt .= chr($hSalt);
}
$hRandomSalt = hash($hMethod, $hRandomSalt);
}
else
{
$hRandomSalt = $hDecode;
}

if (
$keepLength != NULL)
{

if (
$hSLength > (strlen($hRandomSalt) - $minhPass))
{
$hSLength = (strlen($hRandomSalt) - $minhPass);
}
}
else if (
$hSLength < 0)
{
$hSLength = 0;
}

$hLPosition = strlen($hString);

while (
$hLPosition > $hSLength)
{
$hNumber = substr($hLPosition, -1);

$hLPosition = $hLPosition * ($hNumber/10);
}

$hLPosition = (integer)$hLPosition;
$hRPosition = $hSLength - $hLPosition;

$hFSalt = substr($hRandomSalt, 0, $hLPosition);
$hLSalt = substr($hRandomSalt, -$hRPosition, $hRPosition);

$hPassHash = hash($hMethod, ($hLSalt . $hString . $hFSalt));

if (
$keepLength != NULL)
{
if (
$hSLength != 0)
{
$hPassHash = substr($hPassHash, $hLPosition, -$hRPosition);
}
}

return
$hFSalt . $hPassHash . $hLSalt;
}
?>
-1
rich dot sage at gmail dot com
14 年前
如果你使用 Dovecot 來檢索郵件,並且想要自己產生 SHA1 密碼,你需要將 raw_output 值設定為 true,然後將輸出進行 base64 編碼。

<?php
function makeDovecotPassword($input)
{
return
'{SHA}' . base64_encode(sha1($input, true));
}
?>
-4
php at wbhostmax dot de
12 年前
<?php
function DoubleSaltedHash($pw, $salt) {
return
sha1($salt.sha1($salt.sha1($pw)));
}

function
generate_salt() {
$dummy = array_merge(range('0', '9'));
mt_srand((double)microtime()*1000000);
for (
$i = 1; $i <= (count($dummy)*2); $i++)
{
$swap = mt_rand(0,count($dummy)-1);
$tmp = $dummy[$swap];
$dummy[$swap] = $dummy[0];
$dummy[0] = $tmp;
}
return
sha1(substr(implode('',$dummy),0,9));
}
$pw="geheim"
$salt=generate_salt();
echo
"hash:".DoubleSaltedHash($pw, $salt);

?>

這是我加密密碼的方式
-1
labarks
21 年前
將此程式碼附加到你的 sha1lib 檔案中,使其更具可攜性。如果你的 PHP 版本支援 sha1(),它將嘗試使用 Mhash,否則它將使用 sha1lib。如果你想顯示正在使用哪個函數,請使用 $sha1。

if ( function_exists('sha1') )
$sha1 = "sha1";

if ( !function_exists('sha1') && function_exists('mhash'))
{
function sha1($hash_source)
{
$hash = mhash(MHASH_SHA1, $hash_source);
$hex_hash = bin2hex($hash);
return $hex_hash;
}
$sha1 = "Mhash";
}
if ( !function_exists('sha1') && !function_exists('mhash'))
{
function sha1( $string, $raw_output = false )
{
$library = new Sha1Lib();

return $raw_output ? $library->str_sha1($string) : $library->hex_sha1($string);
}
$sha1 = "sha1lib";
}
-1
NoName
17 年前
關於 twistSTR - 問題在於目前相對容易針對任何給定短長度的字母數字明文產生碰撞,例如透過彩虹表。你最好使用足夠長且隨機的鹽。
-2
mVamer
15 年前
如果我正確理解 ranko84 的意思,這將是一個更簡單的函數,具有大致相同的結果。

<?php
function obscure($password, $algorythm = "sha1")
{
// 取得一些隨機鹽,或驗證鹽。
// 由 (grosbedo AT gmail DOT com) 加入
if ($salt == NULL)
{
$salt = hash($algorythm, uniqid(rand(), true));
}

// 決定雜湊的長度。
$hash_length = strlen($salt);

// 決定密碼的長度。
$password_length = strlen($password);

// 決定密碼的最大長度。這只有在使用者輸入非常長的密碼時才需要。
// 無論如何,鹽的最大長度將會是最終結果的一半。雜湊越長,
// 密碼/鹽的長度就可以越長。
$password_max_length = $hash_length / 2;

// 根據密碼長度縮短鹽。
if ($password_length >= $password_max_length)
{
$salt = substr($salt, 0, $password_max_length);
}
else
{
$salt = substr($salt, 0, $password_length);
}

// 決定鹽的長度。
$salt_length = strlen($salt);

// 決定加鹽的雜湊密碼。
$salted_password = hash($algorythm, $salt . $password);

// 如果我們將鹽加到雜湊密碼中,我們會得到一個比一般雜湊密碼更長的雜湊。
// 我們不希望這樣;這會給攻擊者提示。由於密碼和密碼長度都是已知的,
// 我們可以丟棄加鹽密碼的前幾個字元。這樣,鹽和加鹽密碼的總長度
// 就會和沒有鹽的普通雜湊密碼一樣長。
$used_chars = ($hash_length - $salt_length) * -1;
$final_result = $salt . substr($salted_password, $used_chars);

return
$final_result;
}
?>
-2
svn at datapirate dot de
19 年前
想要使用 SHA-2 演算法嗎?試試這個

http://www.adg.us/computers/sha.html 下載 Tar-Ball
編譯 (可能會出現一些警告) 並測試它

cc -O2 -DSHA2_UNROLL_TRANSFORM -Wall -o sha2 sha2prog.c sha2.c
./sha2test.pl

將其複製到 /usr/local/bin/ (別忘了檢查權限)

這裡有兩個函式可以搭配使用

function sha2($bits, $string){
$sha2bin="/usr/local/bin/sha2";
$echocmd="echo";
if(!in_array($bits, array(256, 384, 512)))return(false);
$r=exec($echocmd." ".escapeshellarg($string)."|".$sha2bin." -q -".$bits, $sha2);
return($sha2[0]);
}

function sha2_file($bits, $filename){
$sha2bin="/usr/local/bin/sha2";
if(!in_array($bits, array(256, 384, 512)))return(false);
if(!file_exists($filename)||!is_readable($filename))return(false);
$r=exec($sha2bin." -q -".$bits." ".escapeshellarg($filename), $sha2);
return($sha2[0]);
}

並像下面這樣使用它

<?php
$str
= 'apple';
if (
sha2(256, $str) === '303980bcb9e9e6cdec515230791af8b0ab1aaa244b58a8d99152673aa22197d0') {
echo
"你想要綠蘋果還是紅蘋果?";
exit;
}
?>
-4
paul
15 年前
我相信這提供了最佳的保護,使用隨機鹽,必須儲存起來以便日後驗證。

如果沒有提供鹽(可以透過將輸出減半並取前半部分來檢索),則它會產生一個隨機鹽,將其雜湊,將其放置在相對於密碼長度的位置(在雜湊類型(sha1?md5?)的 0 和長度之間)在雜湊密碼內,然後雜湊完整字串。

這會產生一個使用鹽的密碼雜湊,該鹽會根據密碼長度動態放置。然後將使用的鹽附加到完成的雜湊前端,以便日後檢索以進行驗證。

鑑於使用者會選擇介於 5 到 15 個字元之間的典型密碼,這會讓他們額外有 10 倍的字典攻擊次數,因為它可能會被放置在任何位置,因為這也是隨機產生的鹽,這意味著每次建立密碼時,至少有 10 次字典攻擊(可能高達 40 次),試圖找出您的 sha1 加密密碼。

如果您每個月都更改密碼,即使有人透過本機漏洞查看您的檔案,找出您密碼所需的時間也會遠遠超過您更改密碼的頻率。

沒有什麼是安全的,但這應該會讓他們花更長的時間來找出,然後您再更改它。至少以今天的技術來說是這樣。

保羅

<?php
function createHash($inText, $saltHash=NULL, $mode='sha1'){
// 雜湊文字 //
$textHash = hash($mode, $inText);
// 設定鹽將出現在雜湊中的位置 //
$saltStart = strlen($inText);
// 如果沒有給定鹽,則建立隨機鹽 //
if($saltHash == NULL) {
$saltHash = hash($mode, uniqid(rand(), true));
}
// 將鹽加入文字雜湊中的密碼長度位置,並進行雜湊 //
if($saltStart > 0 && $saltStart < strlen($saltHash)) {
$textHashStart = substr($textHash,0,$saltStart);
$textHashEnd = substr($textHash,$saltStart,strlen($saltHash));
$outHash = hash($mode, $textHashEnd.$saltHash.$textHashStart);
} elseif(
$saltStart > (strlen($saltHash)-1)) {
$outHash = hash($mode, $textHash.$saltHash);
} else {
$outHash = hash($mode, $saltHash.$textHash);
}
// 將鹽放在雜湊的最前面 //
$output = $saltHash.$outHash;
return
$output;
}
?>
-3
brian_bisaillon at rogers dot com
20 年前
用於建立 SSHA 密碼的原始碼...

public function HashPassword($password)
{
mt_srand((double)microtime()*1000000);
$salt = mhash_keygen_s2k(MHASH_SHA1, $password, substr(pack('h*', md5(mt_rand())), 0, 8), 4);
$hash = "{SSHA}".base64_encode(mhash(MHASH_SHA1, $password.$salt).$salt);
return $hash;
}

用於驗證 SSHA 密碼的原始碼...

public function ValidatePassword($password, $hash)
{
$hash = base64_decode(substr($hash, 6));
$original_hash = substr($hash, 0, 20);
$salt = substr($hash, 20);
$new_hash = mhash(MHASH_SHA1, $password . $salt);
if (strcmp($original_hash, $new_hash) == 0)
... 因為您的密碼有效,請執行某些操作 ...
else
echo '未授權:您提供的憑證已被拒絕授權。請使用有效的用戶名和密碼登錄。';
... 請務必清除您的會話資料 ...
}

注意:如果我沒弄錯的話,格式與 OpenLDAP 的 SSHA 方案相容。
-1
alex at milivojevic dot org
19 年前
關於我之前的評論,如果您想安全起見,並且只使用 ASCII 可列印種子(SSHA 種子應該沒關係),可以使用類似這樣的方法

<?php
$salt
= substr(base64_encode(pack("H*", sha1(mt_rand()))), 0, 4);
?>
-4
cneeds athome dot co dot bw
7 年前
目前為止,我發現最安全的傳送密碼方式

是先向伺服器請求並接收一次性代碼,

然後使用該一次性代碼遮罩(雜湊)回傳給伺服器的密碼。
-3
jcastromail at yahoo dot es
8 年前
嗨,大家好

關於 sha1 的複雜性,sha1 會產生一個不同的代碼,每 1,4615016373309029182036848327163e+48 (2 ^ 160 位元)。 因此,使用相同雜湊值的機率非常小。

sha1(以及 md5)的「問題」在於產生速度。 然而,速度與要加密的文字長度成正比。

然而,使用 SALT 可以使安全性提高十倍,即使是弱密碼也是如此。

粗略地說,6 個字元的密碼可以在一分鐘內被破解(如果它儲存在 md5 或 sha 中)。 然而,7 個字元的密碼需要一個小時,8 個字元的密碼需要一年,而 8 個字元以上的密碼幾乎不可能被破解。

然而,如果我們使用 SALT(順便說一句,是一個秘密 salt),那麼即使是 3 個字元的密碼也會非常安全。

sha1('SALT SECRET TEXT!!@@@aaa0000'.'123');

雙重 sha1 將確保更高的安全性

sha1(sha1('SALT SECRET TEXT'.'123',false),false)

這將需要一個 20 個字元的彩虹表,即使對於一年內運行的數千個伺服器來說,也足夠龐大且絕對安全。
To Top