PHP安全編程:記住登錄狀態的安全做法

永久登錄的隱患
服務器君一共花費了130.634 ms進行了5次數據庫查詢,努力地為您提供了這個頁面。
試試閱讀模式?希望聽取您的建議

永久登錄指的是在瀏覽器會話間進行持續驗證的機制。換句話說,今天已登錄的用戶明天依然是處于登錄狀態,即使在多次訪問之間的用戶會話過期的情況下也是這樣。永久登錄的存在降低了你的驗證機制的安全性,但它增加了可用性。不是在用戶每次訪問時麻煩用戶進行身份驗證,而是提供了記住登錄的選擇。

據我觀察,最常見的有缺陷的永久登錄方案是將用戶名和密碼保存在一個cookie中。這樣做的誘惑是可以理解的——不需要提示用戶輸入用戶名和密碼,你只要簡單地從cookie中讀取它們即可。驗證過程的其它部分與正常登錄完全相同,因此該方案是一個簡單的方案。

不過如果你確實是把用戶名和密碼存在cookie中的話,請立刻關閉該功能,同時閱讀本節的余下內容以找到實現更安全的方案的一些思路。你將來還需要要求所有使用該cookie的用戶修改密碼,因為他們的驗證信息已經暴露了。

永久登錄需要一個永久登錄cookie,通常叫做驗證cookie,這是由于cookie是被用來在多個會話間提供穩定數據的唯一標準機制。如果該cookie提供永久訪問,它就會造成對你的應用的安全的嚴重風險,所以你需要確定你保存在cookie中的數據只能在有限的時間段內用于身份驗證。

第一步是設計一個方法來減輕被捕獲的永久登錄cookie造成的風險。盡管cookie被捕獲是你需要避免的,但有一個深度防范流程是最好的,特別是因為這種機制即使是在一切運行正常的情況下,也會降低驗證表單的安全性。這樣,該cookie就不能基于任何提供永久登錄的信息來產生,如用戶密碼。

為避免使用用戶的密碼,可以建立一個只供一次驗證有效的標識:

<?php

$token = md5(uniqid(rand(), TRUE));

?>

你可以把它保存在用戶的會話中以把它與特定的用戶相關聯,但這并不能幫助你在多個會話間保持登錄,這是一個大前提。因此,你必須使用一個不同的方法把這個標識與特定的用戶關聯起來。

由于用戶名與密碼相比要不敏感一些,你可以把它存在cookie中,這可以幫助驗證程序確認提供的是哪個用戶的標識。可是,一個更好的方法是使用一個不易猜測與發現的第二身份標識。考慮在保存用戶名和密碼的數據表中加入三個字段:第二身份標識(identifier),永久登錄標識(token),以及一個永久登錄超時時間(timeout)。

mysql> DESCRIBE users;
+------------+------------------+------+-----+---------+-------+
| Field      | Type             | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+-------+
| username   | varchar(25)      |      | PRI |         |       |
| password   | varchar(32)      | YES  |     | NULL    |       |
| identifier | varchar(32)      | YES  | MUL | NULL    |       |
| token      | varchar(32)      | YES  |     | NULL    |       |
| timeout    | int(10) unsigned | YES  |     | NULL    |       |
+------------+------------------+------+-----+---------+-------+

通過產生并保存一個第二身份標識與永久登錄標識,你就可以建立一個不包含任何用戶驗證信息的cookie。

<?php

$salt = 'SHIFLETT';

$identifier = md5($salt . md5($username . $salt));
$token = md5(uniqid(rand(), TRUE));
$timeout = time() + 60 * 60 * 24 * 7;

setcookie('auth', "$identifier:$token", $timeout);

?>

當一個用戶使用了一個永久登錄cookie的情況下,你可以通過是否符合幾個標準來檢查:

<?php

/* mysql_connect() */
/* mysql_select_db() */

$clean = array();
$mysql = array();

$now = time();
$salt = 'SHIFLETT';

list($identifier, $token) = explode(':', $_COOKIE['auth']);

if (ctype_alnum($identifier) && ctype_alnum($token))
{
  $clean['identifier'] = $identifier;
  $clean['token'] = $token;
}
else
{
  /* ... */
}

$mysql['identifier'] = mysql_real_escape_string($clean['identifier']);

$sql = "SELECT username, token, timeout
        FROM   users
        WHERE  identifier = '{$mysql['identifier']}'";

if ($result = mysql_query($sql))
{
  if (mysql_num_rows($result))
  {
    $record = mysql_fetch_assoc($result);

    if ($clean['token'] != $record['token'])
    {
      /* Failed Login (wrong token) */
    }
    elseif ($now > $record['timeout'])
    {
      /* Failed Login (timeout) */
    }
    elseif ($clean['identifier'] !=
            md5($salt . md5($record['username'] . $salt)))
    {
      /* Failed Login (invalid identifier) */
    }
    else
    {
      /* Successful Login */
    }

  }
  else
  {
    /* Failed Login (invalid identifier) */
  }
}
else
{
  /* Error */
}

?>

