在PHP中防止会话劫持的正确方法。

23

我知道这个话题已经被广泛讨论了,但我还有一些特定的问题没有得到解答。例如:

// **PREVENTING SESSION HIJACKING**
// Prevents javascript XSS attacks aimed to steal the session ID
ini_set('session.cookie_httponly', 1);

// Adds entropy into the randomization of the session ID, as PHP's random number
// generator has some known flaws
ini_set('session.entropy_file', '/dev/urandom');

// Uses a strong hash
ini_set('session.hash_function', 'whirlpool');

// **PREVENTING SESSION FIXATION**
// Session ID cannot be passed through URLs
ini_set('session.use_only_cookies', 1);

// Uses a secure connection (HTTPS) if possible
ini_set('session.cookie_secure', 1);

session_start();

// If the user is already logged
if (isset($_SESSION['uid'])) {
    // If the IP or the navigator doesn't match with the one stored in the session
    // there's probably a session hijacking going on

    if ($_SESSION['ip'] !== getIp() || $_SESSION['user_agent_id'] !== getUserAgentId()) {
        // Then it destroys the session
        session_unset();
        session_destroy();

        // Creates a new one
        session_regenerate_id(true); // Prevent's session fixation
        session_id(sha1(uniqid(microtime())); // Sets a random ID for the session
    }
} else {
    session_regenerate_id(true); // Prevent's session fixation
    session_id(sha1(uniqid(microtime())); // Sets a random ID for the session
    // Set the default values for the session
    setSessionDefaults();
    $_SESSION['ip'] = getIp(); // Saves the user's IP
    $_SESSION['user_agent_id'] = getUserAgentId(); // Saves the user's navigator
}

所以,我的问题是:

  • ini_set是否提供足够的安全性?
  • 保存用户的IP和浏览器信息,然后每次加载页面时检查它来检测会话劫持是否可行?这样做会有什么问题吗?
  • 使用session_regenerate_id()是否正确?
  • 使用session_id()是否正确?

2
所有夸大其词的随机性都可能是安全性中最不相关的部分,因为最广泛的安全漏洞通常完全在其他地方。您只需要在登录成功时重新生成会话ID即可。 - Kerrek SB
好吧,这样做不会有什么坏处。此外,我这样做的主要原因是因为我的网站没有账户,只有匿名浏览。所以,不存在登录这种事情。每次重新生成会话ID是否更好呢? - federico-t
1
没有唯一的答案。首先要在登录时重新生成。除此之外就有点过度了。不过,在某些情况下,会话重建可能会有所帮助。例如:如果您管理某种交易(保存帖子、上传文件、更改应用程序后面的数据),它会有所帮助,因为您可以跟踪用户是否执行了历史记录(-1),并再次发送了旧请求。 - Alfabravo
1
关于重新生成:每当用户权限发生变化时,都需要重新生成。这不仅包括在登录/注销界限上跨越时重新生成,还应包括在授予或撤销任何应用程序权限或角色更改时重新生成。 - Cheekysoft
不要认为这是完整的,但你可能会发现这个清单很有用 https://dev59.com/Ml3Va4cB1Zd3GeqPDsUv#8324014 - Cheekysoft
我原以为session_regenerate_id()会根据你的INI设置为会话ID生成一个随机ID。那么,当session_regenerate_id()已经为你设置了一个新的ID时,为什么还需要调用session_id(sha1(uniqid(microtime())))来设置一个新的ID呢?http://bit.ly/uL2Pc6 - Avicinnian
1个回答

18

你的配置很棒。你肯定阅读了有关如何锁定php会话的资料。然而,这行代码抵消了你的php配置提供的大部分保护:

session_id(sha1(uniqid(microtime()));

这是一种极糟糕的生成会话ID的方法。根据您的配置,您正在从 /dev/urandom 生成会话ID,这是一个很棒的熵池。这将比uniqid()更随机,后者已经大多是时间戳,再添加另一个时间戳并没有什么帮助。尽快删除此行代码。

检查IP地址是有问题的,IP地址出于合法原因而发生变化,例如用户处于负载均衡器或TOR之后。用户代理检查是毫无意义的,就像有一个GET变量?is_hacker=False,如果攻击者拥有会话ID,他们可能已经获得了用户代理,如果他们没有,这个值很容易被暴力破解。


1
很好的答案。我同意,IP地址可以是真正动态的,如果您已经知道会话ID,则导航器信息可以很容易地伪造,但是更多的安全性总是更好(除非它花费了很多资源,在这种特殊情况下并不是)。然而,我可能会将 $_SESSION['ip'] !== getIp() || $_SESSION['user_agent_id'] !== getUserAgentId() 更改为 $_SESSION['ip'] !== getIp() && $_SESSION['user_agent_id'] !== getUserAgentId()。如果两个条件都满足,则肯定发生了一些奇怪的事情。无论如何,感谢您的回答! - federico-t
2
您也可以使用IP地址的前两组数字xxx.xxx。虽然不是最安全的,但比没有更安全,在大型大学和企业中问题也较少。 - Sanne
@Sanne 或者你可以将你的方法与上面的方法结合起来,创建一个更强大的屏障。 - Anonymous Penguin
更随机的会话ID为什么不是一个好主意?是因为碰撞吗? - Anonymous Penguin
@federicot 这里有一些笔记,Opera浏览器允许用户代理提供不同的用户代理。从这里的讨论中,我想象IP和用户代理都不适合作为授权令牌,因此应该在登录时生成一个令牌字符串并存储在数据库中以及会话中,如果它们不匹配,则将该用户退出。 - Martin
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接