HTML5 Security Cheat Sheet



{| style="padding: 0;margin:0;margin-top:10px;text-align:left;" |- Last revision (mm/dd/yy): // = Introduction =
 * valign="top" style="border-right: 1px dotted gray;padding-right:25px;" |

The following cheat sheet serves as a guide for implementing HTML 5 in a secure fashion.

= Communication APIs =

Web Messaging
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:


 * When posting a message, explicitly state the expected origin as the second argument to postMessage rather 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.
 * The receiving page should always:
 * Check the origin attribute of the sender to verify the data is originating from the expected location.
 * Perform input validation on the data attribute of the event to ensure that it's in the desired format.
 * Don't assume you have control over the data attribute. A single Cross Site Scripting flaw in the sending page allows an attacker to send messages of any given format.
 * 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. via innerHTML), as that would create a DOM-based XSS vulnerability. For more information see DOM based XSS Prevention Cheat Sheet.
 * To assign the data value to an element, instead of using a insecure method like element.innerHTML = data;</tt>, use the safer option: element.textContent = data;</tt>
 * Check the origin properly exactly to match the FQDN(s) you expect. Note that the following code:  if(message.orgin.indexOf(".owasp.org")!=-1) { /* ... */ }</tt> is very insecure and will not have the desired behavior as www.owasp.org.attacker.com</tt> will match.
 * If you need to embed external content/untrusted gadgets and allow user-controlled scripts (which is highly discouraged), consider using a JavaScript rewriting framework such as Google Caja or check the information on sandboxed frames.

Cross Origin Resource Sharing

 * Validate URLs passed to XMLHttpRequest.open</tt>. 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.
 * Ensure that URLs responding with Access-Control-Allow-Origin: *</tt> do not include any sensitive content or information that might aid attacker in further attacks. Use the Access-Control-Allow-Origin</tt> header only on chosen URLs that need to be accessed cross-domain. Don't use the header for the whole domain.
 * Allow only selected, trusted domains in the Access-Control-Allow-Origin</tt> header. Prefer whitelisting domains over blacklisting or allowing any domain (do not use *</tt> wildcard nor blindly return the Origin</tt> header content without any checks).
 * Keep in mind that CORS does not prevent the requested data from going to an unauthenticated location. It's still important for the server to perform usual CSRF prevention.
 * While the RFC recommends a pre-flight request with the OPTIONS</tt> verb, current implementations might not perform this request, so it's important that "ordinary" (GET</tt> and POST</tt>) requests perform any access control necessary.
 * Discard requests received over plain HTTP with HTTPS origins to prevent mixed content bugs.
 * 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.

WebSockets

 * Drop backward compatibility in implemented client/servers and use only protocol versions above hybi-00. Popular Hixie-76 version (hiby-00) and older are outdated and insecure.
 * The recommended version supported in latest versions of all current browsers is RFC 6455 (supported by Firefox 11+, Chrome 16+, Safari 6, Opera 12.50, and IE10).
 * While it's relatively easy to tunnel TCP services through WebSockets (e.g. VNC, FTP), doing so enables access to these tunneled services for the in-browser attacker in case of a Cross Site Scripting attack. These services might also be called directly from a malicious page or program.
 * The protocol doesn't handle authorization and/or authentication. Application-level protocols should handle that separately in case sensitive data is being transferred.
 * Process the messages received by the websocket as data. Don't try to assign it directly to the DOM nor evaluate as code. If the response is JSON, never use the insecure eval function; use the safe option JSON.parse instead.
 * Endpoints exposed through the ws://</tt> protocol are easily reversible to plain text. Only wss://</tt> (WebSockets over SSL/TLS) should be used for protection against Man-In-The-Middle attacks.
 * Spoofing the client is possible outside a browser, so the WebSockets server should be able to handle incorrect/malicious input. Always validate input coming from the remote site, as it might have been altered.
 * When implementing servers, check the Origin:</tt> header in the Websockets handshake. Though it might be spoofed outside a browser, browsers always add the Origin of the page that initiated the Websockets connection.
 * As a WebSockets client in a browser is accessible through JavaScript calls, all Websockets communication can be spoofed or hijacked through Cross Site Scripting. Always validate data coming through a WebSockets connection.

Server-Sent Events

 * Validate URLs passed to the EventSource</tt> constructor, even though only same-origin URLs are allowed.
 * As mentioned before, process the messages (<tt>event.data</tt>) as data and never evaluate the content as HTML or script code.
 * Always check the origin attribute of the message (<tt>event.origin</tt>) to ensure the message is coming from a trusted domain. Use a whitelist approach.

= Storage APIs =

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 not to store any sensitive information in local storage.
 * 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.
 * 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 is a bad practice.
 * Do not store session identifiers in local storage as the data is always accesible by JavaScript. Cookies can mitigate this risk using the <tt>httpOnly</tt> flag.
 * 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 host multiple applications on the same origin, all of them would share the same localStorage object, use different subdomains instead.

