预防会话劫持的最佳方法是什么?

特别是在使用客户端会话 Cookie 标识服务器上的会话时。

对整个网站使用 SSL/HTTPS 加密的最佳答案是什么,并且您可以最好地保证没有中间人能够嗅到现有的客户端会话 cookie 吗?

对存储在会话 cookie 中的会话值本身使用某种加密也许是第二个最佳选择?

如果恶意用户对机器有物理访问权限,他们仍然可以查看文件系统来检索有效的会话 cookie 并使用它来劫持会话?

148893 次浏览

为了降低风险,还可以将原始 IP 与会话关联。这样,攻击者必须在同一个私有网络中才能使用会话。

检查引用标头也可以是一个选项,但这些更容易欺骗。

加密会话值将没有任何效果。会话 cookie 已经是一个任意值,对它进行加密只会生成另一个可以嗅探的任意值。

唯一真正的解决方案是 HTTPS。如果您不想在整个站点上使用 SSL (可能有性能方面的问题) ,那么可以只使用 SSL 保护敏感区域。为此,首先要确保您的登录页面是 HTTPS。当用户登录时,除了常规会话 Cookie 之外,还要设置一个安全 Cookie (这意味着浏览器只会通过 SSL 链接传输它)。然后,当用户访问您的某个“敏感”区域时,将它们重定向到 HTTPS,并检查是否存在该安全 cookie。一个真正的用户会拥有它,而会话劫持者不会。

编辑 : 这个答案最初写于2008年。现在是2016年了,没有理由不在整个站点上使用 SSL。不再有明文 HTTP!

SSL 只能帮助嗅探攻击。如果攻击者可以访问您的计算机,我将假定他们也可以复制您的安全 cookie。

至少,要确保过一段时间后,旧饼干就失去了它们的价值。当 cookie 停止工作时,即使是成功的劫持攻击也会被阻止。如果用户有一个来自一个多月前登录的会话的 cookie,让他们重新输入密码。确保每当用户单击站点的“注销”链接时,旧的会话 UUID 永远不会再被使用。

我不确定这个想法是否行得通,但是这样做: 在你的会话 cookie 中添加一个序列号,也许像这样一个字符串:

SessionUUID,序列号,当前日期/时间

加密此字符串并将其用作会话 cookie。定期更改序列号——也许是当 cookie 过期5分钟,然后重新发布 cookie。如果你愿意,你甚至可以在每个页面上重新发布它。在服务器端,记录为该会话发出的最后一个序列号。如果有人发送了一个序列号错误的 cookie,这意味着攻击者可能正在使用一个他们之前截获的 cookie,因此使会话 UUID 无效,并要求用户重新输入他们的密码,然后重新发出一个新的 cookie。

请记住,您的用户可能有多台计算机,所以他们可能有多个活动会话。不要做一些事情,迫使他们再次登录,每次他们之间的电脑切换。

确保不对会话 ID 使用递增整数。最好使用 GUID 或其他随机生成的长字符串。

试试安全 Cookie 协议,这是 Liu,Kovacs,Huang 和 Gouda 在 这个论文中描述的:

如文件所述:

一个安全的 在客户端和服务器之间运行的 cookie 协议 需要提供以下四种服务: 身份验证、机密性、完整性和反重放。

至于部署的便利性:

就效率而言,我们的协议不涉及任何数据库 查找或公钥加密。就可部署性而言,我们的协议可以很容易地部署在现有的 Web 服务器上,并且不需要对 互联网 cookie 规范。

简而言之: 它是安全的,轻量级的,工程为我只是伟大的。

保护:

$ip=$_SERVER['REMOTE_ADDER'];
$_SESSEION['ip']=$ip;

你有没有考虑过读一本关于 PHP 安全性的书? 强烈推荐。

