阅读器中存储访问令牌的最佳通常
阅读器提供了各种耐久化数据的处置方案。当存储令牌时,您应该掂量存储选用与安保危险。
译自Best Practices for Storing Access Tokens in the Browser。
web运行程序不是静态站点,而是静态内容和灵活内容的精心组合。更经常出现的是,web运行程序逻辑在阅读器中运转。与从主机失掉一切内容不同,运行程序在阅读器中运转JavaScript,从后端API失掉数据,并相应地降级web运行程序出现。
为了包全数据访问,组织应该驳回OAuth 2.0。经过OAuth 2.0,JavaScript运行程序须要在对API的每个恳求中参与访问令牌。出于可用性要素,JavaScript运行程序通常不会按需恳求访问令牌,而是存储它。疑问是,如何在JavaScript中失掉这样的访问令牌?当您失掉一个令牌时,运行程序应该在哪里存储令牌,以便在须要时将其参与到恳求中?
本文讨论了阅读器中可用的各种存储处置方案,并突出了与每种选用相关的安保危险。在审查要挟之后,它形容了一种处置方案,以提供最佳的阅读器安保选项,用于必定与OAuth包全的API集成的JavaScript运行程序。
失掉访问令牌
在运行程序可以存储访问令牌之前,它须要先失掉一个令牌。的最佳通常倡导经过“授权码流”这一形式来失掉访问令牌: 授权码流是一个两步流程,首先从用户那里搜集一个授权容许——授权码,而后运行程序在后盾通道中用授权码替换访问令牌。这个恳求称为令牌恳求,例子如下:
const accessToken = await fetch(OAuthServerTokenEndpoint, {method: "POST",// token request with authorization code and PKCE// submits>
请留意,任何人都可以审核阅读器加载的资源,包括任何JavaScript代码。因此,任何用JavaScript成功的OAuth客户端都被以为是一个地下客户端——一个无法隐秘的客户端,因此在令牌恳求时期无法启出发份验证。但是,代码替换的证实密钥(Proof Key for Code Exchange,PKCE)提供了一种方法来确保地下客户端的授权码流的安保性。为了减轻与授权码相关的危险,在经常使用授权码流时,一直运行PKCE。
阅读器要挟
跨站恳求伪造(CSRF)
在跨站恳求伪造(CSRF)攻打中,恶意行为者会诈骗用户经过阅读器有意中口头恶意恳求。例如,攻打者可以在网站中嵌入精心设计的图像源字符串,以触发阅读器运转GET恳求,或许在恶意网站上参与表单,以触发POST恳求。在任何状况下,阅读器都或许会智能将cookie(包括单点登录cookie)参与到这样的恳求中。
CSRF攻打也被称为“会话骑乘”,由于攻打者通常会应用用户的经过身份验证的会话来启动恶意恳求。因此,攻打者可以默默地代表用户口头恳求,并调用用户可以调用的任何端点。但是,攻打者无法读取照应,所以他们通常以一次性性形态更改恳求为指标,如降级用户的明码。
跨站脚本(XSS)
跨站脚本(XSS)破绽准许攻打者将恶意的客户端代码注入到一个原本受信赖的网站中。例如,假设用户输入生成的输入没有被适当清算,web运行程序的任何中央都或许存在破绽。阅读器会智能在受信赖的网站的高低文中运转恶意代码。
XSS攻打可用于窃取访问令牌和刷新令牌,或口头CSRF攻打。不过,XSS攻打有一个时期窗口,由于它们只能在有限的时期段内运转,如令牌的有效期内,或许关上的选项卡存在破绽的时长。
即使在XSS无法用于检索访问令牌的状况下,攻打者也可以应用XSS破绽经过会话骑乘向有包全的Web端点发送经过身份验证的恳求。而后,攻打者可以伪装成用户,调用用户可以调用的任何后端端点,并形成重大侵害。
阅读器中的存储处置方案
运行程序收到访问令牌后,须要存储该令牌以在API恳求中经常使用它。阅读器中有多种方法可以耐久化数据。运行程序可以经常使用公用API(如Web存储API或IndexedDB)来存储令牌。运行程序也可以繁难地将令牌保管在内存中或将其放在cookie中。一些存储机制是耐久的,另一些在一段时期后或页面封锁或刷新后会被肃清。
一些处置方案跨选项卡共享数据,而其余处置方案仅限于选项卡。但是,本指南中引见的大少数方法都针对每个源存储数据。因此,关于任何相关讨论来说,了解一些概念很有协助:origin和site。
某个(Web)资源的origin是其URL的scheme、hostname和port。例如,和具备相反的origin,由于它们共享scheme(https)、hostname(example.com)和端口(自动端口)。它们的origin为,与或不同,由于它们在端口和主机名上有所不同。
相比之下,一个site比资源的origin要大。一个站点是为一组资源提供服务的Web运行程序的通用称号。繁难地说,一个站点是scheme和domain name,如。虽然和有不同的origin(不同的主机名和端口),但它们是相反的站点,由于它们托管在同一个域名(example.com)上并经常使用相反的scheme(https)。(从技术上讲,这个定义还有纤细差异,但这个简化的说法有助于解释这个概念)。
本地存储
本地存储是经过Web存储API中的全局localStorage对象以JavaScript访问的。本地存储中的数据在阅读器选项卡和会话之间可用,也就是说它不会过时或在阅读器封锁时被删除。因此,经过localStorage存储的数据可以在运行程序的一切选项卡中访问。因此,在本地存储中存储令牌十分迷人。
localStoragesetItem accessTokenlet accessToken localStoragegetItem
每当运行程序调用API时,它都会从存储中失掉令牌并手动参与到恳求中。但是,由于本地存储可以经过JavaScript访问,这象征着该处置方案也容易遭到跨站脚本(XSS)攻打。
假设您在本地存储中经常使用access token,并且攻打者设法在您的运行程序中运转外部JavaScript代码,那么攻打者可以窃取任何令牌并间接调用API。此外,XSS还准许攻打者操作运行程序中的本地存储数据,这象征着攻打者可以更改令牌。
请留意,本地存储中的数据会终身存储,这象征着存储在其中的任何令牌会驻留在用户的设施(笔记本电脑、电脑、手机或其余设施)的文件系统上,即使阅读器封锁后也可以被其余运行程序访问。因此,在经常使用localStorage时,请思考终端安保性。思考并防止阅读器之外的攻打向量,如恶意软件、被盗设施或磁盘。
依据上述讨论,请遵照以下倡导:
会话存储
会话存储是Web存储API提供的另一种存储机制。与本地存储不同,经常使用sessionStorage对象存储的数据在选项卡或阅读器封锁时会被肃清。此外,session存储中的数据在其余选项卡中无法访问。只要选项卡和origin中的JavaScript代码可以经常使用相反的会话存储启动读取和写入。
sessionStoragesetItem accessTokenlet accessToken sessionStoragegetItem
与本地存储相比,会话存储可以被以为更安保,由于阅读器会在窗口封锁时智能删除任何令牌。此外,由于会话存储不在选项卡之间共享,攻打者无法从另一个选项卡(或窗口)读取令牌,这缩小了XSS攻打的影响。
在通常中,经常使用sessionStorage存储令牌的关键安保疑问是XSS。假设您的运行程序容易遭到XSS攻打,攻打者可以从存储中提取令牌并在API调用中重放它。因此,会话存储不适宜存储敏感数据,如令牌。
IndexedDB是索引数据库API的缩写。它是一个用于在阅读器中异步存储少量数据的API。但是,在存储令牌时,这个阅读器API提供的性能和容量通常不是必需的。由于运行程序在每次API调用中都发送令牌,最好是使令牌的大小最小化。
因此,不要在IndexedDB中存储访问令牌或其余敏感数据。IndexedDB更适宜用于运行程序脱机上班所需的数据,如图像。
内存
存储令牌的一个相当安保的方法是将其保管在内存中。与其余方法相比,令牌不存储在文件系统中,从而减轻了与设施文件系统相关的危险。
最佳通常倡导在内存中存储令牌时将其保管在闭包中。例如,您可以定义一个独自的方法来经常使用令牌调用API。它不会向主运行程序(主线程)走漏令牌。上方的摘录显示了如何在JavaScript中经常使用内存处置令牌的示例。
protectedCallstokenResponse {const accessToken tokenResponseaccessToken {getOrders: {const req new Requestreqheaders accessToken req}}}const apiClient protectedCallstokenResponseapiClientgetOrders
请留意,攻打者或许无法在失掉令牌后间接访问令牌,因此或许无法间接经常使用令牌调用API。即使如此,经过持有令牌援用的apiClient,他们可以随时经过apiClient调用API。但是,任何此类攻打都限于选项卡关上并且接口提供的性能的时段。
除了与潜在的XSS破绽相关的安保疑问外,在内存中坚持令牌的最大缺陷是页面重载气节牌会失落。而后,运行程序必定失掉一个新令牌,这或许会触发新的用户身份验证。安保的设计招思考到用户体验。
经常使用服务上班者的体系结构经过在独立的线程中运转令牌处感性能来减轻可用性疑问,该线程与主网页分别。服务上班者实践上充任运行程序、阅读器和网络之间的代理。因此,它们可以阻拦恳求和照应,例如缓存数据和启用离线访问,或许失掉和参与令牌。
在经常使用JavaScript闭包或服务上班者处置令牌和API恳求时,XSS攻打或许会针对OAuth流程,如回调流或静默流来失掉令牌。它们可以敞开注册并绕过任何服务上班者,或许经常使用原型污染“实时读取令牌”经过笼罩诸如window.fetch之类的方法。因此,请出于繁难而不是安保性思考JavaScript闭包和服务上班者。
Cookie是存储在阅读器中的数据片段。由设计,阅读器会将cookie参与到对主机的每个恳求中。因此,运行程序必定审慎经常使用cookie。假设未经细心性能,阅读器或许会在跨站恳求时追加cookie,并准许跨站恳求伪造(CSRF)攻打。
Cookie具备控制其安保属性的属性。例如,SameSite属性可以协助缓解CSRF攻打的危险。当一个cookie的SameSite属性设置为Strict时,阅读器只会将其参与到源自并指标与cookie的源站点相反的恳求中。当恳求嵌入在任何第三方网站中时,阅读器不会参与cookie,例如经过链接。
您可以经过JavaScript设置和检索cookie。但是,当经常使用JavaScript读取cookie时,运行程序会变得容易遭到XSS攻打(除了CSRF之外)。因此,首选的选用是让后端组件设置cookie并将其标志为HttpOnly。该标志可以缓解经过XSS攻打暴露数据的疑问,由于它批示阅读器cookie不能经过JavaScript访问。
为防止cookie经过两边人攻打暴露,这或许造成会话劫持,cookie应仅经过加密衔接(HTTPS)发送。要批示阅读器仅在HTTPS恳求中发送cookie,必定将Secure属性设置为cookie。
Cookie:tokenmyvalueSameSiteStrictSecureHttpOnly
与阅读器中的任何其余终身存储处置方案一样,cookie或许会驻留在文件系统中,即使阅读器已封锁(例如,cookie不用过时,或许阅读器可以将会话cookie作为复原会话性能的一局部保管)。为了减轻从文件系统中窃取令牌的危险,只能在cookie中存储加密的令牌。因此,后端组件只能在Set-Cookie头中前往加密的令牌。
要挟矩阵
下表总结了阅读器中存储处置方案的要挟评价,关键要挟向量标志为白色。橙色要挟须要除Web技术之外的缓解措施。绿色要挟曾经或可以经过适当的设置成功消弭。
无论攻打者何时设法窃取令牌,只需令牌有效,他们就可以独立于用户和运行程序经常使用访问令牌。假设攻打者设法窃取刷新令牌,他们可以显着延伸攻打时期并参与侵害,由于他们可以续新访问令牌。黑客甚至可以将攻打裁减到除JavaScript运行程序经常使用的API之外的其余API。例如,攻打者可以尝试重放访问令牌并应用不同API中的破绽。
被盗的访问令牌或许会形成重大侵害,XSS依然是Web运行程序的关键疑问。因此,防止在客户端代码可以访问的中央存储访问令牌。相反,将访问令牌存储在cookie中。当经常使用适当的属性性能cookie时,阅读器暴露访问令牌的危险为零。而后,XSS攻打与在同一站点上的会话劫持攻打相当。
经常使用Cookie的OAuth语义
Cookie依然是传输令牌和充任API凭据的最佳选用,由于即使攻打者成功应用XSS破绽,也无法从cookie中检索访问令牌。但是,为了做到这一点,cookie必定适当性能。
首先,将cookie标志为HttpOnly,以便它们无法经过JavaScript访问,以处置XSS攻打的危险。另一个关键属性是Secure标志,它确保cookie仅经过HTTPS发送,以减轻两边人攻打。
其次,颁发持久的只在几分钟内有效的访问令牌。在最坏的状况下,具备最小有效期的访问令牌只能在可以接受的短时期内被滥用。通常以为15分钟的有效期是适宜的。让cookie和令牌的过时时期大抵相反。
第三,将令牌视为敏感数据。只在cookie中存储加密令牌。假设攻打者设法失掉加密令牌,他们将无法从中解析任何数据。攻打者也无法将加密的令牌重放就任何其余API,由于其余API无法解密令牌。加密令牌只是限度了被盗令牌的影响。
第四,在发送API凭据时要限度性强。只向须要API凭据的资源发送cookie。这象征着确保阅读器只在实践须要访问令牌的API调用中参与cookie。为此,cookie须要有适当的设置,比如SameSite=Strict、指向API端点域的域属性和门路。
最后,在经常使用刷新令牌时,请确保将它们存储在自己的cookie中。没有必要在每个API恳求中都发送它们,所以请确保不是这种状况。刷新令牌必定只在刷新过时的访问令牌时参与。这象征着蕴含刷新令牌的cookie与蕴含访问令牌的cookie有稍微不同的设置。
令牌处置程序形式
在JavaScript客户端中为OAuth提供最佳通常准则的设计形式是令牌处置程序形式。它遵照OAuth 2.0 for Browser-Based Apps中形容的BFF(backend for frontend)方法。该形式引入了一个后端组件,能够收回带有加密令牌和上述必要属性的cookie。
后端组件的责任是:
令牌处置程序形式定义了一个BFF,它为在阅读器中运转的运行程序形象了OAuth。换句话说,令牌处置程序形式倡导一个JavaScript运行程序可以用来认证用户并安保地调用API的API。为此,该形式经常使用cookie来存储和发送访问令牌。
令牌处置程序是一个后端组件,例如可以驻留在API网关中。它由两局部组成:
OAuth代理失掉令牌后,它会收回带有以下属性的cookie:
由于令牌处置程序是一个后端组件,所以OAuth代理是一个隐秘的客户端,可以向授权主机启出发份验证(与地下的JavaScript客户端相比)。这象征着为了取得令牌,OAuth代理须要启出发份验证。因此,攻打者须要失掉客户端凭据能力成功失掉新令牌。在JavaScript中运转静默流而没有客户端凭据将失败。
为了令牌处置程序形式能够上班,JavaScript运行程序和令牌处置程序组件必定部署在同一站点上(换句话说,它们必定在同一域中运转)。否则,由于cookie上的同站限度,阅读器不会将令牌cookie参与到API恳求中。
要失掉数据,JavaScript运行程序只需经过OAuth代理调用API:
const response await {credentials: }
阅读器会智能将cookie参与到恳求中。在上方的示例中,阅读器将cookie蕴含在跨域恳求中。但是,由于cookie属性SameSite=Strict,阅读器只会将cookie参与到同一站点(同一域)的跨域恳求中。
OAuth代了解密cookie并将令牌参与到抢先API。cookie属性确保阅读器仅将cookie参与到HTTPS恳求中,以确保它们在传输环节中是安保的。由于令牌是加密的,它们在劳动时也是安保的。而后令牌用于安保访问API。
总结
经常使用OAuth和访问令牌可以最好地包全API访问。但是,JavaScript运行程序处于不利位置。阅读器中没有安保的令牌存储处置方案。一切可用的处置方案在某种水平上都容易遭到XSS攻打。因此,确保任何运行程序安保的首要义务应该是防止XSS破绽。
令牌处置程序形式经过在JavaScript无法访问的cookie中存储加密令牌来缓解XSS危险。它将Web关注点与API关注点分别,并提供指点,经常使用成熟的Web技术加固JavaScript运行程序,而不会破坏Web架构。检查令牌处置程序形式的具体形容,并探求各种示例。