Breaking down the possibilities of breaking in.
Jesus Rodriguez asks:
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?
See the original question here.
Sending secret data
Joachim Sauer answers (66 votes):
It’s simple: any security mechanism that relies on the client to do only what you tell it to do can be compromised when an attacker has control over the client.
You can have security checks on the client, but only to effectively act as a “cache” (to avoid making an expensive round-trip to the server if the client already knows that the answer will be “no”).
If you want to keep information from a set of users, make sure that those users’ client never gets to that information. If you send that “secret data” together with instructions “but please don’t display it,” it’ll become trivial to disable the code that checks that request.
Once your data leaves the server, you must assume that an attacker has full access to it.
Benjamin Gruenbaum answers (17 vote):
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 HTTPS protocol to prevent MITM (man in the middle) attacks.
- Client handshakes with server for the first time, the server generates a public key for the client and keeps a private one in an asymmetric encryption scheme. 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 his knowledge of that data, and the client encrypts every action he 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.
- Server decrypts the actions, and if they are in correct format it trusts that they originated in the client.
- 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 (with Angular for example).
- 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 ‘offline’ password every time he/she starts running offline code.
- Don’t roll your own crypto: 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.
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.
- Certificates vs username+password
- Why can’t we do security via client side?
- Don’t roll your own crypto.
- Related discussion about making JS secure for crypto operations.
Everyone is against you
nvoigt answers (7 votes):
There is a saying in the gaming community: “The client is in the hands of the enemy”. Any code that is running outside of a secured area like the server is vulnerable. It’s the client’s decision to actually run your “security code” and the user may just “opt out.” While with native code you have at least an automatic obfuscation into assembly and an additional layer of protection by the fact that an attacker needs to be a good programmer to manipulate that, JS comes normally obfuscated and as plain text.
All you need to stage an attack are primitive tools like a proxy server and a text editor. The attacker will still need a certain level of education concerning programming, but it’s way easier to modify a script using any text editor than injecting code into an executable.
Find more answers or leave your own at the original post. See more Q&A like this at Programmers, a site for conceptual programming questions at Stack Exchange. And of course, feel free to login and ask a question for yourself.