对于非 SSL 认证的站点,我使用以下方法取得了很大的成功。

  1. 禁止在同一个帐户下进行多个会话,确保您不会仅仅通过 IP 地址来检查这一点。而是通过登录时生成的令牌进行检查,令牌存储在数据库中的用户会话中,以及 IP 地址、 HTTP _ USER _ AGENT 等等

  2. 使用基于关系的超链接 生成链接(例如 http://example.com/secure.php?token=2349df98sdf98a9asdf8fas98df8) 链接附加了一个 x-BYTE (首选大小)随机加盐的 MD5字符串,在页面重定向时,随机生成的令牌对应于请求的页面。

    • 重新加载后,要进行几次检查。
    • 原始 IP 地址
    • HTTP _ USER _ AGENT
    • 会话纪念品
    • 你明白了吧。
  3. 短生命周期会话身份验证 cookie。 如上所述,一个包含安全字符串的 cookie 是一个好主意,它是对会话有效性的直接引用之一。让它每隔 x 分钟过期一次,重新发出该令牌,并将会话与新的 Data 重新同步。如果数据中有任何错误匹配,要么注销用户,要么让他们重新验证会话。

我并不是这方面的专家,我在这方面有一些经验,希望能对大家有所帮助。

没有办法100% 防止会话劫持,但是通过某种方法可以减少攻击者劫持会话的时间。

防止会话劫持的方法:

1-始终使用 ssl 证书会话;

2-只发送 httponly 设置为 true 的会话 cookie (防止 javascript 访问会话 cookie)

2-在登录和注销时使用会话重新生成 id (注意: 不要在每次请求时使用会话重新生成,因为如果您有连续的 ajax 请求,那么您就有机会创建多个会话。)

3-设置会话超时

$_ SESSION 变量中的4-store 浏览器用户代理,并在每次请求时与 $_ SERVER [‘ HTTP _ USER _ AGENT’]进行比较

5-设置一个令牌 cookie,并将 cookie 的过期时间设置为0(直到浏览器关闭)。 为每个请求重新生成 Cookie 值(对于 ajax 请求不要重新生成令牌 Cookie)。 例如:

    //set a token cookie if one not exist
if(!isset($_COOKIE['user_token'])){
//generate a random string for cookie value
$cookie_token = bin2hex(mcrypt_create_iv('16' , MCRYPT_DEV_URANDOM));


//set a session variable with that random string
$_SESSION['user_token'] = $cookie_token;
//set cookie with rand value
setcookie('user_token', $cookie_token , 0 , '/' , 'donategame.com' , true , true);
}


//set a sesison variable with request of www.example.com
if(!isset($_SESSION['request'])){
$_SESSION['request'] = -1;
}
//increment $_SESSION['request'] with 1 for each request at www.example.com
$_SESSION['request']++;


//verify if $_SESSION['user_token'] it's equal with $_COOKIE['user_token'] only for $_SESSION['request'] > 0
if($_SESSION['request'] > 0){


// if it's equal then regenerete value of token cookie if not then destroy_session
if($_SESSION['user_token'] === $_COOKIE['user_token']){
$cookie_token = bin2hex(mcrypt_create_iv('16' , MCRYPT_DEV_URANDOM));


$_SESSION['user_token'] = $cookie_token;


setcookie('user_token', $cookie_token , 0 , '/' , 'donategame.com' , true , true);
}else{
//code for session_destroy
}


}


//prevent session hijaking with browser user agent
if(!isset($_SESSION['user_agent'])){
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
}


if($_SESSION['user_agent'] != $_SERVER['HTTP_USER_AGENT']){
die('session hijaking - user agent');
}

注意: 不要使用 ajax 请求重新生成令牌 cookie 注意: 上面的代码是一个例子。 注意: 如果用户注销,那么必须销毁 cookie 令牌和会话

6-使用用户 ip 防止会话劫持不是一个好的方法,因为一些用户每次请求都会改变 ip。影响有效用户

7-我个人在数据库中存储会话数据,采用什么方法取决于您

如果你发现我的方法有错误,请纠正我。如果你有更多的方法来防止会话伪装请告诉我。

// Collect this information on every request
$aip = $_SERVER['REMOTE_ADDR'];
$bip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$agent = $_SERVER['HTTP_USER_AGENT'];
session_start();


// Do this each time the user successfully logs in.
$_SESSION['ident'] = hash("sha256", $aip . $bip . $agent);


// Do this every time the client makes a request to the server, after authenticating
$ident = hash("sha256", $aip . $bip . $agent);
if ($ident != $_SESSION['ident'])
{
end_session();
header("Location: login.php");
// add some fancy pants GET/POST var headers for login.php, that lets you
// know in the login page to notify the user of why they're being challenged
// for login again, etc.
}

它所做的是捕获关于用户会话的“上下文”信息,这些信息在单个会话的生命周期中不应该更改。用户不会同时在美国和中国的电脑前,对吧?因此,如果在同一个会话中 IP 地址突然发生变化,这强烈暗示了一种会话劫持尝试,那么你就通过结束会话并强制用户重新验证来保护会话。这阻止了攻击尝试,攻击者也被迫登录,而不是获得访问会话的权限。通知用户的尝试(Ajax 它了一点) ,和 vola,稍微恼火 + 通知用户和他们的会话/信息是受保护的。

我们引入用户代理和 X-FORWARDED-FOR 来尽最大努力为代理/网络背后的系统捕获会话的唯一性。你也许可以使用更多的信息,尽情发挥你的创造力。

虽然不是100% 有效,但是非常有效。

你还可以做更多的事情来保护会话,使它们过期,当用户离开一个网站,回来强迫他们再次登录。你可以通过捕获一个空白的 HTTP _ REFERER (在 URL 栏中输入了域名)来检测用户的离开和回来,或者检查 HTTP _ REFERER 中的值是否等于你的域名(用户点击了一个外部/精心制作的链接来到你的站点)。

会话到期,不要让它们无限期地保持有效。

不要依赖 cookie,它们可能被偷走,它是会话劫持攻击的载体之一。

让我们考虑一下,在登录阶段,客户机和服务器可以就一个秘密 salt 值达成一致。此后,服务器将在每次更新时提供一个 count 值,并期望客户机使用(secret salt + count)的 hash 进行响应。潜在的劫持者没有任何方法来获得这个秘密的 salt 值,因此不能生成下一个 hash。

有许多方法可以创建对会话劫持的保护,但是所有这些方法都会降低用户的满意度或者不安全。

  • IP 和/或 X-转发-检查。这些工作,是相当安全的... 但想象用户的痛苦。他们带着无线网络来到办公室,得到新的 IP 地址,然后就失去了会话。又要登录了。

  • 用户代理检查。和上面一样,新版本的浏览器已经过时,并且您丢失了一个会话。此外,这些真的很容易“黑客”。对于黑客来说,发送假的 UA 字符串是微不足道的。

  • LocalStorage 令牌。在登录时生成一个令牌,将其存储在浏览器存储中,并将其存储到加密的 cookie 中(在服务器端加密)。这对用户没有副作用(通过浏览器升级,localStorage 将持续存在)。不像隐晦式安全那么安全。另外,您可以向 JS 添加一些逻辑(加密/解密)来进一步掩盖它。

  • 饼干重新发售。这可能是正确的做法。诀窍是一次只允许一个客户端使用 cookie。因此,活动用户每隔一小时或更少时间就会重新发布 Cookie。如果发出新 cookie,则旧 cookie 无效。黑客仍然是可能的,但更难做-无论是黑客或有效的用户将获得访问拒绝。

AFAIK 会话对象不能在客户端访问,因为它存储在 Web 服务器上。但是,会话 ID 是以 Cookie 的形式存储的,它允许 Web 服务器跟踪用户的会话。

为了防止会话劫持使用 session id,你可以在 session 对象中存储一个散列字符串,这个散列字符串由两个属性组合而成,即 remote addr 和 remote port,这两个属性可以在 request 对象中的 web 服务器上访问。这些属性将用户会话绑定到用户登录的浏览器。

如果用户从另一个浏览器或同一系统上的匿名模式登录,IP addr 将保持不变,但端口将不同。因此,当访问应用程序时,Web 服务器将为用户分配一个不同的会话 ID。

下面是我通过将会话 ID 从一个会话复制到另一个会话来实现和测试的代码。效果很好。如果有漏洞,告诉我你是怎么模拟的。

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
String sessionKey = (String) session.getAttribute("sessionkey");
String remoteAddr = request.getRemoteAddr();
int remotePort = request.getRemotePort();
String sha256Hex = DigestUtils.sha256Hex(remoteAddr + remotePort);
if (sessionKey == null || sessionKey.isEmpty()) {
session.setAttribute("sessionkey", sha256Hex);
// save mapping to memory to track which user attempted
Application.userSessionMap.put(sha256Hex, remoteAddr + remotePort);
} else if (!sha256Hex.equals(sessionKey)) {
session.invalidate();
response.getWriter().append(Application.userSessionMap.get(sessionKey));
response.getWriter().append(" attempted to hijack session id ").append(request.getRequestedSessionId());
response.getWriter().append("of user ").append(Application.userSessionMap.get(sha256Hex));
return;
}
response.getWriter().append("Valid Session\n");
}

