私有密匙(passport_key)
由於一些關鍵參數採用了 GET 方式進行傳遞,即便兩次 header 跳轉並不會直接將鏈接顯示在外面,但我們仍然對關鍵的參數進行了加密,私有
密匙共有兩個作用:其一是供下面提到的可逆加密算法(AzDGCrypt)進行數據的加解密。其二是生成不可逆驗證字串(verify),以防止關鍵信息被
偽造。
在啟用 Discuz! Passort 後,您需要在應用程序和 Discuz! 後台配置兩處私有密匙,這兩處的內容必須完全相同,這樣應用程序和論壇之間才能
正常通信。私有密匙決定了加密算法的強度,因此密匙長度請不要小於 10 個字節,並包含字母、數字和符號,以保證系統的安全。
加密算法
Discuz! Passport 採用 Azerbaijan Development Group(AzDG)開發的可逆加密算法 AzDGCrypt 對用戶資料進行加密。如提供正確的私有密匙,
可通過本加密算法對數據進行加密及解密,因此只要保證私有密匙的保密性,即可確保數據傳遞過程中的安全。以下為 Discuz! Passport 中應用到
的可逆加密算法,為了生成可以被 Discuz! Passport 正確解密的 auth 字串,需要將如下函數放置於應用程序中,並可在登錄及註冊時調用。
passport_encrypt()是加密函數,用法為 passport_encrypt($txt, $key),其中 $txt 是待加密的字串,$key 是私有密匙。
passport_decrypt()是解密函數,用法為 passport_decrypt($txt, $key),其中 $txt 是加密後的字串,$key 是私有密匙。
/**
* Passport 加密函數
*
* @param string 等待加密的原字串
* @param string 私有密匙(用於解密和加密)
*
* @return string 原字串經過私有密匙加密後的結果
*/
function passport_encrypt($txt, $key) {
// 使用隨機數發生器產生 0~32000 的值並 MD5()
srand((double)microtime() * 1000000);
$encrypt_key = md5(rand(0, 32000));
// 變量初始化
$ctr = 0;
$tmp = '';
// for 循環,$i 為從 0 開始,到小於 $txt 字串長度的整數
for($i = 0; $i < strlen($txt); $i++) {
// 如果 $ctr = $encrypt_key 的長度,則 $ctr 清零
$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
// $tmp 字串在末尾增加兩位,其第一位內容為 $encrypt_key 的第 $ctr 位,
// 第二位內容為 $txt 的第 $i 位與 $encrypt_key 的 $ctr 位取異或。然後 $ctr = $ctr + 1
$tmp .= $encrypt_key[$ctr].($txt[$i] ^ $encrypt_key[$ctr++]);
}
// 返回結果,結果為 passport_key() 函數返回值的 base64 編碼結果
return base64_encode(passport_key($tmp, $key));
}
/**
* Passport 解密函數
*
* @param string 加密後的字串
* @param string 私有密匙(用於解密和加密)
*
* @return string 字串經過私有密匙解密後的結果
*/
function passport_decrypt($txt, $key) {
// $txt 的結果為加密後的字串經過 base64 解碼,然後與私有密匙一起,
// 經過 passport_key() 函數處理後的返回值
$txt = passport_key(base64_decode($txt), $key);
// 變量初始化
$tmp = '';
// for 循環,$i 為從 0 開始,到小於 $txt 字串長度的整數
for ($i = 0; $i < strlen($txt); $i++) {
// $tmp 字串在末尾增加一位,其內容為 $txt 的第 $i 位,
// 與 $txt 的第 $i + 1 位取異或。然後 $i = $i + 1
$tmp .= $txt[$i] ^ $txt[++$i];
}
// 返回 $tmp 的值作為結果
return $tmp;
}
/**
* Passport 密匙處理函數
*
* @param string 待加密或待解密的字串
* @param string 私有密匙(用於解密和加密)
*
* @return string 處理後的密匙
*/
function passport_key($txt, $encrypt_key) {
// 將 $encrypt_key 賦為 $encrypt_key 經 md5() 後的值
$encrypt_key = md5($encrypt_key);
// 變量初始化
$ctr = 0;
$tmp = '';
// for 循環,$i 為從 0 開始,到小於 $txt 字串長度的整數
for($i = 0; $i < strlen($txt); $i++) {
// 如果 $ctr = $encrypt_key 的長度,則 $ctr 清零
$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
// $tmp 字串在末尾增加一位,其內容為 $txt 的第 $i 位,
// 與 $encrypt_key 的第 $ctr + 1 位取異或。然後 $ctr = $ctr + 1
$tmp .= $txt[$i] ^ $encrypt_key[$ctr++];
}
// 返回 $tmp 的值作為結果
return $tmp;
}
/**
* Passport 信息(數組)編碼函數
*
* @param array 待編碼的數組
*
* @return string 數組經編碼後的字串
*/
function passport_encode($array) {
// 數組變量初始化
$arrayenc = array();
// 遍歷數組 $array,其中 $key 為當前元素的下標,$val 為其對應的值
foreach($array as $key => $val) {
// $arrayenc 數組增加一個元素,其內容為 "$key=經過 urlencode() 後的 $val 值"
$arrayenc[] = $key.'='.urlencode($val);
}
// 返回以 "&" 連接的 $arrayenc 的值(implode),例如 $arrayenc = array('aa', 'bb', 'cc', 'dd'),
// 則 implode('&', $arrayenc) 後的結果為 」aa&bb&cc&dd"
return implode('&', $arrayenc);
}
|
passport_encode()是將數組轉換合成為字串形式存儲的函數:變量名和數值之間用等號連接,如果數值包含特殊字符,使用 urlencode() 將其轉碼。
多個變量間使用 & 分割。例如原始數組內容為 array('username' => 'abc', 'email' => 'my+discuz@gmail.com'),經過 passport_encode() 編碼後
結果為 username=abc&email=my%2Bdiscuz%40gmail.com。
信息字串(auth)
應用程序在收到登錄或註冊請求,並讀取到用戶資料後,請按如下的要求將用戶資料及部分其他信息存放於一個數組之中。數組各鍵值的含義為:
- cookietime
應用程序保存該用戶登錄記錄的時間,可為非負整數,單位秒,Discuz! Passport 收到此參數後,會設置同樣的 Cookie 過期時間,這樣應用
程序和論壇可以保證同樣的登錄有效性。如不傳遞此參數,或參數數值不正確,則 Discuz! Passport 按照 0 設置 Cookie 有效期。
- time
應用程序所在服務器當前時間(9 或 10 位數字 Unix Timestamp),此參數用於 Discuz! 所在服務器當前時間進行比對,如果早於當前時間
超過若干秒(取決於 Discuz! Passport 中的「驗證字串有效期」設定),則視為本 auth 內容無效,避免此URL被人得知後可能的安全問題。
- username
用戶登錄或註冊的用戶名。Discuz! 的註冊用戶名規則為:
- 長度 1~15 個字符,不得為空
- 不得為 c:\con\con、遊客(gb2312 或 big5 內碼)、Guest
- 不得包含 (,)、(*)、(")、([TAB])、([SPACE])、([\r])、([\n])、(<)、(>)、(&)其中之一
如果應用程序提交過來的用戶名不符合上述規則,Passport 將自動去處其中的特殊字符,將過濾後的結果寫入數據庫中。
- password
用戶密碼經 MD5 不可逆加密後的值。如果此密碼使用非 MD5 加密,則應用程序和 Passport 不能正常關聯和使用。
- email
用戶 Email 地址(50 個字節以內)。
- isadmin
當前用戶是否是應用程序的最高管理員,1=是,0=否。最高管理員的權限,將同步到論壇中去,其他下級管理員的身份將不進行同步,而由最高
管理員分別在不同的系統中進行設置。
- credits
當前用戶在應用程序中的積分值,範圍 -2147483648 到 2147483647,如果 Discuz! Passport 中設置了目標積分項,則用戶登錄時,Passport
會把應用程序傳遞過來的 credits 值同步到指定的論壇的指定積分項目中
- gender
當前用戶的性別,1=男,2=女,0=未知。
- bday
當前用戶的生日,格式 yyyy-mm-dd。
- regip
當前用戶註冊時的 IP 地址。
- regdate
當前用戶註冊的時間(9 或 10 位數字 Unix Timestamp)。
- nickname
當前用戶的暱稱(30 個字節以內,如傳遞此參數,必須打開相應用戶組的暱稱權限,否則用戶在控制面板中提交個人資料時,會導致暱稱失效)。
- site
當前用戶的主頁地址(包含http://)。
- qq
當前用戶的 QQ 號碼。
- ICQ
當前用戶的 ICQ 賬號。
- msn
當前用戶的 MSN Messenger 賬號。
- yahoo
當前用戶的 Yahoo! Messanger 賬號。
以上參數中,以黑體下劃線顯示的 time、username、password、email 是必須傳遞的參數,缺少上述參數 Passport 將無法正常工作。其他的參數是可選的,如果
不傳遞某些參數,則 Passport 會進行識別,自動不更新沒有傳遞的參數所在的字段。所有數值,請提供原始值,而非經過反斜線轉義(addslashes)後
的結果。
把上述信息存放於數組中,假定為如下的形式:
$member = array
(
'cookietime' => 31536000,
'time' => 1117415922,
'username' => 'Abcd',
'password' => 'e2fc714c4727ee9395f324cd2e7f331f',
'email' => 'abcd@efgh.com',
'credits' => 123,
'regip' => '210.120.222.111',
'regdate' => '1012752000',
'msn' => 'email@hotmail.com'
);
|
將其經過如下的加密變換,即可得到 auth 的值:
$auth = passport_encrypt(passport_encode($member), $passport_key);
|
其中,passport_encode() 在前文已做了說明,用於將數組內容存放於特定的格式,$passport_key 是私有密匙。
切記:由於 $auth 中可能含有等號、加號等特殊字符,請將 $auth 經過 rawurlencode() 編碼後再在 URL 中傳遞,否則可能會產生問題。
導向字串(forward)
導向字串用於通知 Discuz! Passport 在完成自身操作後,返回到哪一個 URL 地址,例如 http://www.myforums.com/forumdisplay.php?fid=2。
如果 forward 為空,則默認導向到應用程序的首頁
切記:由於 $forward 中可能含有冒號、問號、等號等特殊字符,請將 $forward 經過 rawurlencode() 編碼後再在 URL 中傳遞,否則可能會
產生問題。
驗證字串(verify)
驗證字串用戶檢驗 auth 和 forward 兩個參數的合法性,避免非法構造參數進行破壞的可能。無論 auth 和 forward 變量是否存在,驗證字串
(verify)的值均為:
$verify = md5($action.$auth.$forward.$passport_key);
|
其中,$action 是當前執行的 Passport 操作,如 login 等等;$auth 是用戶信息加密後,並經 rawurlencode() 之前的內容。$forward 是經
rawurlencode() 前的導向字串、$passport_key 是私有密匙。如果 verify 的值不匹配,則 Passport 拒絕進行下一步操作。
|