HTML5 Security Cheat Sheet / HTML5安全速查表¶
Introduction / 介绍¶
The following cheat sheet serves as a guide for implementing HTML 5 in a secure fashion.
以下速查表作为在安全方式中实现HTML 5的指南。
Communication APIs / 通信API¶
Web Messaging / Web消息传递¶
Web Messaging (also known as Cross Domain Messaging) provides a means of messaging between documents from different origins in a way that is generally safer than the multiple hacks used in the past to accomplish this task. However, there are still some recommendations to keep in mind:
Web消息传递(也称为跨域消息传递)提供了一种在来自不同来源的文档之间进行消息传递的方法,这种方法通常比过去完成此任务所使用的多种黑客攻击方式更安全。然而,还有一些建议需要牢记:
- When posting a message, explicitly state the expected origin as the second argument to
postMessagerather than*in order to prevent sending the message to an unknown origin after a redirect or some other means of the target window's origin changing.
中文:在发送消息时,应明确指定预期的来源作为postMessage的第二个参数,而不是*,以防止在重定向或其他导致目标窗口来源改变的手段之后将消息发送到未知来源。 - The receiving page should always:
中文:接收页面应始终:- Check the
originattribute of the sender to verify the data is originating from the expected location.
中文:检查发送者的origin属性,以验证数据是否来自预期的位置。 - Perform input validation on the
dataattribute of the event to ensure that it's in the desired format.
中文:在事件的data属性上执行输入验证,以确保其符合期望的格式。
- Check the
- Don't assume you have control over the
dataattribute. A single Cross Site Scripting flaw in the sending page allows an attacker to send messages of any given format.
中文:不要假设你对data属性有控制权。发送页面中的单个 跨站脚本攻击 漏洞允许攻击者发送任何格式的消息。 - Both pages should only interpret the exchanged messages as data. Never evaluate passed messages as code (e.g. via
eval()) or insert it to a page DOM (e.g. viainnerHTML), as that would create a DOM-based XSS vulnerability. For more information see DOM based XSS Prevention Cheat Sheet.
中文:两个页面应仅将交换的消息解释为 数据。切勿将传递的消息作为代码(例如通过eval())评估,或将其插入页面 DOM(例如通过innerHTML),因为这会创建基于 DOM 的 XSS 漏洞。更多信息请参阅 基于 DOM 的 XSS 防范备忘单。 - To assign the data value to an element, instead of using a insecure method like
element.innerHTML=data;, use the safer option:element.textContent=data;
中文:要将数据值分配给元素,而不是使用不安全的方法如element.innerHTML=data;,请使用更安全的选项:element.textContent=data; - Check the origin properly exactly to match the FQDN(s) you expect. Note that the following code:
if(message.origin.indexOf(".owasp.org")!=-1) { /* ... */ }is very insecure and will not have the desired behavior asowasp.org.attacker.comwill match.
中文:请正确检查来源,确保与您期望的 FQDN(s) 完全匹配。请注意,以下代码:if(message.origin.indexOf(".owasp.org")!=-1) { /* ... */ }非常不安全,并且不会产生预期的行为,因为owasp.org.attacker.com也会匹配。 - If you need to embed external content/untrusted gadgets and allow user-controlled scripts (which is highly discouraged), please check the information on sandboxed frames.
中文:如果您需要嵌入外部内容/不受信任的小工具并允许用户控制的脚本(这强烈不建议),请检查有关 沙盒框架 的信息。
Cross Origin Resource Sharing / 跨源资源共享¶
- Validate URLs passed to
XMLHttpRequest.open. Current browsers allow these URLs to be cross domain; this behavior can lead to code injection by a remote attacker. Pay extra attention to absolute URLs.
中文:验证传递给XMLHttpRequest.open的 URL。当前浏览器允许这些 URL 跨域;这种行为可能导致远程攻击者进行代码注入。特别注意绝对 URL。 - Ensure that URLs responding with
Access-Control-Allow-Origin: *do not include any sensitive content or information that might aid attacker in further attacks. Use theAccess-Control-Allow-Originheader only on chosen URLs that need to be accessed cross-domain. Don't use the header for the whole domain.
中文:确保响应Access-Control-Allow-Origin: *的 URL 不包含任何敏感内容或可能帮助攻击者进一步攻击的信息。仅将Access-Control-Allow-Origin标头用于需要跨域访问的选定 URL。不要将该标头用于整个域名。 - Allow only selected, trusted domains in the
Access-Control-Allow-Originheader. Prefer allowing specific domains over blocking or allowing any domain (do not use*wildcard nor blindly return theOriginheader content without any checks).
中文:仅允许选定的、受信任的域名在Access-Control-Allow-Origin标头中。优先允许特定域名,而不是阻止或允许任何域名(不要使用*通配符,也不要盲目返回Origin标头内容而不进行任何检查)。 - Keep in mind that CORS does not prevent the requested data from going to an unauthorized location. It's still important for the server to perform usual CSRF prevention.
中文:请注意,CORS 并不能阻止请求数据发送到未经授权的位置。服务器执行常规 CSRF 预防措施仍然很重要。 - While the Fetch Standard recommends a pre-flight request with the
OPTIONSverb, current implementations might not perform this request, so it's important that "ordinary" (GETandPOST) requests perform any access control necessary.
中文:虽然 Fetch 标准 建议使用OPTIONS动词进行预检请求,但当前实现可能不会执行此请求,因此对于普通(GET和POST)请求执行必要的访问控制非常重要。 - Discard requests received over plain HTTP with HTTPS origins to prevent mixed content bugs.
中文:丢弃通过 HTTPS 原因接收的纯 HTTP 请求,以防止混合内容错误。 - Don't rely only on the Origin header for Access Control checks. Browser always sends this header in CORS requests, but may be spoofed outside the browser. Application-level protocols should be used to protect sensitive data.
中文:不要仅依赖 Origin 头部进行访问控制检查。浏览器在 CORS 请求中始终发送此头部,但可能在外部被伪造。应使用应用层协议来保护敏感数据。
WebSockets¶
- Check out WebSocket Security Cheat Sheet to learn about WebSocket specific protections.
中文:查看 WebSocket 安全性 Cheatsheet 了解 WebSocket 特定的保护措施。
Server-Sent Events / 服务器发送事件¶
- Validate URLs passed to the
EventSourceconstructor. Cross-origin connections use CORS and require permission from the event-stream server.
中文:验证传递给EventSource构造函数的 URL。跨源连接使用 CORS 并需要从事件流服务器获取权限。 - As mentioned before, process the messages (
event.data) as data and never evaluate the content as HTML or script code.
中文:如前所述,将消息(event.data)作为数据处理,永远不要将其内容作为 HTML 或脚本代码评估。 - Always check the origin attribute of the message (
event.origin) to ensure the message is coming from a trusted domain. Use an allow-list approach.
中文:始终检查消息的 origin 属性(event.origin),以确保消息来自受信任的域。使用允许列表方法。
Storage APIs / 存储API¶
Local Storage / 本地存储¶
- Also known as Offline Storage, Web Storage. Underlying storage mechanism may vary from one user agent to the next. In other words, any authentication your application requires can be bypassed by a user with local privileges to the machine on which the data is stored. Therefore, it's recommended to avoid storing any sensitive information in local storage where authentication would be assumed.
中文:也称为离线存储、Web存储。底层存储机制可能因用户代理而异。换句话说,任何应用程序所需的身份验证都可以被对存储数据的机器具有本地权限的用户绕过。因此,建议避免在本地存储中存储任何敏感信息,因为在这种情况下会假定身份验证。 - Due to the browser's security guarantees it is appropriate to use local storage where access to the data is not assuming authentication or authorization.
中文:由于浏览器的安全保证,在不假定数据访问需要身份验证或授权的情况下,使用本地存储是合适的。 - Use the object sessionStorage instead of localStorage if persistent storage is not needed. sessionStorage object is available only to that window/tab until the window is closed.
中文:如果不需要持久存储,请使用 sessionStorage 对象而不是 localStorage。sessionStorage 对象仅在窗口/标签页关闭之前对该窗口/标签页可用。 - A single Cross Site Scripting can be used to steal all the data in these objects, so again it's recommended not to store sensitive information in local storage.
中文:单个 跨站脚本攻击 可用于窃取这些对象中的所有数据,因此再次建议不要在本地存储中存储敏感信息。 - A single Cross Site Scripting can be used to load malicious data into these objects too, so don't consider objects in these to be trusted.
中文:单个 跨站脚本攻击 也可用于将这些恶意数据加载到这些对象中,因此不要认为这些对象中的内容是可信的。 - Pay extra attention to "localStorage.getItem" and "setItem" calls implemented in HTML5 page. It helps in detecting when developers build solutions that put sensitive information in local storage, which can be a severe risk if authentication or authorization to that data is incorrectly assumed.
中文:请特别注意HTML5页面中实现的“localStorage.getItem”和“setItem”调用。这有助于检测开发者在本地存储中放置敏感信息的解决方案,如果错误地假定对数据的身份验证或授权,这可能是一个严重风险。 - Do not store session identifiers in local storage as the data is always accessible by JavaScript. Cookies can mitigate this risk using the
httpOnlyflag.
中文:不要在本地存储中存储会话标识符,因为JavaScript始终可以访问这些数据。可以使用httpOnly标志的cookie来减轻这种风险。 - There is no way to restrict the visibility of an object to a specific path like with the attribute path of HTTP Cookies, every object is shared within an origin and protected with the Same Origin Policy. Avoid hosting multiple applications on the same origin, all of them would share the same localStorage object, use different subdomains instead.
中文:无法像HTTP Cookies的路径属性那样限制对象的可见性到特定路径,每个对象都在一个源内共享,并受到同源策略的保护。避免在同一源上托管多个应用程序,所有这些应用程序都会共享同一个localStorage对象,而是使用不同的子域。
Client-side databases / 客户端数据库¶
- Web SQL Database was deprecated by the W3C in 2010 and is removed from all major browsers: Chromium dropped support in version 119 (October 2023) and Safari/Firefox never shipped it for third-party origins. Do not use Web SQL. If you specifically need an SQL interface in the browser, prefer running an embedded engine such as the official SQLite WebAssembly build (
sqlite-wasm), backed by IndexedDB or the Origin Private File System (OPFS) for persistence.
中文:Web SQL 数据库于 2010 年被 W3C 弃用,并被从所有主流浏览器中移除:Chromium 从版本 119(2023 年 10 月)开始不再支持,Safari 和 Firefox 也从未为第三方来源提供支持。请勿使用 Web SQL。如果您在浏览器中特别需要 SQL 接口,请优先考虑运行嵌入式引擎,如官方的 SQLite WebAssembly 构建 (sqlite-wasm),并支持 IndexedDB 或 Origin Private File System (OPFS) 以实现持久性。 - The current standard for client-side structured storage is IndexedDB, a transactional key-value store that has been a W3C Recommendation since 2015 and is supported in all evergreen browsers.
中文:客户端结构化存储的当前标准是 IndexedDB,这是一个自 2015 年以来一直是 W3C 建议的事务性键值存储,并且所有主流浏览器都支持。 - Underlying storage mechanisms vary across user agents and operating systems. A user or process with access to the browser profile can read or modify stored data; do not assume IndexedDB provides confidentiality. Avoid storing session tokens, credentials, or other secrets there. If sensitive data must be stored locally, design encryption and key management for the device-access threat. A non-extractable
CryptoKeyrestricts Web Crypto export operations, but does not guarantee protection of persisted keys from device access or prevent hostile scripts from using the key.
中文:底层存储机制在不同用户代理和操作系统之间有所不同。具有访问浏览器配置文件的用户或进程可以读取或修改存储的数据;不要假设 IndexedDB 提供机密性。避免在其中存储会话令牌、凭证或其他秘密。如果必须在本地上存储敏感数据,请为设备访问威胁设计加密和密钥管理。不可提取的CryptoKey限制了 Web Crypto 导出操作,但 并不能保证持久化密钥免受设备访问或防止恶意脚本使用该密钥。 - A single Cross-Site Scripting vulnerability can read or write any data in IndexedDB; treat its contents as untrusted input on read.
中文:单个 跨站脚本攻击 漏洞可以读取或写入 IndexedDB 中的任何数据;在读取时将其内容视为不受信任的输入。 - Apply the same input validation and output encoding rules to data coming from IndexedDB as you would to data coming from the network.
中文:将应用于从 IndexedDB 来的数据的相同输入验证和输出编码规则应用于从网络来的数据。
Geolocation / 地理位置¶
- The Geolocation API requires that user agents ask for the user's permission before calculating location. Whether or how this decision is remembered varies from browser to browser. Some user agents require the user to visit the page again in order to turn off the ability to get the user's location without asking, so for privacy reasons, it's recommended to require user input before calling
getCurrentPositionorwatchPosition.
中文:地理位置 API 要求用户代理在计算位置之前请求用户的许可。用户是否以及如何记住此决定因浏览器而异。某些用户代理要求用户再次访问页面才能关闭在不询问的情况下获取用户位置的能力,因此出于隐私原因,建议在调用getCurrentPosition或watchPosition之前要求用户输入。
Web Workers¶
- Web Workers are allowed to use
XMLHttpRequestobject to perform in-domain and Cross Origin Resource Sharing requests. See relevant section of this Cheat Sheet to ensure CORS security.
中文:Web Workers 可以使用XMLHttpRequest对象执行域内和跨源资源共享请求。请参阅本速查表的相应部分以确保 CORS 安全性。 - While Web Workers don't have access to DOM of the calling page, malicious Web Workers can use excessive CPU for computation, leading to Denial of Service condition or abuse Cross Origin Resource Sharing for further exploitation. Ensure code in all Web Workers scripts is not malevolent. Don't allow creating Web Worker scripts from user supplied input.
中文:虽然 Web Workers 没有访问调用页面的 DOM,但恶意的 Web Workers 可以使用过量的 CPU 进行计算,导致拒绝服务条件或滥用跨源资源共享进行进一步利用。确保所有 Web Workers 脚本中的代码不是恶意的。不允许从用户提供的输入创建 Web Worker 脚本。 - Validate messages exchanged with a Web Worker. Do not try to exchange snippets of JavaScript for evaluation e.g. via
eval()as that could introduce a DOM Based XSS vulnerability.
中文:验证与 Web Worker 交换的消息。不要尝试交换 JavaScript 片段进行评估,例如通过eval(),因为这可能会引入 基于 DOM 的 XSS 漏洞。
Tabnabbing¶
Attack is described in detail in this article.
攻击在本 文章 中有详细描述。
To summarize, it's the capacity to act on parent page's content or location from a newly opened page via the back link exposed by the opener JavaScript object instance.
总结来说,这是通过打开器JavaScript对象实例暴露的后链,从新打开的页面操作父页面内容或位置的能力。
It applies to an HTML link or a JavaScript window.open function using the attribute/instruction target to specify a target loading location that does not replace the current location and then makes the current window/tab available.
这适用于使用target属性/指令指定目标加载位置的HTML链接或JavaScript window.open函数,该属性/指令不会替换当前位置,并使当前窗口/标签可用。
To prevent this issue, the following actions are available:
为了防止这个问题,以下是一些可行的措施:
Cut the back link between the parent and the child pages:
切断父页面和子页面之间的回链:
- For HTML links:
中文:对于HTML链接:- To cut this back link, add the attribute
rel="noopener"on the tag used to create the link from the parent page to the child page. This attribute value cuts the link, but depending on the browser, lets referrer information be present in the request to the child page.
中文:为了切断这个回链,在从父页面到子页面的链接标签上添加属性rel="noopener"。这个属性值切断链接,但根据浏览器的不同,允许在请求子页面的请求中包含来源信息。 - To also remove the referrer information use this attribute value:
rel="noopener noreferrer".
中文:要同时移除来源信息,请使用此属性值:rel="noopener noreferrer"。
- To cut this back link, add the attribute
- For the JavaScript
window.openfunction, add the valuesnoopener,noreferrerin the windowFeatures parameter of thewindow.openfunction.
As the behavior using the elements above is different between the browsers, either use an HTML link or JavaScript to open a window (or tab), then use this configuration to maximize the cross supports:
由于上述元素在不同浏览器中的行为不同,因此可以使用HTML链接或JavaScript打开一个窗口(或标签),然后使用此配置以实现最大程度的跨浏览器支持:
- For HTML links, add the attribute
rel="noopener noreferrer"to every link. - For JavaScript, use this function to open a window (or tab):
中文:对于JavaScript,使用此函数打开窗口(或标签页):
function openPopup(url, name, windowFeatures = "") {
const features = ["noopener", "noreferrer", windowFeatures]
.filter(Boolean)
.join(",");
window.open(url, name, features);
}
- Add the HTTP response header
Referrer-Policy: no-referrerto every HTTP response sent by the application (Header Referrer-Policy information. This configuration will ensure that no referrer information is sent along with requests from the page.
Compatibility matrix:
兼容性矩阵:
Sandboxed frames / 沙箱框架¶
- Use the
sandboxattribute of aniframefor untrusted content.
中文:使用iframe的sandbox属性来加载不受信任的内容。 - The
sandboxattribute of aniframeenables restrictions on content within aniframe. The following restrictions are active when thesandboxattribute is set:
中文:当设置sandbox属性时,iframe的sandbox属性可以启用对iframe内内容的限制。以下限制在设置sandbox属性时生效:- All markup is treated as being from a unique origin.
中文:所有标记都被视为来自唯一来源。 - All forms and scripts are disabled.
中文:所有表单和脚本都被禁用。 - All links are prevented from targeting other browsing contexts.
中文:所有链接都被阻止以针对其他浏览上下文。 - All features that trigger automatically are blocked.
中文:所有自动触发的功能都被阻止。 - All plugins are disabled.
中文:所有插件都被禁用。
- All markup is treated as being from a unique origin.
It is possible to have a fine-grained control over iframe capabilities using the value of the sandbox attribute.
可以使用sandbox属性的值对iframe功能进行细粒度控制。
- In old versions of user agents where this feature is not supported, this attribute will be ignored. Use this feature as an additional layer of protection or check if the browser supports sandboxed frames and only show the untrusted content if supported.
中文:在旧版本的用户代理中,如果此功能不受支持,则此属性将被忽略。将此功能用作额外的保护层,或检查浏览器是否支持沙盒框架,并且仅在支持的情况下显示不受信任的内容。 - Apart from this attribute, to prevent Clickjacking attacks and unsolicited framing it is encouraged to use the header
X-Frame-Optionswhich supports thedenyandsame-originvalues. Other solutions like framebustingif(window!==window.top) { window.top.location=location;}are not recommended.
中文:除了此属性之外,为了防止点击劫持攻击和未经请求的框架,建议使用支持deny和same-origin值的X-Frame-Options头。其他如framebustingif(window!==window.top) { window.top.location=location;}之类的解决方案不推荐使用。
Credential and Personally Identifiable Information (PII) Input hints / 凭证和可识别个人信息(PII)输入提示¶
Form attributes provide input and autofill hints; they are not a guarantee that the browser will avoid storing sensitive values.
表单属性提供输入和自动填充提示;它们不能保证浏览器避免存储敏感值。
For sensitive fields where autofill is inappropriate, autocomplete="off" requests that the browser not remember or prefill the value. However, browsers may still offer to save and autofill login credentials. Do not treat this attribute as protection against credential reuse on a shared computer.
对于不适合自动填充的敏感字段,autocomplete="off" 请求浏览器不要记住或预填充值。然而,浏览器可能仍然会提供保存和自动填充登录凭证的选项。不要将此属性视为防止在共享计算机上重复使用凭证的保护措施。
<input type="text" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off">
These attributes can adjust input assistance, but do not enforce a no-storage policy:
这些属性可以调整输入辅助,但不会强制执行无存储策略:
spellcheck="false"autocomplete="off"autocorrect="off"autocapitalize="off"
Offline Applications / 离线应用¶
- The HTML5 Application Cache (
<html manifest="...">and.appcachefiles) has been removed from all major browsers (Firefox 85, Chrome 93). Do not use it for new applications and migrate any remaining usage to Service Workers with the Cache API. - Service Workers run on a separate, scriptable thread and intercept network requests for the registered scope. Because they can transparently serve cached responses, they have a significant security impact:
中文:服务工作者在独立的、可脚本化的线程上运行,并拦截注册作用域的网络请求。由于它们可以透明地提供缓存的响应,因此对安全性有重大影响:- Only register Service Workers from your own origin and only serve the worker script over HTTPS with a long-cache-busting filename (e.g.
sw.<hash>.js).
中文:仅从您的源注册服务工作者,并且仅通过HTTPS提供工作者脚本,使用长缓存破坏文件名(例如sw.<hash>.js)。 - Validate that the scope of the Service Worker is restricted (use the
scopeoption or theService-Worker-Allowedresponse header) so a compromised worker cannot intercept unrelated paths.
中文:验证服务工作者的作用域是否受到限制(使用scope选项或Service-Worker-Allowed响应头),以确保受损害的工作者无法拦截无关路径。 - A malicious or compromised Service Worker can intercept requests from the pages it controls. Have a documented recovery process that updates or unregisters the worker and removes affected caches. Expiring cached data does not unregister a worker.
- Do not put responses containing sensitive data into the Cache API.
Cachedoes not honor HTTP caching headers, and entries do not expire automatically; service-worker code must explicitly exclude these responses and delete any sensitive entries already stored. Continue sendingCache-Control: no-storeto control HTTP caches.
- Only register Service Workers from your own origin and only serve the worker script over HTTPS with a long-cache-busting filename (e.g.
Progressive Enhancements and Graceful Degradation Risks / 渐进式增强和优雅降级风险¶
- The best practice now is to determine the capabilities that a browser supports and augment with substitutes only for capabilities that are not directly supported. Do not fall back to obsolete browser plugins — Adobe Flash Player reached end-of-life on 31 December 2020 and is removed from all browsers; Java applets, Silverlight, and ActiveX are likewise unsupported. Native HTML5 (
<video>,<audio>,<canvas>, WebAssembly) covers these legacy use cases.
中文:现在的最佳实践是确定浏览器支持的功能,并仅对直接不支持的功能使用替代方案。不要回退到过时的浏览器插件——Adobe Flash Player于2020年12月31日达到生命周期的终点,并从所有浏览器中删除;Java小程序、Silverlight和ActiveX同样不受支持。原生的HTML5(<video>、<audio>、<canvas>、WebAssembly)覆盖了这些遗留用例。
HTTP Headers to enhance security / HTTP头以增强安全性¶
Consult the project OWASP Secure Headers in order to obtains the list of HTTP security headers that an application should use to enable defenses at browser level.
参考项目OWASP安全头,以获取应用程序应使用的HTTP安全头列表,以在浏览器级别启用防御。
References / 参考¶
- MDN: Window.postMessage() security concerns) (MDN: Window.postMessage() 安全问题)
- MDN: IndexedDB API