Client-side databases

 * On November 2010, the W3C announced Web SQL Database (relational SQL database) as a deprecated specification. A new standard Indexed Database API or IndexedDB (formerly WebSimpleDB) is actively developed, which provides key/value database storage and methods for performing advanced queries.
 * Underlying storage mechanisms 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 not to store any sensitive information in local storage.
 * If utilized, WebDatabase content on the client side can be vulnerable to SQL injection and needs to have proper validation and parameterization.
 * Like Local Storage, a single Cross Site Scripting can be used to load malicious data into a web database as well. Don't consider data in these to be trusted.

= Geolocation =


 * The Geolocation RFC recommends that the user agent ask 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 <tt>getCurrentPosition</tt> or <tt>watchPosition</tt>.

= Web Workers =


 * Web Workers are allowed to use <tt>XMLHttpRequest</tt> object to perform in-domain and Cross Origin Resource Sharing requests. See relevant section of this Cheat Sheet to ensure CORS security.
 * 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.
 * 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.

= Tabnabbing =

Attack is described in details into this article.

To resume, it's the capacity from a new opened page to act on parent page's content or location via the back link exposed by the opener javascript object instance.

It apply to html link or javascript  function using the attribute/instruction   to specify a target loading location that do not replace the current location and then let the current window/tab available.

To prevent this issue the following actions are available:
 * 1) Cut the back link between the parent and the child pages:
 * 2) * For html link:
 * 3) ** To cut this back link then add the attribute  on the tag used to create the link from the parent page to the child page. This attribute value cut the link but, depending on the browser, let referrer information be present in the request to the child page.
 * 4) ** To remove also the referrer information then use this attribute value:.
 * 5) * For javascript  function, add the values   in the windowFeatures parameter of the    function.

As the behavior using the elements above is different between the browsers either using html link or javascript to open a window (or tab) then use this configuration to maximize the cross supports:


 * For html link, add the attribute  for every links.
 * For Javascript, use this function to open a window (or tab):
 * Add the HTTP response header  the every HTTP responses send by the application (Header Referrer-Policy information). This configuration will ensure that no referrer information is sent along with requests from page.

Compatibility matrix:
 * https://caniuse.com/#search=noopener
 * https://caniuse.com/#search=noreferrer
 * https://caniuse.com/#feat=referrer-policy

= Sandboxed frames =


 * Use the <tt>sandbox</tt> attribute of an <tt>iframe</tt> for untrusted content.
 * The <tt>sandbox</tt> attribute of an <tt>iframe</tt> enables restrictions on content within a <tt>iframe</tt>. The following restrictions are active when the <tt>sandbox</tt> attribute is set:
 * 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 triggers automatically are blocked.
 * All plugins are disabled.

It is possible to have a fine-grained control over <tt>iframe</tt> capabilities using the value of the <tt>sandbox</tt> attribute.


 * 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 <tt>X-Frame-Options</tt> which supports the <tt>deny</tt> and <tt>same-origin</tt> values. Other solutions like framebusting <tt>if(window!== window.top) { window.top.location = location; }</tt> are not recommended.

= Offline Applications =


 * Whether the user agent requests permission to the user to store data for offline browsing and when this cache is deleted varies from one browser to the next. Cache poisoning is an issue if a user connects through insecure networks, so for privacy reasons it is encouraged to require user input before sending any <tt>manifest</tt> file.
 * Users should only cache trusted websites and clean the cache after browsing through open or insecure networks.

= Progressive Enhancements and Graceful Degradation Risks =


 * The best practice now is to determine the capabilities that a browser supports and augment with some type of substitute for capabilities that are not directly supported. This may mean an onion-like element, e.g. falling through to a Flash Player if the &lt;video&gt; tag is unsupported, or it may mean additional scripting code from various sources that should be code reviewed.

= HTTP Headers to enhance security =

X-Frame-Options

 * This header can be used to prevent ClickJacking in modern browsers.
 * Use the <tt>same-origin</tt> attribute to allow being framed from urls of the same origin or <tt>deny</tt> to block all. Example: <tt>X-Frame-Options: DENY</tt>
 * For more information on Clickjacking Defense please see the Clickjacking Defense Cheat Sheet.

X-XSS-Protection

 * Enable XSS filter (only works for Reflected XSS).
 * Example: <tt>X-XSS-Protection: 1; mode=block</tt>

Strict Transport Security

 * Force every browser request to be sent over TLS/SSL (this can prevent SSL strip attacks).
 * Use includeSubDomains.
 * Example: Strict-Transport-Security: max-age=8640000; includeSubDomains

Content Security Policy

 * Policy to define a set of content restrictions for web resources which aims to mitigate web application vulnerabilities such as Cross Site Scripting.
 * Example: Content-Security-Policy: allow 'self'; img-src *; object-src media.example.com; script-src js.example.com

