This specification defines an API for persistent, encrypted data storage of key-value pair data in Web clients.

Introduction

This specification introduces a mechanism for storing secure structured data on the browser.

Current mechanisms for storing data on the browser include cookies, sessionStorage, localStorage, and client-side databases. None of these mechanisms offer built-in encryption or any other manner of security. For sessionStorage, in which data is stored in memory, accessible only from the same top-level browsing context in which it was created, and deleted when the top-level browsing context is destroyed, encrypting of data is not important. Cookies, localStorage, and client-side databases all write their data to disk, and storing unencrypted user data represents a security issue for both developers and users.

As client-side data storage becomes more fully supported, web application authors will start to store more data on the browser, such as email messages. Currently, browsers store this information on disk in plain text format, making it accessible to anyone who has access to the computer. This represents a significant security risk and requires web developers to either create their own security mechanisms (i.e., encrypting the data themselves) or to avoid using client-side data storage altogether if they wish to protect their data.

Security-conscious companies have rules regarding the storage of personalized data on disk. Such rules typically require all personalized data to be encrypted before being written to disk and may even specify which encryption algorithm to use. Given these prevalent requirements, and the desirability of client-side data storage for web applications, a mechanism for providing a secure client-side data storage location both ensures that developers can make use of client-side storage even when operating under strict guidelines and also encourages other developers to secure their data.

The mechanism described in this specification is similar to localStorage, in that the data is written to disk and accessible from multiple browsing contexts. It differs in that it provides additional security features with the goal of protecting user data from various attacks:

  • Data is encrypted with a developer-selected cipher and key to prevent reading of data saved on disk.
  • Storage locations are accessible only by knowing a developer-defined name to prevent identifying specific data on disk.
  • Storage locations may be assigned an expiration date at which time the browser automatically deletes all of the contained data. This ensures that data will now be left around on disk for longer than necessary.

The secureStore IDL attribute is used to access an origin’s secure data storage mechanism.

The SecureStorage interface

interface SecureStorage {
    readonly attribute unsigned long length;
    getter DOMString key(in unsigned long index);
    getter any getItem(in DOMString key);
    setter creator void setItem(in DOMString key, in any data);
    deleter void removeItem(in DOMString key);
    void setExpiration(in Date expires);
    void clear();
};

Each SecureStorage object provides access to a list of key/value pairs, which are sometimes called items. Keys are strings. Any string (including the empty string) is a valid key. Values can be any data type supported by the structured clone algorithm.

Each SecureStorage object is initialized with a list of key/value pairs when it is created, as defined in the sections on the secureStore attribute. Multiple separate objects implementing the SecureStorage interface can all be initialized with the same list of key/value pairs simultaneously.

The length attribute must return the number of key/value pairs currently present in the list associated with the object.

The key(n) method must return the name of the nth key in the list. The order of keys is user-agent defined, but must be consistent within an object so long as the number of keys doesn’t change. (Thus, adding or removing a key may change the order of the keys, but merely changing the value of an existing key must not.) If n is greater than or equal to the number of key/value pairs in the object, then this method must return null.

The keys of each key/value pair are not present on the SecureStorage object as named properties and therefore cannot be enumerated. To obtain a list of each key in the SecureStorage object, a combination of the length attribute and key(n) method may be used.

The getItem(key) method must return a structured clone of the current value associated with the given key. If the given key does not exist in the list associated with the object then this method must return null.

The setItem(key, data) method must first create a structured clone of the given value. If this raises an exception, then the exception must be thrown.

Otherwise, the user agent must then check if a key/value pair with the given key already exists in the list associated with the object.

If it does not, then a new key/value pair must be added to the object, with the given key and with its value set to the newly obtained clone of value.

If the given key does exist in the list, then it must have its value updated to the newly obtained clone of value.

If it couldn’t set the new value, the method must raise an QUOTA_EXCEEDED_ERR exception. (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.)

Once the new value is set, the user agent must first serialize the key/value pairs contained in data, associated the specified name, and then encrypt the data using the specified encryption and with the specified key.

This information then must be written to disk in such a way that the name and key/value pairs are all encrypted. The cipher and key must not be written to disk.

If an exception occurs during serialization, then an INVALID_DATA_ERR must be thrown.

The removeItem(key) method must cause the key/value pair with the given key to be removed from the object, if it exists. If no item with that key exists, the method must do nothing.

