具体来说,这与使用客户端会话 cookie 来识别服务器上的会话有关。
对整个网站使用 SSL/HTTPS 加密是否是最佳答案,并且您有最好的保证,中间攻击中的任何人都无法嗅探现有的客户端会话 cookie?
或许次佳对存储在会话 cookie 中的会话值本身使用某种加密?
如果恶意用户可以物理访问机器,他们仍然可以查看文件系统以检索有效的会话 cookie 并使用它来劫持会话?
加密会话值将产生零影响。会话 cookie 已经是一个任意值,加密它只会生成另一个可以被嗅探的任意值。
唯一真正的解决方案是 HTTPS。如果您不想在整个站点上使用 SSL(也许您有性能问题),您也许可以只使用 SSL 保护敏感区域。为此,首先确保您的登录页面是 HTTPS。当用户登录时,除了常规会话 cookie 之外,还设置一个安全 cookie(意味着浏览器只会通过 SSL 链接传输它)。然后,当用户访问您的“敏感”区域之一时,将其重定向到 HTTPS,并检查是否存在该安全 cookie。真正的用户会拥有它,会话劫持者不会。
编辑:这个答案最初是在 2008 年写的。现在是 2016 年,没有理由不在整个网站上使用 SSL。不再有明文 HTTP!
SSL 仅有助于嗅探攻击。如果攻击者可以访问您的机器,我会假设他们也可以复制您的安全 cookie。
至少,确保旧 cookie 在一段时间后失去其价值。当 cookie 停止工作时,即使是成功的劫持攻击也会被阻止。如果用户有一个来自一个多月前登录的会话的 cookie,请让他们重新输入密码。确保每当用户点击您网站的“注销”链接时,旧的会话 UUID 将永远无法再次使用。
我不确定这个想法是否可行,但这里是:在会话 cookie 中添加一个序列号,可能是这样的字符串:
SessionUUID、序列号、当前日期/时间
加密此字符串并将其用作您的会话 cookie。定期更改序列号 - 可能在 cookie 使用 5 分钟后重新发布 cookie。如果您愿意,您甚至可以在每次页面浏览时重新发布它。在服务器端,记录您为该会话发出的最后一个序列号。如果有人发送了带有错误序列号的 cookie,则意味着攻击者可能正在使用他们之前截获的 cookie,因此使会话 UUID 无效并要求用户重新输入密码,然后重新发出新的 cookie。
请记住,您的用户可能拥有不止一台计算机,因此他们可能有多个活动会话。不要做任何迫使他们每次在计算机之间切换时重新登录的事情。
您是否考虑过阅读有关 PHP 安全性的书?强烈推荐。
对于非 SSL 认证站点,我使用以下方法取得了很大成功。
禁止同一帐户下的多个会话,确保您不是仅通过 IP 地址进行检查。而是通过登录时生成的令牌进行检查,该令牌与用户会话一起存储在数据库中,以及 IP 地址、HTTP_USER_AGENT 等
使用基于关系的超链接生成一个链接(例如http://example.com/secure.php?token=2349df98sdf98a9asdf8fas98df8)该链接附加了一个 x-BYTE(首选大小)随机加盐 MD5 字符串,在页面重定向时随机生成令牌对应于请求的页面。
短寿命会话身份验证 cookie。如上所述,包含安全字符串的 cookie 是一个好主意,它是对会话有效性的直接引用之一。使其每 x 分钟过期一次,重新颁发该令牌,并将会话与新数据重新同步。如果数据中有任何不匹配,请注销用户或让他们重新验证其会话。
我绝不是该主题的专家,我对这个特定主题有一些经验,希望其中一些对那里的任何人有所帮助。
// 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 地址在同一会话中突然发生变化,强烈暗示会话劫持尝试,那么您可以通过结束会话并强制用户重新进行身份验证来保护会话。这阻止了黑客攻击,攻击者也被迫登录而不是访问会话。将尝试通知用户(将其放大一点),vola,有点恼火+知情的用户,他们的会话/信息受到保护。
我们引入了用户代理和 X-FORWARDED-FOR 来尽最大努力为代理/网络后面的系统捕获会话的唯一性。您也许可以使用更多信息,请随意发挥创意。
这不是100%,但它非常有效。
你可以做更多的事情来保护会话,使它们过期,当用户离开网站并返回时,可能会强制他们再次登录。您可以通过捕获空白 HTTP_REFERER(在 URL 栏中输入域)来检测用户离开和返回,或者检查 HTTP_REFERER 中的值是否等于您的域(用户单击外部/制作的链接以访问您的地点)。
过期会话,不要让它们无限期地保持有效。
不要依赖 cookie,它们可能会被窃取,它是会话劫持的攻击媒介之一。
没有办法 100% 防止会话劫持,但是通过一些方法,我们可以减少攻击者劫持会话的时间。
防止会话劫持的方法:
1 - 始终使用带有 ssl 证书的会话;
2 - 仅在 httponly 设置为 true 的情况下发送会话 cookie(防止 javascript 访问会话 cookie)
2 - 在登录和注销时使用会话重新生成 ID(注意:不要在每个请求中使用会话重新生成,因为如果您有连续的 ajax 请求,那么您有机会创建多个会话。)
3 - 设置会话超时
4 - 将浏览器用户代理存储在 $_SESSION 变量中,并在每次请求时与 $_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 - 我个人将会话数据存储在数据库中,取决于您采用什么方法
如果您在我的方法中发现错误,请纠正我。如果您有更多方法来防止会话 hyjaking,请告诉我。
试试Liu、Kovacs、Huang 和 Gouda在这篇论文中描述的 Secure Cookie 协议:
如文件所述:
在客户端和服务器之间运行的安全 cookie 协议需要提供以下四种服务:身份验证、机密性、完整性和防重放。
至于易于部署:
在效率方面,我们的协议不涉及任何数据库查找或公钥加密。在可部署性方面,我们的协议可以很容易地部署在现有的 Web 服务器上,并且不需要对 Internet cookie 规范进行任何更改。
简而言之:它安全、轻巧,非常适合我。
确保不要对会话 ID 使用递增整数。使用 GUID 或其他一些随机生成的长字符串要好得多。
有很多方法可以创建针对会话劫持的保护,但是所有这些方法要么降低了用户满意度,要么不安全。
IP 和/或 X-FORWARDED-FOR 检查。这些工作,并且非常安全......但想象一下用户的痛苦。他们来到有 WiFi 的办公室,他们获得了新的 IP 地址并失去了会话。必须重新登录。
用户代理检查。和上面一样,新版本的浏览器已经出来了,你失去了一个会话。此外,这些真的很容易“破解”。黑客发送虚假的 UA 字符串是微不足道的。
本地存储令牌。登录时生成一个令牌,将其存储在浏览器存储中并将其存储到加密的 cookie(在服务器端加密)。这对用户没有副作用(localStorage 通过浏览器升级持续存在)。它并不安全——因为它只是通过默默无闻的安全。此外,您可以向 JS 添加一些逻辑(加密/解密)以进一步模糊它。
Cookie 重新发行。这可能是正确的方法。诀窍是一次只允许一个客户端使用一个cookie。因此,活跃用户将每隔一小时或更短时间重新发布 cookie。如果发出新的 cookie,旧的 cookie 将失效。黑客仍然是可能的,但更难做到 - 无论是黑客还是有效用户都将被拒绝访问。
AFAIK 会话对象无法在客户端访问,因为它存储在 Web 服务器上。但是,会话 id 存储为 Cookie,它允许 Web 服务器跟踪用户的会话。
为了防止使用会话 id 进行会话劫持,您可以在会话对象中存储一个散列字符串,该字符串由远程地址和远程端口这两个属性组合而成,可以在请求对象内的 Web 服务器上访问。这些属性将用户会话与用户登录的浏览器联系起来。
如果用户从其他浏览器或同一系统上的隐身模式登录,IP 地址将保持不变,但端口将不同。因此,当访问应用程序时,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 算法使用在 baeldung 的 SHA-256 Hashing 中给出的示例对值进行散列
期待您的评论。
让我们考虑在登录阶段,客户端和服务器可以就秘密盐值达成一致。此后,服务器为每次更新提供一个计数值,并期望客户端以(秘密盐 + 计数)的哈希值进行响应。潜在的劫持者没有任何方法获得这个秘密盐值,因此无法生成下一个哈希值。
为了降低风险,您还可以将原始 IP 与会话相关联。这样,攻击者必须在同一个专用网络中才能使用会话。
检查引用标题也可以是一种选择,但这些更容易被欺骗。
仅使用 SSL,而不是在会话 id 中加密 HTTP_USER_AGENT 并在每个请求上验证它,只需将 HTTP_USER_AGENT 字符串也存储在会话数据库中。
现在您只有一个简单的基于服务器的字符串与 ENV'HTTP_USER_AGENT' 进行比较。
或者,您可以在字符串中添加某种变体,以便对浏览器版本更新更加稳健。你可以拒绝某些 HTTP_USER_AGENT id。(空的,即)不能完全解决问题,但它至少增加了一点复杂性。
另一种方法可能是使用更复杂的浏览器指纹识别技术,并将这些值与 HTTP_USER_AGENT 结合起来,并在单独的标头值中不时发送这些值。但是,您应该加密会话 ID 本身中的数据。
但这使它变得更加复杂,并提高了每次请求解密时的 CPU 使用率。
如果 ISP 劫持证书验证,则 ISP 可能会发起中间人攻击。尤其是在证书颁发机构受损的情况下。
所以我相信你不能阻止来自 ISP 的会话劫持。尤其是当法律力量带有根据执法机构从 CA 获得的假证书时。
您将需要网络外部的一些东西来保护您的会话,例如一个时间垫。这就是为什么一次性垫如此敏感,只能由少数公司出售的原因。
小心,一次性垫可能会被利用。选择您的专业一次性垫。
通过以下方式保护:
$ip=$_SERVER['REMOTE_ADDER'];
$_SESSEION['ip']=$ip;