Origin

 * Sent by CORS/WebSockets requests.
 * There is a proposal to use this header to mitigate CSRF attacks, but is not yet implemented by vendors for this purpose.

= WebSocket implementation hints =

In addition to the elements mentioned above, this is the list of area for which caution must be taken during the implementation.


 * Access filtering through the "Origin" HTTP request header
 * Input / output validation
 * Authentication
 * Authorization
 * Access token explicit invalidation
 * Confidentiality and Integrity

The section below will propose some implementation hints on every area and will be along with an application example showing all the points described.

The complete source code of the example application is available here.

Access filtering
During a websocket channel initiation, the browser send the Origin HTTP request header that contain th source domain initiation the request to handshake. Event if this header can be spoofed in a forged HTTP request (not browser based), it cannot be overrided or forced in a browser context. It then represent a good candidate to apply filtering according to an expected value.

An example of attack using this vector and named Cross-Site WebSocket Hijacking (CSWSH) is described here.

The code below define a configuration that apply filtering based on a "whitelist" of origins. This ensure that only allowed origins can establish a full handshake:

Authentication and Input/Output validation
When using websocket as communication channel, it's important to use an authentication method allowing the user to receive an access Token that is not automatically sent by the browser and then must be expliclty sent by the client code during each exchange.

JSON Web Token is a good candidate because it allow to transport access ticket information in a stateless and not alterable way. Moreover, it define a validity timeframe. You can find additional information about JWT token hardening on this article.

JSON Validation Schema are used to define and validate the expected content in input and ouput messages.

The code below define the complete authentication messages flow handling:

Authentication Web Socket endpoint - Provide a WS endpoint the enable authentication exchange

Authentication message handler - Handle all authentication requests

Utility class to manage JWT token - Handle the issuing and the validation of the access token. Simple JWT token has been used for the example (focus was made here on the global WS endpoint implementation) here without extra hardening (see this article to apply extra hardening on the JWT token)

JSON schema of the input and output authentication message - Define the expected structure of the input and output messages from the authentication endpoint point of view { "$schema": "http://json-schema.org/schema#", "title": "AuthenticationRequest", "type": "object", "properties": { "login": { "type": "string", "pattern": "^[a-zA-Z]{1,10}$" },   "password": { "type": "string" } },  "required": [ "login", "password" ] }

{ "$schema": "http://json-schema.org/schema#", "title": "AuthenticationResponse", "type": "object", "properties": { "isSuccess;": { "type": "boolean" }, "token": { "type": "string", "pattern": "^[a-zA-Z0-9+/=\\._-]{0,500}$" }, "message": { "type": "string", "pattern": "^[a-zA-Z0-9!\\s]{0,100}$" } }, "required": [ "isSuccess", "token", "message" ] }

Authentication message decoder and encoder - Perform the JSON serialization/deserialization and the input/output validation using dedicated JSON Schema. It allow to systematically ensure that all messages received and sent by the endpoint strictly respect the expected structure and content.

Note that the same approach is used in the messages handling part of the POC. All messages exchanged between the client and the server are systematically validated using the same way, using dedicated JSON schemas linked to messages dedicated Encoder/Decoder (serialization/deserialization).

Authorization and access token explicit invalidation
Authorization information is stored in the access token using the JWT Claim feature (in the POC the name of the claim is access_level). Authorization is validated when a request is received and before any other action using the user input information.

The access token is passed with every message sent to the message endpoint and a blacklist is used in order to allow the user to request an explicit token invalidation. Explicit token invalidation is interesting from a user point of view because, often when token are used, the validity timeframe of the token is relatively long (it's common to see a valid timeframe superior to 1 hour) so it's important to allow a user to have a way to indicate to the system "OK, i have finished my exchange with you so you can close our exchange session and cleanup associated links". It's help also a user to revoke itself is current access if a malicious concurrent access is detected using the same token (case of token stealing).

Token blacklist - Maintain a temporary list using memory and time limited Caching of hashes of token that are not allowed to be used anymore

Message handling - Process a request from a user to add a message in the list. Show a authorization validation approach example

Confidentiality and Integrity
If the raw version of the protocol is used (protocol WS://) then the transfered data are exposed to eavesdropping and potential on-the-fly alteration.

Example of capture using Wireshark and searching for password exchanges in the stored PCAP file, not printable characters has been explicitly removed from the command result:

There is a way to check, at WebSocket endpoint level, if the channel is secure by calling the method isSecure on the session object instance.

Example of implementation in the method of the endpoint in charge of setup the session and affect the message handler:

Expose WebSocket endpoints only on WSS:// protocol (WebSockets over SSL/TLS) in order to ensure Confidentiality and Integrity of the traffic like using HTTP over SSL/TLS to secure HTTP exchanges.

= Authors and Primary Editors =

= Other Cheatsheets =


 * }