你應該堅持從三個方面來限制永久登錄cookie的使用。?

  • Cookie需在一周內(或更少)過期
  • Cookie最好只能用于一次驗證(在一次成功驗證后即刪除或重新生成)
  • 在服務器端限定cookie在一周(或更少)時間內過期?

如果你想要用戶無限制的被記住,那只要是該用戶的訪問你的應用的頻度比過期時間更大的話,簡單地在每次驗證后重新生成標識并設定一個新的cookie即可。?

另一個有用的原則是在用戶執行敏感操作前需要用戶提供密碼。你只能讓永久登錄用戶訪問你的應用中不是特別敏感的功能。在執行一些敏感操作前讓用戶手工進行驗證是不可替代的步驟。?

最后,你需要確認登出系統的用戶是確實登出了,這包括刪除永久登錄cookie:

<?php

setcookie('auth', 'DELETED!', time());

?>

上例中,cookie被無用的值填充并設為立即過期。這樣,即使是由于一個用戶的時鐘不準而導致cookie保持有效的話,也能保證他有效地退出。

延伸閱讀

此文章所在專題列表如下:

  1. PHP安全編程:register_globals的安全性
  2. PHP安全編程:不要讓不相關的人看到報錯信息
  3. PHP安全編程:網站安全設計的一些原則
  4. PHP安全編程:可用性與數據跟蹤
  5. PHP安全編程:過濾用戶輸入
  6. PHP安全編程:對輸出要進行轉義
  7. PHP安全編程:表單與數據安全
  8. PHP安全編程:從URL的語義進行攻擊
  9. PHP安全編程:文件上傳攻擊的防御
  10. PHP安全編程:跨站腳本攻擊的防御
  11. PHP安全編程:跨站請求偽造CSRF的防御
  12. PHP安全編程:關于表單欺騙提交
  13. PHP安全編程:HTTP請求欺騙
  14. PHP安全編程:不要暴露數據庫訪問權限
  15. PHP安全編程:防止SQL注入
  16. PHP安全編程:cookie暴露導致session被劫持
  17. PHP安全編程:session固定獲取合法會話
  18. PHP安全編程:session劫持的防御
  19. PHP安全編程:防止源代碼的暴露
  20. PHP安全編程:留心后門URL
  21. PHP安全編程:阻止文件名被操縱
  22. PHP安全編程:文件包含的代碼注入攻擊
  23. PHP安全編程:文件目錄猜測漏洞
  24. PHP安全編程:打開遠程文件的風險
  25. PHP安全編程:shell命令注入
  26. PHP安全編程:暴力破解攻擊
  27. PHP安全編程:密碼嗅探與重播攻擊
  28. PHP安全編程:記住登錄狀態的安全做法
  29. PHP安全編程:共享主機的源碼安全
  30. PHP安全編程:更優的會話數據安全
  31. PHP安全編程:會話數據注入
  32. PHP安全編程:主機文件目錄瀏覽
  33. PHP安全編程:PHP的安全模式

本文地址:http://www.snpmgr.live/librarys/veda/detail/2076,歡迎訪問原出處。

不打個分嗎?

轉載隨意,但請帶上本文地址:

http://www.snpmgr.live/librarys/veda/detail/2076

如果你認為這篇文章值得更多人閱讀,歡迎使用下面的分享功能。
小提示:您可以按快捷鍵 Ctrl + D,或點此 加入收藏

大家都在看

閱讀一百本計算機著作吧,少年

很多人覺得自己技術進步很慢,學習效率低,我覺得一個重要原因是看的書少了。多少是多呢?起碼得看3、4、5、6米吧。給個具體的數量,那就100本書吧。很多人知識結構不好而且不系統,因為在特定領域有一個足夠量的知識量+足夠良好的知識結構,系統化以后就足以應對大量未曾遇到過的問題。

奉勸自學者:構建特定領域的知識結構體系的路徑中再也沒有比學習該專業的專業課程更好的了。如果我的知識結構體系足以囊括面試官的大部分甚至吞并他的知識結構體系的話,讀到他言語中的一個詞我們就已經知道他要表達什么,我們可以讓他坐“上位”畢竟他是面試官,但是在知識結構體系以及心理上我們就居高臨下。

所以,閱讀一百本計算機著作吧,少年!

《深入理解計算機系統(原書第2版)》 布萊恩特(Randal E.Bryant) (作者), 奧哈拉倫(David R.O'Hallaron) (作者), 龔奕利 (譯者), 雷迎春 (譯者)

《深入理解計算機系統》從程序員的視角詳細闡述計算機系統的本質概念,并展示這些概念如何實實在在地影響應用程序的正確性、性能和實用性。全書共12章,主要內容包括信息的表示和處理、程序的機器級表示、處理器體系結構、優化程序性能、存儲器層次結構、鏈接、異常控制流、虛擬存儲器、系統級I/O、網絡編程、并發編程等。書中提供子大量的例子和練習題,并給出部分答案,有助于讀者加深對正文所述概念和知識的理解。

更多計算機寶庫...

燃烧吧足球登陆