Javascript 堆栈溢出';s使用本地存储进行授权似乎不安全。这是正确的吗?我们如何加强它?

Javascript 堆栈溢出';s使用本地存储进行授权似乎不安全。这是正确的吗?我们如何加强它?,javascript,html,authorization,local-storage,single-sign-on,Javascript,Html,Authorization,Local Storage,Single Sign On,我一直在开发一个身份验证和授权模块,类似于stackexchange的实现方式。现在我确信他们使用了oAuth的某个模型或令牌生成服务器,该服务器授权使用他们的各个站点。我做了一个小实验 登录Stackoverflow后,我会从开发者控制台删除所有cookie 我保留了本地存储对象,该对象包含stackoverflow域的键se:fkey xxxxxxxxxxxxxxxxxxxxxxxxx stackauth域GlobalIn还有另一个密钥:xxxxxxxxxxxxxxxxxxxx 如果我使用s

我一直在开发一个身份验证和授权模块,类似于stackexchange的实现方式。现在我确信他们使用了oAuth的某个模型或令牌生成服务器,该服务器授权使用他们的各个站点。我做了一个小实验

登录Stackoverflow后,我会从开发者控制台删除所有cookie

我保留了本地存储对象,该对象包含stackoverflow域的键
se:fkey xxxxxxxxxxxxxxxxxxxxxxxxx

stackauth域
GlobalIn还有另一个密钥:xxxxxxxxxxxxxxxxxxxx

如果我使用sef.fkey进行会话劫持,则不会发生任何事情。但是GlobalLogin,我能够复制并劫持我的会话。所以,我的问题是,S/O如何处理每个站点的授权后身份验证。此外,是否有一种方法可以在使用一次globalLogin后使其无效

{EDIT1}

因此,仅使用
globalloin
就足够了。如果您可以获得该密钥,只需打开一个私有浏览实例。登录页面时,在stackauth的Localstorage中,创建
键值映射并刷新页面。您将登录

{EDIT2}

globalwin
键在多个会话中似乎是一致的。我的
globalloin
键已经一天没有刷新了。可以安全地假设,如果您的密钥被劫持,攻击者将无限期地访问您的配置文件

{EDIT3}

对于所有正在投票并将投票支持此问题的人,请将其视为与编程无关的问题。让我这样说吧,我们如何使用localstorage安全地将SSO存储在web浏览器上?既然它们很容易受到破坏,我们需要做些什么来防止这种情况发生?我的一位同事很体贴地给了我他的GlobalIn密钥,我能够从另一台计算机上劫持他的会话,尽管它在同一个网络上


PS:这纯粹是为了从理论上理解我做了这件事。

好吧,与其看漏洞,不如看可能的攻击向量。我将在这里添加一个表作为TL/DR

Attacker      | Vulnerable?
Eavesdropper  | Yes
MITM          | Yes
Local Attack  | Yes
Server Attack | Yes
是的,这是一个问题

远程攻击者,可以观察流量,但不能修改流量 将其视为咖啡馆中的被动攻击者。他们可以看到所有TCP级别的流量

在默认情况下,向SO来回的请求是不加密的。您可以通过HTTPS进行浏览,但默认情况下它仅为HTTP

因此,攻击者可以看到任何请求经过,并检查/窃取数据

因此,让我们看看是否在请求中发送了
globalloin
令牌

事实上,的确如此。在登录页面上,通过iframe向以下URL发送请求:

https://stackauth.com/auth/global/read?request=//snip//
该URL返回一个脚本:

var data = {
    "ReadSession":"https://stackauth.com/auth/global/read-session",      
    "Request":"//snip//",
    "Nonce":"//snip//",
    "Referrer":"//snip//",
    "StorageName":"GlobalLogin"
};

var toMsg = window.parent;
var obj = localStorage.getItem(data.StorageName);

if(obj != null) {
    var req = new XMLHttpRequest();
    req.open(
        'POST', 
            data.ReadSession+
            'request='+encodeURIComponent(data.Request)+
            '&nonce='+encodeURIComponent(data.Nonce)+
            '&seriesAndToken='+encodeURIComponent(obj), 
        false
    );
    req.send(null);

    if(req.status == 200){
        toMsg.postMessage(req.responseText, data.Referrer);
    }else{
        toMsg.postMessage('No Session', data.Referrer);
    }
}else{
    toMsg.postMessage('No Local Storage', data.Referrer);
}
现在,请注意,
globalloin
通过HTTPS发送到服务器。因此,能够读取流量的远程攻击者将无法获取
globalwin
令牌

因此,
globalloin
部分是安全的,不会被窃听

但是,请注意,它仍然很容易嗅探会话cookie,因为它是通过HTTP发送的

远程攻击者,可以修改流量(MITM) 嗯,这就是事情变得有趣的地方

如果你可以修改流量,你可以做一些真正有趣的事情

初始页面创建一个iframe,通过HTTPS引入上述stackauth.com URL。如果可以修改初始页面(也可以通过XSS),那么可以将请求降级为HTTP

StackAuth.com也会接受的。当它向stackauth.com发出请求时,您还需要拦截它,并将其
ReadSession
URL更改为HTTP

但是,你所需要做的就是观察对
ReadSession
URL的调用,然后砰的一声,你偷了
globalwin
令牌

但是不管怎么说,流量是HTTP的,所以这并不重要,因为你不需要费劲去偷cookie。那为什么要麻烦呢

本地攻击者 如果此人有权访问计算机以读取本地存储文件,那么他们所做的远比窃取您的登录令牌更糟糕

有一种称为“中间浏览器”的攻击类型,浏览器中的漏洞允许攻击者做任何他们想做的事情

除了努力保持浏览器的安全(从你的角度来说,你无能为力),没有什么真正有效的保护措施可以防止它

因此,如果攻击者可以在本地访问计算机,游戏就结束了

基于服务器的攻击者 如果攻击者可以访问StackOverflow的服务器,游戏就结束了

结论 只要允许HTTP(因为MITM总是可以降级到HTTP的连接),就没有什么需要保护的,因为会话机密总是可以通过窃听窃取的

保护此信息的唯一方法是在任何地方使用并强制使用HTTPS

值得注意的是,您可以通过在stackauth.com上强制HST来保护
GlobalLogin
,从而使主站点可以通过HTTP访问。这不会阻止攻击的效果(会话劫持)。但它会保护一个载体

但是,在任何地方都只通过HST使用HTTPS将是防止此类问题的最佳且真正唯一的方法。其他任何东西都会是枪伤上的绷带


注意:我在发布这篇文章之前确实和SO谈过这件事

我已经在我的系统上尝试了你的建议,这看起来像一个笑话,很简单。无论如何,这都不是一个安全的实现,您只需在5分钟内使用一个受损的系统,复制对给定用户保持不变的GlobalLogin密钥,然后使用该用户的GlobalLogin随时从任何使用匿名浏览的系统登录