你的JWTs存储在哪里

如何存储这些令牌。如果你正在构建一个web应用程序,你有两种选择:

  • HTML5 Web Storage (localStorage或sessionStorage)
  • Cookies

比较这两个,假设我们有一个虚构的AngularJS或单页应用(SPA)叫做galaxies.com,登录路由(/token)验证用户身份返回一个JWT。访问你的SPA其他API端点服务,客户端需要传递一个有效的JWT。

单一页面应用程序的请求将会类似:

HTTP/1.1
 
POST /token
Host: galaxies.com
Content-Type: application/x-www-form-urlencoded
 
username=tom@galaxies.com&password=andromedaisheadingstraightforusomg&grant_type=password

你的服务器的响应会根据你是否使用cookie或Web Storage而变化。为了比较,让我们看看如何两者兼顾。

JWT localStorage或sessionStorage(Web存储)

用username和password交换JWT存储在浏览器存储中(sessionStorage或localStorage)是相当简单的。响应正文将包含JWT作为一个访问令牌:

HTTP/1.1 200 OK
 
  {
  "access_token": "eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB",
       "expires_in":3600
   }

在客户端,你可以将这个令牌存储在HTML5 Web存储中(假设我们有一个成功的回调函数):

function tokenSuccess(err, response) {
    if(err){
        throw err;
    }
    $window.sessionStorage.accessToken = response.body.access_token;
}

回传访问令牌到你受保护的API,你将使用HTTP Authorization Header和Bearer组合。请求你的SPA将会像:

HTTP/1.1
 
GET /stars/pollux
Host: galaxies.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB

JWT Cookie Storage

用username和password交换JWT存储在cookie中也很简单。响应使用Set-Cookie HTTP头:

HTTP/1.1 200 OK
 
Set-Cookie: access_token=eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB; Secure; HttpOnly;

回传访问令牌到你同一领域受保护的API,浏览器将自动包括cookie的值。请求你受保护的API将类似于:

GET /stars/pollux
Host: galaxies.com
 
Cookie: access_token=eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB;

那么,有什么区别呢?

如果你比较这些方法,两者都获取一个JWT到浏览器。两者都是无状态的,因为你的API需要的所有信息都在JWT中。都是简单的传递备份到你受保护的API。区别在于介质。

JWT sessionStorage和localStorage的安全性

Web存储(localStorage/sessionStorage)可以通过同一域上JavaScript访问。这意味着任何在你的网站上运行的JavaScript都可以访问Web存储,因为这样容易受到跨站点脚本(XSS)攻击。XSS,简而言之,是一种漏洞,攻击者可以注入在页面上运行的JavaScript。基本的XSS攻击试图通过input表单注入JavaScript,攻击者将<script>alert('You are Hacked');</script>放入表单,以查看其是否能被浏览器运行,并能被其他用户查看。

为了防止XSS,普通的响应是避开和编码所有不可信的数据。但这远不是故事的全部。2015年,现代的web应用程序使用托管在CDN或者外部基础设施上的JavaScript。现代的Web应用程序使用第三方JavaScript库,用于A/B测试、 funnel/market analysis和广告。我们使用像Bower这样的包管理器导入别人的代码到我们的应用程序。

如果你使用的脚本中有一个被盗用了怎么办?恶意的JavaScript可以嵌入到页面上,并且Web存储被盗用。这些类型的XSS攻击可以得到每个人的Web存储来访问你的网站,没有他们的知识。这可能是为什么许多组织建议不要在Web存储中存储任何有价值或信任任何Web存储中的信息。这包括会话标识符和令牌。

作为一种存储机制,Web存储在传输过程中不强制执行任何安全标准。无论谁读取和使用Web存储,必须进行尽职调查以确保他们总是通过HTTPS发送JWT,绝不用HTTP。

JWT Cookie存储的安全性

