动态标准 - 最后更新于 2024 年 9 月 12 日
所有当前引擎都支持。
本节是非规范性的。
本规范引入了两种相关机制,类似于 HTTP 会话 Cookie,用于在客户端存储键值对。 [COOKIES]
第一个机制用于用户执行单个交易的场景,但用户可能在不同的窗口同时执行多个交易。
Cookie 无法很好地处理这种情况。例如,用户可能在两个不同的窗口中购买机票,使用同一个网站。如果网站使用 Cookie 来跟踪用户正在购买的机票,那么当用户在两个窗口中点击页面时,当前正在购买的机票会从一个窗口“泄露”到另一个窗口,这可能会导致用户购买两张同一个航班的机票而没有注意到。
为了解决这个问题,本规范引入了 sessionStorage
获取器。网站可以将数据添加到会话存储中,该数据可在同一网站在该窗口中打开的任何页面中访问。
例如,页面可以有一个复选框,用户勾选该复选框表示他们想要购买保险
< label >
< input type = "checkbox" onchange = "sessionStorage.insurance = checked ? 'true' : ''" >
I want insurance on this trip.
</ label >
然后,后续页面可以通过脚本检查用户是否勾选了复选框
if ( sessionStorage. insurance) { ... }
如果用户在网站上打开了多个窗口,每个窗口都会拥有其独立的会话存储对象副本。
第二个存储机制旨在实现跨多个窗口的存储,并在当前会话结束后继续存在。特别是,网络应用程序可能希望出于性能原因,将兆字节的用户数据(例如,整个用户创作的文档或用户的邮箱)存储在客户端。
同样,Cookie 无法很好地处理这种情况,因为它们会与每个请求一起传输。
获取器用于访问页面的本地存储区域。localStorage
example.com 网站可以通过将其页面底部添加以下代码来显示用户加载其页面的次数
< p >
You have viewed this page
< span id = "count" > an untold number of</ span >
time(s).
</ p >
< script >
if ( ! localStorage. pageLoadCount)
localStorage. pageLoadCount = 0 ;
localStorage. pageLoadCount = parseInt( localStorage. pageLoadCount) + 1 ;
document. getElementById( 'count' ). textContent = localStorage. pageLoadCount;
</ script >
每个网站都有其独立的存储区域。
获取器提供对共享状态的访问。本规范没有定义与多进程用户代理中的其他代理集群的交互,作者应假设没有锁定机制。例如,网站可以尝试读取键的值,增加其值,然后使用新值写入该值,并将新值用作会话的唯一标识符;如果网站在两个不同的浏览器窗口中同时执行此操作两次,它可能会使用相同的“唯一”标识符来标识这两个会话,这可能会产生灾难性的后果。localStorage
所有当前引擎都支持。
Storage
接口[Exposed =Window ]
interface Storage {
readonly attribute unsigned long length ;
DOMString ? key (unsigned long index );
getter DOMString ? getItem (DOMString key );
setter undefined setItem (DOMString key , DOMString value );
deleter undefined removeItem (DOMString key );
undefined clear ();
};
storage.length
所有当前引擎都支持。
返回键值对的数量。
storage.key (n)
所有当前引擎都支持。
返回第 n 个键的名称,如果 n 大于或等于键值对的数量,则返回 null。
value = storage.getItem (key)
所有当前引擎都支持。
value = storage[key]
返回与给定 key 关联的当前值,如果给定 key 不存在,则返回 null。
storage.setItem (key, value)
所有当前引擎都支持。
storage[key] = value
将由 key 标识的对的值设置为 value,如果以前不存在与 key 关联的键值对,则创建一个新的键值对。
如果无法设置新值,则抛出 "QuotaExceededError
" DOMException
异常。(设置可能会失败,例如,如果用户已为该网站禁用了存储,或者如果已超过配额。)
storage.removeItem (key)
所有当前引擎都支持。
delete
storage[key]
如果存在具有给定 key 的键值对,则删除具有给定 key 的键值对。
storage.clear()
所有当前引擎都支持。
如果存在任何键值对,则删除所有键值对。
对象有一个关联的Storage
local
" 或 "session
"。要 重新排序
对象 storage,请以 实现定义的方式重新排序 storage 的 映射 的 条目。Storage
不幸的是,迭代顺序没有定义,并且在大多数变异后会发生变化。
要 广播
对象 storage,在给定 key、oldValue 和 newValue 的情况下,执行以下步骤Storage
令 thisDocument 为 storage 的 相关全局对象 的 关联的 Document
。
令 url 为 thisDocument 的 URL。
令 remoteStorages 为所有
对象,不包括 storage,其Storage
并且,如果 类型 为 "session
",其 相关设置对象 的 关联的 Document
的 节点可导航 的 可遍历可导航 为 thisDocument 的 节点可导航 的 可遍历可导航。
对于每个 remoteStorage 的 remoteStorages:在给定 remoteStorage 的 相关全局对象 的 DOM 操作任务源 上 排队一个全局任务,以 在 remoteStorage 的 相关全局对象 上触发名为 storage
的事件,使用 StorageEvent
,其中 key
初始化为 key,oldValue
初始化为 oldValue,newValue
初始化为 newValue,url
初始化为 url,storageArea
初始化为 remoteStorage。
The length
getter steps are to return this's map's size.
The key(index)
method steps are
在Storage
对象 storage 上支持的属性名称 是在 storage's map 上运行 获取键 的结果。
The getItem(key)
method steps are
The setItem(key, value)
method are
设 oldValue 为 null。
设 reorder 为 true。
如果无法存储 value,则抛出"QuotaExceededError
" DOMException
异常。
The removeItem(key)
method steps are
The clear()
method steps are
sessionStorage
getterinterface mixin WindowSessionStorage {
readonly attribute Storage sessionStorage ;
};
Window includes WindowSessionStorage ;
window.sessionStorage
所有当前引擎都支持。
返回与该 window 的来源的会话存储区域关联的Storage
对象。
如果文档
的来源 是不透明来源,或者请求违反了策略决定(例如,如果用户代理配置为不允许页面持久化数据),则抛出 "SecurityError
" DOMException
。
文档
对象有一个关联的会话存储持有者,它可以是 null 或Storage
对象。它最初为 null。
The sessionStorage
getter steps are
如果 this's 关联的 Document
's 会话存储持有者 不为 null,则返回 this's 关联的 Document
's 会话存储持有者。
设 map 为使用 this's 相关设置对象 和 "sessionStorage
" 运行 获取会话存储瓶地图 的结果。
如果 map 为失败,则抛出 "SecurityError
" DOMException
。
将 this's 关联的 Document
's 会话存储持有者 设置为 storage。
返回 storage。
在创建新的辅助浏览上下文和文档 之后,会话存储被复制 过去。
localStorage
getterinterface mixin WindowLocalStorage {
readonly attribute Storage localStorage ;
};
Window includes WindowLocalStorage ;
window.localStorage
所有当前引擎都支持。
返回与 window 的来源的本地存储区域关联的Storage
对象。
如果文档
的来源 是不透明来源,或者请求违反了策略决定(例如,如果用户代理配置为不允许页面持久化数据),则抛出 "SecurityError
" DOMException
。
文档
对象有一个关联的本地存储持有者,它可以是 null 或Storage
对象。它最初为 null。
The localStorage
getter steps are
如果 this's 关联的 Document
's 本地存储持有者 不为 null,则返回 this's 关联的 Document
's 本地存储持有者。
如果 map 为失败,则抛出 "SecurityError
" DOMException
。
将 this's 关联的 Document
's 本地存储持有者 设置为 storage。
返回 storage。
StorageEvent
接口所有当前引擎都支持。
[Exposed =Window ]
interface StorageEvent : Event {
constructor (DOMString type , optional StorageEventInit eventInitDict = {});
readonly attribute DOMString ? key ;
readonly attribute DOMString ? oldValue ;
readonly attribute DOMString ? newValue ;
readonly attribute USVString url ;
readonly attribute Storage ? storageArea ;
undefined initStorageEvent (DOMString type , optional boolean bubbles = false , optional boolean cancelable = false , optional DOMString ? key = null , optional DOMString ? oldValue = null , optional DOMString ? newValue = null , optional USVString url = "", optional Storage ? storageArea = null );
};
dictionary StorageEventInit : EventInit {
DOMString ? key = null ;
DOMString ? oldValue = null ;
DOMString ? newValue = null ;
USVString url = "";
Storage ? storageArea = null ;
};
event.key
返回正在更改的存储项的键。
event.oldValue
返回正在更改其值的存储项的键的旧值。
event.newValue
返回正在更改其值的存储项的键的新值。
event.url
返回发生存储项更改的文档的URL。
event.storageArea
返回受影响的Storage
对象。
key
、oldValue
、newValue
、url
和 storageArea
属性必须返回它们被初始化时的值。
initStorageEvent(type, bubbles, cancelable, key, oldValue, newValue, url, storageArea)
方法必须以类似于同名initEvent()
方法的方式初始化事件。 [DOM]
第三方广告商(或任何能够将内容分发到多个网站的实体)可以使用存储在其本地存储区域中的唯一标识符跨多个会话跟踪用户,构建用户兴趣概况,以便进行高度针对性的广告。结合了解用户真实身份的网站(例如需要身份验证凭据的电子商务网站),这可以让压制性团体比在一个纯粹匿名网络使用环境中更准确地定位个人。
有许多技术可以用来降低用户追踪的风险。
用户代理可以将对localStorage
对象的访问限制为源自活动文档域名的脚本顶级可遍历,例如,拒绝对在iframe
中运行的其他域名的页面上的 API 的访问。
用户代理可以(可能以用户配置的方式)在一段时间后自动删除存储的数据。
例如,用户代理可以配置为将第三方本地存储区域视为仅限会话的存储,在用户关闭所有可以访问它的可导航时删除数据。
这可以限制网站跟踪用户的可能性,因为网站只能在用户自己向网站进行身份验证时(例如,通过购买商品或登录服务)才能跨多个会话跟踪用户。
然而,这也降低了 API 作为长期存储机制的用处。如果用户不完全了解数据过期带来的影响,它也会将用户的安全置于风险之中。
如果用户试图通过清除 Cookie 而没有清除本地存储区域中存储的数据来保护他们的隐私,那么网站可以通过使用这两个功能作为彼此的冗余备份来阻止这些尝试。用户代理应该以帮助用户了解这种可能性并使他们能够同时删除所有持久存储功能中的数据的方式来呈现清除这些功能的界面。 [COOKIES]
用户代理可以允许网站以不受限制的方式访问会话存储区域,但要求用户授权访问本地存储区域。
用户代理可以记录包含来自导致数据存储的第三方来源的内容的网站的来源。
如果然后使用此信息来显示当前位于持久存储中的数据的视图,它将允许用户就需要修剪持久存储的哪些部分做出明智的决定。结合黑名单(“删除此数据并阻止此域名再次存储数据”),用户可以将持久存储的使用限制在他们信任的网站。
用户代理可以允许用户共享他们的持久存储域黑名单。
这将允许社区共同行动来保护他们的隐私。
虽然这些建议可以防止这个 API 被简单地用于用户追踪,但它们并不能完全阻止它。在一个域名内,网站可以在一个会话中继续跟踪用户,然后将所有这些信息以及网站获得的任何识别信息(姓名、信用卡号码、地址)传递给第三方。如果第三方与多个网站合作以获取此类信息,则仍然可以创建用户概况。
然而,即使没有任何来自用户代理的合作,用户追踪在某种程度上也是可能的,例如通过在 URL 中使用会话标识符,这是一种已经普遍用于无害目的但很容易被重新用于用户追踪的技术(即使是追溯性地)。然后,可以使用访问者的 IP 地址和其他用户特定数据(例如用户代理标头和配置设置)将这些信息与其他网站共享,将独立的会话合并到连贯的用户概况中。
用户代理应该将持久存储的数据视为可能敏感的;电子邮件、日历约会、健康记录或其他机密文件很可能存储在这个机制中。
为此,用户代理应该确保在删除数据时,立即将其从底层存储中删除。
由于 DNS 欺骗攻击的可能性,不能保证声称位于特定域名的主机确实来自该域名。为了减轻这种风险,页面可以使用 TLS。使用 TLS 的页面可以确保只有用户、代表用户工作的软件以及使用 TLS 并具有识别它们来自相同域名的证书的其他页面可以访问其存储区域。
不同的作者共享一个主机名,例如在现已停用的geocities.com
上托管内容的用户,他们共享一个本地存储对象。没有功能可以限制路径名によるアクセス。因此,共享主机上的作者应避免使用这些功能,因为其他作者可以轻松地读取数据并覆盖数据。
即使提供了路径限制功能,通常的 DOM 脚本安全模型也会使绕过此保护并从任何路径访问数据变得微不足道。
在实现这些持久存储功能时,两个主要风险是让恶意网站读取来自其他域名的信息,以及让恶意网站写入然后从其他域名读取的信息。
让第三方网站读取不应该从其域名读取的数据会导致信息泄漏。例如,一个用户在一个域名上的购物愿望清单可以被另一个域名用于针对性广告;或者用户在一个文字处理网站上存储的正在进行的机密文档可以被竞争公司的网站检查。
让第三方网站将数据写入其他域名的持久存储中会导致信息欺骗,这同样危险。例如,一个恶意网站可以将商品添加到用户的愿望清单中;或者一个恶意网站可以将用户的会话标识符设置为已知 ID,恶意网站然后可以使用该 ID 跟踪用户在受害者网站上的操作。
因此,严格遵循本规范中描述的来源模型对于用户安全至关重要。