我使用了 SHA-2算法,使用 SHA-256在 Baeldung 散步中给出的示例对值进行散列

期待你的评论。

仅使用 SSL,而不是在会话 id 中加密 HTTP _ USER _ AGENT 并在每个请求中验证它,只是将 HTTP _ USER _ AGENT 字符串也存储在会话 db 中。

现在您只有一个简单的基于服务器的字符串与 ENV‘ HTTP _ USER _ AGENT’进行比较。

或者,您可以在字符串中添加某种变体,以便对浏览器版本更新更加健壮。 您可以拒绝某些 HTTP _ USER _ AGENT id (即空的) 虽然不能完全解决问题,但至少增加了一点复杂性。

另一种方法是使用更复杂的浏览器指纹技术,将这些值与 HTTP _ USER _ AGENT 结合起来,不时地将这些值发送到一个单独的头部值中。但是您应该加密会话标识本身中的数据。

但是这使得解密变得更加复杂,并且提高了每个请求的解密所需的 CPU 使用量。

如果因特网服务供应商劫持证书验证,因特网服务供应商可能会启动一个中间人攻击,特别是在证书权威受到威胁的情况下。

所以我相信你不能阻止会话劫持从 ISP。特别是当法律部门使用从 CA 那里获得的假证书时。

您将需要网络之外的东西来保护您的会话,例如一个时间垫。这就是为什么一个时间垫如此敏感,只能由少数公司销售。

小心,一个时间垫可能被利用。选择你的专业一个时间垫。