The setItem() and removeItem() methods must be atomic with respect to failure. That is, changes to the data storage area must either be successful, or the data storage area must not be changed at all.

The clear() method must atomically cause the object to be emptied of all key/value pairs, if there are any. If there are none, then the method must do nothing. Calling this method must not remove any set expiration date.

The setExpiration(expires) method assigns a date on which the user agent must delete the entire storage location, including key/value pairs, the name of the storage location, and any other associated meta data.

User agents must delete data on disk when the specified date and time are reached. Deleting data on disk must update any SecureStorage objects initialized with key/value pairs from the storage location.

If the expires argument indicates a date and time that has occurred in the past, then this date is considered null and therefore the expiration date must be removed while not removing any data from the storage location.

If the expires argument is different than the last expiration date assigned to the storage location, then the new expiration date must overwrite the previous expiration date.

If the expires argument is null , and the storage location has an expiration date associated with it, then the expiration date must be removed. If the storage location has no expiration date associated with it, then no change should be made.

The WindowSecureStorageManager interface

[Supplemental, NoInterfaceObject]
interface WindowSecureStorageManager {
    const unsigned short AES_128 = 1;
    const unsigned short AES_192 = 2;
    const unsigned short AES_256 = 3;

    void openSecureStorage(in DOMString name, in unsigned short cipher, in DOMString key, in SecureStorageCallback callback);
    void removeSecureStorage(in DOMString name);
}
Windows implements WindowSecureStorageManager

[Callback=FunctionOnly, NoInterfaceObject]
interface SecureStorageCallback{
  void handleOpen(in SecureStorage storage);
};

Each SecureStorageManager object provides access to secure data storage locations associated with an origin.

User agents must have a set of secure storage locations, one for each origin.

When the openSecureStorage(name, cipher, key, callback) method is called, the user agent must run the following steps:

  1. Determine if key is a valid key for the given cipher. A key is considered valid for the cipher if it is a base64-encoded string representation of a numeric key containing the correct number of bits for the cipher. If key is not a valid key for the given cipher, then throw an INVALID_KEY_ERR and abort any further operation.
  2. If the Document‘s effective script origin is not the same origin as the Document‘s origin, then throw a SECURITY_ERR exception and abort these steps.
  3. Check to see if the user agent has allocated a secure storage area for the origin of the Document of the Window object on which the method was invoked. If it has not, create a new storage area for that origin.

The openSecureStorage(name, cipher, key, callback) method must first create a new SecureStorage object. This object must be initialized with data from the storage location with the given name. The storage location is read from disk and unencrypted using the given cipher and key. If an incorrect cipher or key is specified such that the storage location data cannot be deserialized into key/value pairs, then the SecureStorage object must remain empty. If a storage location with the name doesn’t exist, this method must return an empty SecureStorage object. When the SecureStorage object has been filled with all data appropriate based on its name and cipher, the callback must be called asynchronously and the SecureStorage object must be passed in as the only argument.

The removeSecureStorage(name) method must cause the storage area with the given name to be removed from disk. If no storage area with that name exists, the method must do nothing.

If an exception occurs during serialization, or data is not a SecureStorage object, then an INVALID_DATA_ERR must be thrown.

User agents must expire data from the secure storage areas only for security reasons or when requested to do so by the user or through a call to removeSecureStorage().

Example Usage

window.openSecureStorage("emails", window.AES_192, secureKey, function(storage){

    //set an expiration date one year from now

    var expires = new Date();

    expires.setFullYear(expires.getFullYear()+1);

    storage.setExpiration(expires);

    //determine if client-side data is too old

    if (typeof storage.getItem("lastLogin") == "number"){

        if (new Date(storage.getItem("lastLogin")) < Application.UPGRADE_DATE){

            storage.clear();

            Application.updateClientSideData()

        }

    }

    //store information

    storage.setItem("lastLogin", Date.now());

});

Disk Space

User agents should limit the total amount of space allowed for storage areas.

User agents should guard against sites storing data under the origins of other affiliated sites, e.g. storing up to the limit in a1.example.com, a2.example.com, a3.example.com, etc, circumventing the main example.com storage limit.

User agents may prompt the user when quotas are reached, allowing the user to grant a site more space. This enables sites to store many user-created documents on the user’s computer, for instance.

User agents should allow users to see how much space each domain is using.