
[英]Best way to do 'loose' IP check for protection against session hijacking
[英]What is the best way to prevent session hijacking?
具体来说,这与何时使用客户端会话 cookie 来标识服务器上的会话有关。
是对整个网站使用 SSL/HTTPS 加密的最佳答案,并且您有最好的保证,没有中间人攻击将能够嗅探现有的客户端会话 cookie?
也许第二个最好的方法是对存储在会话 cookie 中的会话值本身使用某种加密?
如果恶意用户可以物理访问机器,他们仍然可以查看文件系统以检索有效的会话 cookie 并使用它来劫持会话?
加密会话值将产生零影响。 会话 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。
请记住,您的用户可能拥有不止一台计算机,因此他们可能有多个活动会话。 不要做一些强迫他们每次在计算机之间切换时重新登录的事情。
// 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,它们可能会被盗,这是会话劫持攻击的载体之一。
你有没有考虑过读一本关于 PHP 安全的书? 强烈推荐。
对于非 SSL 认证站点,我使用以下方法取得了很大成功。
禁止在同一帐户下进行多个会话,确保您不是仅通过 IP 地址进行检查。 而是通过登录时生成的令牌进行检查,该令牌与数据库中的用户会话一起存储,以及 IP 地址、HTTP_USER_AGENT 等
使用基于关系的超链接生成一个链接(例如http://example.com/secure.php?token=2349df98sdf98a9asdf8fas98df8 )该链接附加一个 x-BYTE(首选大小)随机加盐 MD5 字符串,在页面重定向时随机生成令牌对应于请求的页面。
短寿命会话身份验证 cookie。 如上所述,包含安全字符串的 cookie 是对会话有效性的直接引用之一,这是一个好主意。 让它每 x 分钟过期一次,重新发布该令牌,并使用新数据重新同步会话。 如果数据中有任何不匹配,请注销用户,或让他们重新验证他们的会话。
我绝不是该主题的专家,我在这个特定主题上有一些经验,希望其中一些可以帮助那里的任何人。
没有办法 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 的会话劫持。 特别是当法律力量带着从执法机构获得的假证书时。
您将需要网络之外的某些东西来保护您的会话,例如一次性密码。 这就是为什么一次性垫如此敏感并且只能由少数公司出售的原因。
小心,一次性垫可能会被利用。 选择您的职业一次性垫。
通过以下方式保护:
$ip=$_SERVER['REMOTE_ADDER'];
$_SESSEION['ip']=$ip;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.