HTML5 offline authentication

2020-06-12 02:14发布

问题:

I am looking for advice/criticism on how best to control access to an HTML5 application that is used predominantly offline.

The application uses a combination of IndexedDB, local and session storage to store data so that it can be used offline. The data/pages are served via HTTPS.

The aim is to minimise the risk of the data being viewed if the tablet/PC was lost/stolen.

Currently the application uses the Stanford Javascript Crypto library to encrypt the user/password and then save it to local storage if the user is able to successfully authenticate to the server. If the application then goes offline a user must authenticate 'locally' against the encrypted user/password in local storage.

In addition an unencrypted user/password is stored in the session storage if the user is able to successfully authenticate to the server. This is used so that the application can periodically attempt to re-establish contact with the server and 'seemlessly' re-authenticate without requiring the user to re-enter their credentials.

I am aware of a number of posts/discussions about the fallibility of client side encryption refer http://www.matasano.com/articles/javascript-cryptography/ and http://rdist.root.org/2010/11/29/final-post-on-javascript-crypto/ and .nczonline.net/blog/2010/04/13/towards-more-secure-client-side-data-storage/ + others. However I am unsure how these arguments apply in this scenario.

I am looking for criticism of the approach given the need of storing data offline. If there is a better approach please elaborate.

Thanks

回答1:

Authentication vs. secure storage

I'll start with the big design issue: You seem to work with the problem as if it is about authentication, where the (potentially malign) user needs to prove to your application that she really is valid user. But actually you are facing a storage problem, because the whole runtime environment, containing all the sensitive information your application is working with, is in the hand of the attacker if the computing device is stolen. In the case of a javascript application the analysis of the offline data and code is even more comfortable than in the case of some binary only code.

For example if I would want to attack your application I would first look into the session storage (cookies? Simply use the browser interface to look them up) and see if I can find the username and password there. If not I would follow the code that is used to decrypt the password in the local storage (probably using a javascript debugger). The way you have described your application it seems like the functions can decrypt it without a key supplied by the user. Maybe I can just comment out the local authentication of the user by changing something like if(authenticateUser()) to if(true).

So what you really have to do is encrypt all sensitive, local data with a key that is not stored on the client side at all. For example ask the user for a decryption key every time he accesses your application, use that key to decrypt the locally stored data (and encrypt every new data you store) and throw the key away after a certain time of inactivity. Or you can authenticate the user against the server every time he accesses your application and retrieve the decryption key from there and throw it away after a certain time of inactivity.

At this point the choice of a javascript environment really hampers your cause as you cannot force the runtime environment to throw away the decryption key when you want it gone. It is difficult enough with C applications even, as you have to carefully work around swapping the RAM out onto the HDD. Depending on how sensitive the information your application works with is it might be enough to ask the user to close the browser after she is finished and assume that an attacker is not motivated enough to look for the key in swapped out RAM of the browser.

Locally saving the login data

As it is the most sensitive information you work with you should never store the user login information on the client. Instead authenticate against the server once and retrieve an authentication token from it for future interactions. This would be basically identical to a session cookie and expires after some time (if it does not expire at all it is as good as the password).



回答2:

I have now implemented a solution which I describe below in case it is useful to someone else. I do understand it is 'not an answer' to my question i.e. does not offer critique, but given that the application must work 'offline' along with the requirement of seamlessly re-authenticating I cannot see how @Perseids answer can be implemented, although I do appreciate the dialogue (from both @SilverlightFox and @Perseids).

If there is a solution to not having to store the user's credentials 'offline' while fulfilling the requirements outlined in my question I would be keen to hear.


The application must be able to authenticate a user when the application is 'online' and 'offline'. For an 'online' application normally a session token solution would be adopted i.e. only a session identifier would be stored on the client (usually in a cookie) but not the user's credentials. However the user's credentials necessarily have to be stored on the client (perhaps someone will come up with a clever alternative?) so that security can be enforced while the application is offline i.e. allow a user to authenticate while offline and decrypt/encrypt IndexedDB data. In order to make the application more secure, the user's username and password are stored in an encrypted form. Some sensitive IndexedDB data is also stored in an encrypted form. Thus even if a malicious user were to obtain the computer or tablet with a cached instance of the application they would only be able to view the usernames, passwords and data in their encrypted form (provided the user has logged out or closed their browser).

Unfortunately at this time there doesn't seem to be any 'standard' protocol for securing HTML5 offline applications. Almost all literature warns not to store user credentials or any sensitive data on the client. However this is a paradox as this application must work while offline hence the data must be stored offline.

The security protocol implemented here has two keys, although once the first key is cracked it will be easy to obtain the second key. In the first level the user's password is encrypted with their own password as the key along with their username reversed as the salt. There is also a second key, 'data encryption key', that is returned from the server upon successfully authenticating to the server. This key is used to encrypt both the username and any IndexedDB data. This 'data encryption key' is inturn encrypted using the user's password. Thus if an attacker were to be able to decrypt the user's password they would then easily be able to use the password to decrypt the 'data encryption key' and then using the decrypted 'data encryption key' decrypt the user's username and any encrypted IndexedDB data. Only the encrypted form of the usernames, passwords and data then needs to be stored permanently on the client as by using the user's username and password entered into the login screen it is then possible to decrypt any persisted data.

However, after logging in, the username and password are stored in the client's session in their unencrypted form so that 1) the application can periodically re-authenicate with the server, this makes re-authentication seamless if there is intermittent connectivity and 2) retrieve the decrypted data encryption key at any time so as to be able to query/save the IndexedDB data and decrypt/encrypt it where necessary. If 1) weren't a requirement it should only be necessary to store the data encryption key in the session. This leads to a vunerability if the user has not logged out or has not closed their browser since a malicious user would then be able to view the user's password and username in their decrypted form (using a debugging tool). However this is not much worse than the same thing happening to a traditional online application that gives the user the ability to change their password, although normally a traditional online application has a session timeout so the malicious user would only have a limited time to act. Also if the browser crashes, normally it will give the user the option to restore their previous windows/tabs with their session information, hence the browser should be closed properly.

The protocol adopted above almost certainly does not follow best practices. For example the salt is not random (username reversed), is likely to be short, vunerable to a dictionary attack, the same may apply to the password (the strength of the password is a function of the server), there is no key stretching e.g. PBKDF2. However I cannot see how it is possible to follow 'best practices' and fulfill the requirements given the constraints imposed. It may be possible to improve the hashing a bit e.g. improve the salt, perhaps a combination of the username and a site-specific string however even that would require logic in the javascript that could be understood by a determined attacker. The Javascript can be obfuscated but that too only makes it more difficult but not impossible and any person capable of cracking the encryption keys would not find obfuscation of the Javascript much of a hinderance. Perhaps with some future clever inherent inbuilt support from the browser significant improvement will be possible.