Cookies,当使用带有HttpOnly的cookie标志时,通过JavaScript是无法访问的,并且对XSS是免疫的。你还可以设置安全的cookie标志来保证cokie仅通过HTTPS发送。这是过去利用cookie存储令牌或会话数据的主要原因之一。现代开发人员不愿使用cookie,因为它们通常要求状态被存储在服务器上,从而打破RESTful的最佳实践。如果你在cookie上存储JWT,cookie作为存储机制不用将状态存储在服务器上。这是因为JWT封装了所有服务器需要服务的请求。

然而,cookies容易受到不同类型的攻击:跨站点请求伪造(CSRF)。CSRF攻击是当一个恶意网站,电子邮件或博客导致用户在当前已验证用户的可信站点上的web浏览器中,执行一个有害的动作时发生的攻击。这是一个浏览器如何处理cookies的漏洞。cookie只能被发送到的允许的域中。默认情况下,这个是最初设置cookie的域。请求将发送一个cookie无论你在galaxies.com或hahagonnahackyou.com。

CSRF的工作试图引诱你到hahagonnahackyou.com。该网站将有一个img标记或JavaScript来模拟一个表单post到galaxies.com,并试图劫持你的会话,如果它仍然有效,就修改您的帐户。

例如:

<body>
 
  <!– CSRF 用img标签 –>
 
  <img href="http://galaxies.com/stars/pollux?transferTo=tom@stealingstars.com" />
 
  <!– 或用一个隐藏表单post–>
 
  <script type="text/javascript">
  $(document).ready(function() {
    window.document.forms[0].submit();
  });
  </script>
 
  <div style="display:none;">
    <form action="http://galaxies.com/stars/pollux" method="POST">
      <input name="transferTo" value="tom@stealingstars.com" />
    <form>
  </div>
</body>

两者都将发送cookie为galaxies.com,并可能导致未经授权的状态改变。使用同步令牌模式,CSRF是可以预防的。这听起来很复杂,但是所有现代的web框架都支持这个。

例如,AngularJS有一个解决方案去验证,只能由你的域才能访问cookie。直接来自AngularJS文档:

当执行XHR请求时,$http服务从cookie中读取令牌(默认,XSRF-TOKEN)并将其作为一个http头(X-XSRF-TOKEN)。因为只有JavaScript运行在你的域才可以读取cookie,你的服务器可以确信XHR来

自你的域中运行的JavaScript。通过包含一个xsrfToken JWT claim,你可以让这个CSRF保护无状态。

{
  "iss": "http://galaxies.com",
  "exp": 1300819380,
  "scopes": ["explorer", "solar-harvester", "seller"],
  "sub": "tom@andromeda.com",
  "xsrfToken": "d9b9714c-7ac0-42e0-8696-2dae95dbc33e"
}

如果我使用Stormpath SDK for AngularJS,获得无状态CSRF保护没有开发工作。

利用你的web应用程序框架的CSRF保护,使得cookies存储JWT绝对可靠。CSRF也可以从你的API中通过检查HTTP Referer和原始header部分阻止。CSRF攻击将有与应用程序无关的Referer和原始heade。

虽然他们更安全的存储你的JWT,cookies可能导致一些开发商头痛,这取决于你的应用程序的运转是否需要跨域访问。只是知道cookies有附加属性(域名/路径),可以对其进行修改,从而允许你指定cookie的发送位置。使用AJAX,你的服务器端也可以通知浏览器证书(包含Cookies)是否应该随着CORS请求发送。

结论

JWTs是一个很棒的身份验证机制。它们给你一个结构化的方式声明用户和它们可以访问的内容。可以对它们进行加密和签名来防止在客户端进行篡改,但魔鬼在于细节和你把它们存放在哪里。
Stormpath建议你把JWT存储到Web应用程序的cookies中,因为他们提供的额外的安全性,防止现代web框架CSRF的简单性。HTML5 Web存储很容易受到XSS攻击,有一个更大的攻击面积,一个成功的攻击会影响所有应用程序的用户。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容