18

I have implemented an authentication system which works like this:

  1. Upon successful login, the server takes the username of client and encrypts it with AES-256.

  2. This ciphertext is stored in the client's browser and when the client wants to do something which requires login, this ciphertext is sent to the server. The server decrypts the ciphertext and obtains the username of the client who is logged in.

An attacker cannot breach a client's account because he/she doesn't know the encryption key, so it doesn't matter if the attacker knows the username. However, I'm worried if client's browser is exposed, the attacker will access both the ciphertext and plain text (username). Does this allow the attacker to "calculate" the encryption key given that both the ciphertext and plaintext are known? Because that key is used for all clients, so if it's exposed the entire system is ruined.

Peter Mortensen
  • 877
  • 5
  • 10
Mr. Engineer
  • 684
  • 1
  • 4
  • 10
  • 6
    Not an answer... but I feel a bit weird here. Perhaps I miss some details. Why do we need to encrypt username? Why not just send the username plain-text? – Hoàng Long Apr 15 '21 at 03:55
  • 5
    Possible duplicate of: [Does symmetric encryption provide data integrity?](https://security.stackexchange.com/questions/9437/does-symmetric-encryption-provide-data-integrity). Known plaintext-ciphertext pairs don't help you get the *key*, so they don't help you decrypt other ciphertexts; but they often *do* make it possible to encrypt other plaintexts, which would completely break your scheme. You need integrity, not encryption. – ruakh Apr 15 '21 at 07:11
  • 7
    Normally a JWT is a **signed** token that contains username in plaintext. It is used worldwide and accepted as best practice. Why reinveting the wheel then? – usr-local-ΕΨΗΕΛΩΝ Apr 15 '21 at 08:58
  • 1
    Just a note: The result of encryption isn't a "hash", it's a "cyphertext". A hash can't be decrypted at all ever, while a cyphertext can. – HiddenWindshield Apr 15 '21 at 14:37
  • @HiddenWindshield True, fixed – Mr. Engineer Apr 15 '21 at 15:04
  • 3
    Why not use a randomly-generated session key, like is commonplace? Are you using deterministic encryption? In other words, is the encrypted username always the same ciphertext? If so, that would be very questionable. – marcelm Apr 15 '21 at 19:34
  • 1
    I'm confused. Are you sending this blob of cyphertext over the wire using plain HTTP? Is it the same blob every time for a given user? If so, wouldn't it be trivial for an attacker to sniff the blob off the wire and re-send it? And if you're using HTTPS / TLS, then why bother encrypting the username separately? – mindcrime Apr 16 '21 at 15:15
  • @mindcrime I'm securing the connection with TLS, but the purpose of username encryption is to create "token" for authentication that cannot be forged without knowing the encryption key. That is, before client's browser gets exposed. – Mr. Engineer Apr 16 '21 at 15:25
  • `if client's browser is exposed, attacker will access both the ciphertext and plain text (username)` Wouldn't the attacker get access to the encryption key too? – sox with Monica Apr 16 '21 at 15:57
  • 6
    *I have implemented authentication system* This is nearly always a bad sign. I have been programming for 30 years, and I avoid writing security code as much as possible because it's always better to use tested, verified libraries when available. – chrylis -cautiouslyoptimistic- Apr 16 '21 at 16:21
  • 1
    Small contribution to the discussion: many have questioned why do this and/or suggested instead how this could be done, but without pointing at actual sources for further documentation or similar. I think a very informative and beginner friendly source for security is [OWASP](https://cheatsheetseries.owasp.org). For this question in particular [Authentication](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html) and [Session Management](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html) – Gruber Apr 17 '21 at 02:58
  • 5k views... alright guys you have convinced me, I will just use JWT before moving my app to production. @Gruber Thank you for the links, I will check them out when I have some time. – Mr. Engineer Apr 20 '21 at 13:09

4 Answers4

25

In answer to your main question, AES256 is secure as far as we know into the foreseeable future. However your authentication scheme has several drawbacks.

First, if any request where the token is sent is compromised, or if a the user installs a malicious addon that can grab the encrypted user name from their browser, that account is forever unusable. You are essentially creating a token to use for authentication that cannot ever be changed and that is a 1:1 relationship with the user name. The only way to deny access if it is compromised is to shut down the account and force the user to create a new account with a different username.

A much better way would be to generate a random token when the user authenticates and store that in the database, or generate a random value and encrypt that as the token. Then if the account is compromised or if the user wishes to 'log out', you can remove that token and generate a new one.

If your encryption key is ever compromised or if it is ever cracked somehow, the attacker can do anything as any user in your system. With a random token based approach, they would have to know the random part used to generate the token for each user. The attacker would have to have access to your database and your encryption key.

Jason Goemaat
  • 592
  • 3
  • 7
  • 1
    This is a good answer. One solution could be to add login date to username string and encrypt both. If date is too old token could be set to expired. – Mr. Engineer Apr 15 '21 at 14:00
  • 8
    @Mr.Engineer That solution isn't very good either (though it's better); there aren't all that many dates to try. – wizzwizz4 Apr 15 '21 at 14:53
24

No. The attacker cannot obtain the encryption key from the plaintext and the encrypted text, because AES is resistant to known-plaintext attacks. See details in the answer on Crypto SE.

I'd suggest you to review your design. Making user name secret can lead to many problems. For instance, if user needs to report a problem, how can user tell user name if it is secret? Of if an administrator needs to change user permissions, how can the responsible person tell administrator what user needs to be changed, if the user name is secret? And so on. I'd suggest not to encrypt the user name.

So actually we have an XY problem here. Actually, an authentication token should be used to know who the user actually is. In such case encryption of user name is not needed and thus the question about AES and known-plaintext attack is not needed.

mentallurg
  • 8,536
  • 4
  • 26
  • 41
  • How does the server know which user is logged in? The only way is to store identifier in client's browser. Username is a good identifier, but the purpose of encryption here is to prevent attacker from spoofing/forging the identifier if he knows the username. – Mr. Engineer Apr 15 '21 at 06:09
  • 19
    Why you would use the user's username as the session identifier is beyond me. You should be using revoke-able tokens and/or standard session ID's. These need not be encrypted, and you don't have to jump through a bunch of hoops with usernames. @Mr.Engineer – Florian Humblot Apr 15 '21 at 06:55
  • @Mr.Engineer: Of course we do need user name. But it should not be secret. User name is not password and should not be secret. There is no need to encrypt user name. – mentallurg Apr 15 '21 at 07:03
  • @FlorianHumblot Because it's easier for server to obtain username of user that way, I have tried with JWT Tokens but I couldn't read the username from token. But I think you have just spotted a weakness in my design: my session identifier cannot be revoked without changing the encryption key. – Mr. Engineer Apr 15 '21 at 07:03
  • 2
    @Mr.Engineer: To prevent spoofing an authentication token should be used. – mentallurg Apr 15 '21 at 07:04
  • 2
    @mentallurg I will probably need to implement a dictionary on server-side for storing random session identifiers and related usernames. – Mr. Engineer Apr 15 '21 at 07:11
  • 9
    @Mr.Engineer Yes, indeed. Or you could use JWT, which is a stateless design. –  Apr 15 '21 at 08:37
  • 10
    @Mr.Engineer "I have tried with JWT Tokens but I couldn't read the username from token" You didn't try hard enough, the token does contain the name of the authorized party. – Ben Voigt Apr 15 '21 at 15:40
  • 6
    @Mr.Engineer JWT's payload is human readable JSON **encoded** (not encrypted) in base64. You can paste any JWT token into https://jwt.io/ to see the payload. In general, it is recommended that a JWT token include a parameter called "sub" that is supposed to be the user identifier. This can be a user id (integer), user's email or username. In your case you should make it the username. What secures JWT is the signature which is a similar idea to your idea above but proven and designed by crypto experts (not just the algorithm but the entire flow) – slebetman Apr 16 '21 at 05:36
  • 2
    @Mr.Engineer In both your token design and JWT revocation must be handled by a revocation list. Just store revoked tokens in a database and check if the token match any revoked tokens. – slebetman Apr 16 '21 at 05:40
  • 1
    @Mr.Engineer To read username from JWT in any language you just need to do `parseJSON(base64Decode(token)).sub`. Of course you need to use the appropriate functions from the language you're using. This is often much easier to do than using AES encryption because most language have built-in functions for both base64 and JSON processing – slebetman Apr 16 '21 at 05:42
  • I understand the idea behind JWT and that is one working solution, but it does require keeping the list of granted tokens, blacklist etc. so I was looking for alternative and invented my own system... I will probably add date and clocktime to token in order to make it revokeable. – Mr. Engineer Apr 16 '21 at 10:45
  • 2
    @Mr.Engineer but it doesn't need keeping a list of granted tokens, unless you want to expire tokens, which you also need to do for every other type of token... don't make your own system please – not my real name Apr 16 '21 at 16:30
  • 1
    @Mr.Engineer You apparently don't understand the idea behind JWT. There will be more knowledgeable people than you trying to defeat your design. They will come up with attacks that you didn't think of. So please use something proven for your own good. – gronostaj Apr 17 '21 at 07:21
10

Would be bad form to ask "Why?"

There are plenty of free, open source, battle-tested authentication systems in place.

Unless you are expert in crypto engineering (and the fact that we are here shows otherwise), please don't invent your own system.

It's great messing around with code just to teach yourself, but don't take it further.

Jan Hertsens
  • 237
  • 1
  • 3
  • 3
    WHile the other answers address the technical details, this is the practical answer. – sleske Apr 16 '21 at 09:17
  • My design makes it easier to obtain the username of those who are logged in and also makes it impossible to forge the identifier, so I wouldn't call it a bad idea, but it's weakness is that if client is exposed, revoking would require to change the encryption key and that would log out all users. Btw, what do you mean by "the fact that we are here shows otherwise"? I think this is the place where experts meet :) – Mr. Engineer Apr 16 '21 at 10:39
  • 8
    It's not. It's the place where amateurs who think they are experts meet, with the token actual expert chiming in from time to time to dole out useful advice like "don't try to implement your own authentication system." – mindcrime Apr 16 '21 at 15:09
  • @Mr.Engineer I think it's fair to say that experts don't ask random strangers on the internet about their encryption scheme. At least I haven't seen Bruce Schneier asking questions around here so far. (Experts generally also know why it's an awful idea to write their own schema and how the manyfold of existing options covers their use case - and if it doesn't the reason why providing that option would be an awful idea) – Voo Apr 16 '21 at 17:22
  • 2
    This answer would be better if it mentioned a couple of the existing open-source authentication systems -- such as, for example, Flask-Security ( https://pythonhosted.org/Flask-Security/ ). – David Cary Apr 16 '21 at 23:13
  • 1
    Basically, this entire thing is saying "Googling 'How to get username from JWT' is too much hassle, and using the language's session system is not hip enough, lets invent my own system" https://auth0.com/docs/tokens/json-web-tokens/json-web-token-structure – Jan Hertsens Apr 17 '21 at 15:14
8

AES is secure against Known-Plaintext-Attacks (KPA) where an attacker has access to both plaintext and ciphertext. AES withstands attacks for more than 20 years and AES-256 is the golden standard that even AES-256 can beat the Quantum attack of Grover's optimal Search Algorithm. Even AES-128 is secure in the foreseeable future - except the multi-target attack..

Your authentication scheme is insecure as you noticed. You should either use

  • standard password hashing with Scrypt, PBKDF2, or Argon2 with 128-bit random salt per user and users choose dicewire passwords for good strength against password searches.
  • Or, use PAKE if that fits you.
kelalaka
  • 5,409
  • 4
  • 24
  • 47
  • The purpose of encryption here is to prevent attacker from forging the identifier which is stored in client's browser. Username is a good identifier but it's not secret so encryption is vital here. Can you point out where is the weakness if known-plaintext-attacks do not work? – Mr. Engineer Apr 15 '21 at 06:15
  • To clarify: attacker cannot generate the ciphertext because he doesn't know the key so he cannot forge the identifier. This is how it's supposed to work :) – Mr. Engineer Apr 15 '21 at 06:25
  • 1
    What kind of attacker we are talking about exactly? If the attacker installed a key logger then they can learn everything the user typed. – kelalaka Apr 15 '21 at 06:59
  • We are talking about attacker who is attempting to access the account by forging the identifier, assuming that client's browser is not exposed. Username as identifier can very easily be forged without encryption. – Mr. Engineer Apr 15 '21 at 07:07
  • If they cannot access the AES key of the server, then with a given user name, they cannot produce the corresponding ciphertext, unless your server acts as an encryption oracle. – kelalaka Apr 15 '21 at 07:15
  • @Mr.Engineer "attacker cannot generate the ciphertext because he doesn't know the key so he cannot forge the identifier.". What is to stop the attacker from carrying out step 1 of your scheme? ("Upon successful login, server takes the username of client and encrypts it with AES-256"). Presumably during the first "successful login" the user has to provide the username? – Jon Bentley Apr 15 '21 at 08:13
  • @JonBentley Attacker doesn't know the key so he cannot produce the ciphertext, and thus, cannot access the account without password. Server will provide client that ciphertext but only after checking that his password is correct. But there is another weakness here which is that this ciphertext cannot be revoked without changing the encryption key, which would logout all users. – Mr. Engineer Apr 15 '21 at 13:45
  • 1
    @JonBentley Server needs to know which user is logged in and for that we need to set session identifier to his browser. Username is not secret, but ciphertext is. But as Jason Goemaat pointed out, this system has another weakness which is that those identifiers cannot be revoked if they get exposed. – Mr. Engineer Apr 15 '21 at 14:05
  • 1
    @Mr.Engineer I think you want signing, not encryption. Like with RSA or HMAC – not my real name Apr 16 '21 at 16:31