How Easy is it to Hack JavaScript in a Browser?

browserhackingjavascriptSecurity

My question has to do with JavaScript security.

Imagine an authentication system where you're using a JavaScript framework like Backbone or AngularJS, and you need secure endpoints. That's not a problem, as the server always has the last word and will check if you're authorized to do what you want.

But what if you need a little security without involving the server? Is that possible?

For example, say you've got a client-side routing system and you want a concrete route to be protected for logged-in users. So you ping the server asking if you're allowed to visit protected routes and you go on. The problem is that when you ping the server, you store the response in a variable, so the next time you go to a private route, it will check that if you're already logged in (no ping to the server), and depending on the response it will go or not.

How easy is for a user to modify that variable and get access?

My security (and JavaScript) knowledge isn't great. But if a variable is not in global scope and is in the private part of a module pattern which only have getters but not setters, even in that case, can you hack the thing out?

Best Answer

Please read Joachim's answer before reading this one. He covers the general reasons behind client-side vulnerability. Now, for a suggestion how you might get around this problem...

A secure scheme for client-server communication without having to authenticate with the server manually on every request:

You're still letting the server have the last say, and the server still has to validate everything the client says, but it happens transparently.

Assume the HTTPS protocol to prevent man-in-the-middle attacks (MITMA).

  • The client handshakes with the server for the first time, and the server generates a public key for the client and keeps a private one using an asymmetric encryption scheme. The client stores the server's "public" key in the local storage, encrypted with a secure password you don't save anywhere.

  • The client is now offline. The client wants to perform trusted actions. The client enters his password and grabs the server's public key.

  • The client now performs actions based on its knowledge of that data, and the client encrypts every action it performs with the server's public key for that client.

  • When the client is online, client sends its client ID and all actions the client performed are sent to the server encrypted with the server's public key.

  • The server decrypts the actions, and if they are in correct format it trusts that they originated in the client.

Note:

  • You can't store the client's password anywhere, otherwise an attacker would be able to fetch the key and sign the actions as its own. The security of this scheme relies solely on the integrity of the key the server generates for the client. The client would still need to be authenticated with the server when asking for that key.

  • You're still in fact relying on the server for security and not the client. Every action the client performs you must validate on the server.

  • It's possible to run external scripts in web workers. Keep in mind every JSONP request you have is now a much bigger security issue. You need to protect the key at all costs. Once you lose it, an attacker can impersonate the user.

  • This meets your demand that 'no ping to the server' is performed. An attacker can't simply imitate an HTTP request with forged data if they don't know the key.

  • Joachim's answer is still correct. You're still, in fact, performing all authentication on the server. The only thing you saved here is the need for password validation with the server every time. You now need only to involve the server when you want to commit, or pull updated data. All we did here is save a trusted key on the client side and have the client re-validate it.

  • This is a pretty common scheme for single page applications (for example, with AngularJS).

  • I call the server's public key "public" because of what that means in schemes like RSA, but it is in fact sensitive information in the scheme and should be safeguarded.

  • I wouldn't keep the password anywhere in memory. I'd make the user submit his/her 'offline' password every time he/she starts running offline code.

  • Don't roll your own cryptography - use a known library like Stanford's for authentication.

  • Take this advice as is. Before you roll this sort of authentication in a real world business-critical application consult a security expert. This is a serious issue that is both painful and easy to get wrong.

It's critical that no other scripts have access to the page. This means you only allow external scripts with web workers. You can't trust any other external scripts that might intercept your password when the user enters it.

Use a prompt and not an inline password field if you're not completely sure and don't defer its execution (that is, it shouldn't live to a point where events have access to it but only in sync code). And don't actually store the password in a variable -- again, this only works if you trust the user's computer isn't compromised (although that's all true for validating against a server too).

I'd like to add again that we still don't trust the client. You can't trust the client alone, and I think Joachim's answer nails it. We only gained the convenience of not having to ping the server before starting to work.

Related material: