-2

I was tasked to beef up security of a website built with use of Angular v15 + JWT. And the first part was to replace login POST-request (HTTPS) from this kind:

/api/login?username=user_name&password=pass123

to this kind:

/api/login?credentials=U2FsdGVkX19ViJp9Vr9DiJKq7cjzsR8imgBbrYgxb5rtsBL9D01r

To that end, I added crypto-js to the client, to encrypt the credentials, while putting the encryption secret code right in JavaScript, which gives me the willies, as secret keys are ordinarily kept on the server only. But in this case it is unavoidable to have them in both server and client.

My question is:

Given the open nature of everything on the client side, is there any recommended approach to at least mediate the security of storing a secret key on the client? Maybe even specifically for Angular.

vitaly-t
  • 24,279
  • 15
  • 116
  • 138
  • Why are you passing credentials in query? You should login passing the credentials into the request body. – CesareIsHere Apr 16 '23 at 11:20
  • @CesareIsHere Does this mean they will be automatically encrypted on the client, and decrypted on the server? – vitaly-t Apr 16 '23 at 11:21
  • You don't need to encrypt credentials, even if you want you should encrypt the password on the client (using md5, sha, ecc) and send it to the server without decrypting it (You should use algorithms that are undecryptable, so you don't need any secretKey). – CesareIsHere Apr 16 '23 at 11:24
  • @CesareIsHere Your suggestion contradicts information posted [here](https://stackoverflow.com/questions/4121629/password-encryption-at-client-side). According to that post, those md5 etc approach isn't really encrypted. – vitaly-t Apr 16 '23 at 11:28
  • If you are using HTTPS the transmission is already encrypted by using SSL, so i think that your encryption is enough , however if you are not using SSL/TLS you should read the post you linked. – CesareIsHere Apr 16 '23 at 11:35
  • @CesareIsHere There's functionally no difference between putting creds in a header or body. It's all decrypted at the same layer so if https is broken nothing is gained. Encrypting/hashing passwords client side in a form the server uses for auth comparisons is means you either have to store a salt on the client (rendering the salt useless) or not use a salt at all. Either way, if https is broken the attacker has what they need to get authorization. – possum Apr 16 '23 at 11:36
  • @CesareIsHere md5, sha aren't encryptions. They're hashes – possum Apr 16 '23 at 11:36
  • Sorry for the wrong term. Thanks for the many insights in this discussion – CesareIsHere Apr 16 '23 at 11:42
  • @CesareIsHere Thanks for the suggestion to use `body`, which I changed to - see my answer below. – vitaly-t Apr 16 '23 at 14:41
  • I have seen this done by an authentication appliance, but it used asymmetric crypto not a shared secret. So the client side had a public key and there was no need to overly protect it. – Rup Apr 16 '23 at 23:08

1 Answers1

0

After speaking to some security specialists, I have found out that the best approach (most secure one) is as follows...

Implement a simple custom algorithm that can produce a secure key for encryption, based on the current date/time (while observing the timezone), and then use it in both client and server. The secret key will be consistent and dynamic, while at the same time it won't be kept anywhere.

This will make it more secure for the client than anything else, as production Angular code is scrambled nicely, and it would be very difficult to break such a website.

UPDATE

Later on, I went with the defaults supported by Angular v15+, like this:

  • API service on the client side:
login(username: string, password: string) {
    const headers = {
        'Content-Type': 'application/json'
    };
    return this.http.post('./api/login', {username, password}, {headers});
}
  • API handler on the server-side, where I'm using Express.js:
import express, {Request, Response} from 'express';

const app = express();

app.use(express.json()); // To auto-parse "body"

app.post(`/api/login`, (req: Request, res: Response) => {
    const {username, password} = req.body;
});

At least, no more open-text URL parameters.

vitaly-t
  • 24,279
  • 15
  • 116
  • 138
  • How would the date & time thing work? You can't assume the client and server clocks are 100% synced, so you either need to supply the time from the server, in which case it's just necessary to unpick the key derivation algorithm out of your script, or you'll need to snap the time to something relatively low resolution e.g. nearest second and accept the keys for a few seconds either side on the server, but then you're leaving a smaller number of keys for the attacker to try generating based on the number of seconds and your algorithm. I can't see what this will gain you. – Rup Apr 16 '23 at 23:11