HTML5 Web应用程序中OAuth2的本地存储与cookie
Localstorage vs cookies for OAuth2 in HTML5 Web App
我目前正在试用OAuth2来开发一个完全用JavaScript构建的移动应用程序,该应用程序可以与CakePHP API对话。看看下面的代码,看看我的应用程序目前的样子(请注意,这是一个实验,因此代码混乱,区域缺乏结构等。)
var access_token,
refresh_token;
var App = {
init: function() {
$(document).ready(function(){
Users.checkAuthenticated();
});
}(),
splash: function() {
var contentLogin = '<input id="Username" type="text"> <input id="Password" type="password"> <button id="login">Log in</button>';
$('#app').html(contentLogin);
},
home: function() {
var contentHome = '<h1>Welcome</h1> <a id="logout">Log out</a>';
$('#app').html(contentHome);
}
};
var Users = {
init: function(){
$(document).ready(function() {
$('#login').live('click', function(e){
e.preventDefault();
Users.login();
});
$('#logout').live('click', function(e){
e.preventDefault();
Users.logout();
});
});
}(),
checkAuthenticated: function() {
access_token = window.localStorage.getItem('access_token');
if( access_token == null ) {
App.splash();
}
else {
Users.checkTokenValid(access_token);
}
},
checkTokenValid: function(access_token){
$.ajax({
type: 'GET',
url: 'http://domain.example/api/oauth/userinfo',
data: {
access_token: access_token
},
dataType: 'jsonp',
success: function(data) {
console.log('success');
if( data.error ) {
refresh_token = window.localStorage.getItem('refresh_token');
if( refresh_token == null ) {
App.splash();
} else {
Users.refreshToken(refresh_token);
}
} else {
App.home();
}
},
error: function(a,b,c) {
console.log('error');
console.log(a,b,c);
refresh_token = window.localStorage.getItem('refresh_token');
if( refresh_token == null ) {
App.splash();
} else {
Users.refreshToken(refresh_token);
}
}
});
},
refreshToken: function(refreshToken){
$.ajax({
type: 'GET',
url: 'http://domain.example/api/oauth/token',
data: {
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: 'NTEzN2FjNzZlYzU4ZGM2'
},
dataType: 'jsonp',
success: function(data) {
if( data.error ) {
alert(data.error);
} else {
window.localStorage.setItem('access_token', data.access_token);
window.localStorage.setItem('refresh_token', data.refresh_token);
access_token = window.localStorage.getItem('access_token');
refresh_token = window.localStorage.getItem('refresh_token');
App.home();
}
},
error: function(a,b,c) {
console.log(a,b,c);
}
});
},
login: function() {
$.ajax({
type: 'GET',
url: 'http://domain.example/api/oauth/token',
data: {
grant_type: 'password',
username: $('#Username').val(),
password: $('#Password').val(),
client_id: 'NTEzN2FjNzZlYzU4ZGM2'
},
dataType: 'jsonp',
success: function(data) {
if( data.error ) {
alert(data.error);
} else {
window.localStorage.setItem('access_token', data.access_token);
window.localStorage.setItem('refresh_token', data.refresh_token);
access_token = window.localStorage.getItem('access_token');
refresh_token = window.localStorage.getItem('refresh_token');
App.home();
}
},
error: function(a,b,c) {
console.log(a,b,c);
}
});
},
logout: function() {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
access_token = window.localStorage.getItem('access_token');
refresh_token = window.localStorage.getItem('refresh_token');
App.splash();
}
};
我有许多与OAuth:的实现有关的问题
显然,将access_token存储在localStorage中是一种糟糕的做法,我应该使用cookie。有人能解释一下原因吗?据我所知,这不再安全或不太安全,因为cookie数据不会被加密。
根据这个问题:本地存储与Cookies将数据存储在本地存储中,无论如何,它只在客户端可用,并且不像Cookies那样执行任何HTTP请求,所以对我来说似乎更安全,或者至少据我所知,它似乎没有任何问题!**
关于问题1,使用cookie作为过期时间,对我来说同样毫无意义,因为如果你查看代码,就会在应用程序启动时请求获取用户信息,如果在服务器端过期,则会返回错误,并需要refresh_token。因此,不确定客户端和服务器都有到期时间的好处,而服务器才是真正重要的。
在没有a的情况下,我如何获得刷新令牌,将其与原始access_token一起存储以供以后使用,并且B)也存储client_id?我被告知这是一个安全问题,但我以后如何使用这些,但在仅JS的应用程序中保护它们?再次查看上面的代码,了解到目前为止我是如何实现这一点的。
看起来您正在使用资源所有者密码凭据OAuth 2.0流,例如提交用户名/密码以获取访问令牌和刷新令牌。
- 访问令牌可以在JavaScript中暴露,访问令牌暴露的风险可以通过其短寿命来减轻
- 刷新令牌不应暴露给客户端JavaScript。它用于获得更多的访问令牌(如上所述),但如果攻击者能够获得刷新令牌,他们就可以随意获得更多的接入令牌,直到OAuth服务器撤销了为其颁发刷新令牌的客户端的授权
考虑到这一背景,让我回答您的问题:
- cookie或本地存储将为您提供跨页面刷新的本地持久性。将访问令牌存储在本地存储中可以为您提供更多的CSRF攻击保护,因为它不会像cookie那样自动发送到服务器。您的客户端JavaScript需要将其从本地存储中取出,并在每次请求时进行传输。我正在开发OAuth2应用程序,因为这是一种单页方法,所以我两者都不做;相反,我只是把它留在记忆中
- 我同意。。。如果您存储在cookie中,这只是为了持久性,而不是为了过期,那么当令牌过期时,服务器将以错误响应。我认为你可以创建一个过期的cookie的唯一原因是,你可以在不首先发出请求和等待错误响应的情况下检测它是否过期。当然,您可以通过保存已知的过期时间来对本地存储执行同样的操作
- 这是整个问题的关键,我相信"在没有a的情况下,我如何获得刷新令牌,将其与原始access_token一起存储以供以后使用,并且B)还存储一个client_id";。不幸的是,你真的不能。。。如介绍性评论中所述,拥有刷新令牌客户端会否定访问令牌
- 用户向服务器提交用户名和密码
-
服务器然后将用户名和密码转发到OAuth端点,在上面的
http://domain.example/api/oauth/token
示例中,并接收访问令牌和刷新令牌 - 服务器加密刷新令牌并将其设置在cookie中(应为HTTP Only)
- 服务器以明文形式(在JSON响应中)使用访问令牌ONLY和加密的仅HTTP cookie进行响应
- 客户端JavaScript现在可以读取和使用访问令牌(存储在本地存储中或其他任何位置
- 当访问令牌过期时,客户端向服务器(不是OAuth服务器,而是托管应用程序的服务器)提交一个新令牌的请求
- 服务器接收其创建的加密的仅HTTP cookie,对其进行解密以获得刷新令牌,请求新的访问令牌,并最终在响应中返回新的访问令牌
诚然,这确实违反了";仅JS";您正在寻找的约束。然而,a)同样,你真的不应该在JavaScript中有刷新令牌,b)它在登录/注销时需要非常少的服务器端逻辑,并且没有持久的服务器端存储。
关于CSRF的注意事项:如评论中所述,此解决方案不解决跨站点请求伪造问题;请参阅OWASP CSRF预防作弊表,了解有关解决这些攻击形式的进一步想法。
另一种选择是根本不请求刷新令牌(不确定这是否是您正在处理的OAuth 2实现的选项;根据规范,刷新令牌是可选的),并在其过期时不断重新进行身份验证。
完全安全的唯一方法是不在客户端存储访问令牌。任何能够(物理)访问您的浏览器的人都可以获得您的代币。
-
你对两者都不是一个好的解决方案的评估是准确的。
-
如果您仅限于客户端开发,那么使用过期时间将是最好的选择。它不需要您的用户频繁地重新向Oauth进行身份验证,并保证令牌不会永远存在。仍然不是最安全的。
-
获取新令牌需要执行Oauth工作流以获取新令牌。client_id绑定到特定的域,以便Oauth发挥作用。
保留Oauth令牌最安全的方法是服务器端实现。
对于纯客户端方法,如果有机会,请尝试使用"隐式流;而不是";资源所有者流";。您不会收到作为响应一部分的刷新令牌。
- 当用户访问页面JavaScript在localStorage中检查access_token并检查其expires_in时
- 如果丢失或过期,则应用程序打开新选项卡并将用户重定向到登录页面,成功登录后,用户将使用访问令牌重定向回,该访问令牌仅在客户端处理,并使用重定向页面保存在本地存储中
- 主页可能对本地存储中的访问令牌具有轮询机制,用户登录后(重定向页面将令牌保存到存储中)页面就会正常处理
在上述方法中,访问令牌应该是长寿命的(例如1年)。若对长寿符有顾虑,你们可以使用以下技巧。
- 当用户访问页面JavaScript在localStorage中检查access_token并检查其expires_in时
- 若丢失或过期,应用程序将打开隐藏的iframe并尝试登录用户。通常auth网站有一个用户cookie,并将授权存储到客户端网站,因此登录会自动进行,iframe中的脚本会将令牌填充到存储中
- 客户端的主页面设置了access_token和timeout的轮询机制。如果在这段短暂的时间内,access_token没有填充到存储中,这意味着我们需要打开新的选项卡并设置正常的隐含流
- 用于存储、检索和附加到url的javascript cookie
- 为什么大型网站不;I don’我更喜欢本地存储而不是cookie
- 将会话登录页存储在cookie中以进行表单解析(通过隐藏字段)
- 使用 Cookie 存储数据
- 如何将jQuery克隆的文本输入保存到cookie或html5存储中
- Javascript Security:将敏感数据存储在比cookie更安全的自调用函数中
- 为样式表切换器存储js cookie
- 使用纯js在cookie中存储多个值
- 如何在Javascript的跨域操作中存储cookie
- 通过javascript存储cookie
- 使用滑动存储cookie以删除jquery mobile
- 在用户浏览表单时存储cookie
- 在服务器上存储 Cookie 数据
- 如何在 socket.io 中存储cookie(socket.handshake.headers.cookie)
- 函数获胜't存储cookie
- 当你在iOS的主屏幕上添加一个web应用程序时,你就不能再存储cookie了
- 使用ETag在浏览器中存储cookie信息
- Phonegap/Javascript应用程序:我如何存储cookie
- 本地存储cookie会回退
- Javascript Cookie代码未存储Cookie/读取空Cookie值