0

I implemented a 2FA authentication for a web app with PHP and Google Authenticator. In order to login to my system there are a few steps:

  1. User types in a complex master password in order to access the login page. The request is throttled for only 1 request every hour and includes a CAPTCHA.

  2. User types in username and password in a web application on their computer. Forms contain CSRF token.

  3. There is a Google Recaptcha V3 to ensure the request comes from a human. This check is done in the front and backend.

  4. When the username and password matches, the server gets the secret key from the user database table and generates a QR code which is injected as a data URI src inside an HTML image tag. The user scans the bar code with google authenticator, which in turn generates a code which the user can use to login to the system.

What I don't understand: Can't anyone scan the QR-Code if they cracked the username and password? I have the feeling I'm doing something wrong in step 4.

Anyone could download the authenticator so I'm not entirely sure if its secure or is storing the secret inside my database a bad idea? Should users type in their secret to generate the QR code manually? Also, I'm thinking, shouldn't the secret change every once a while?

schroeder
  • 123,438
  • 55
  • 284
  • 319
Asperger
  • 135
  • 3
  • 3
    You have a login page to access the login page? Is this for you, personally, or general users? How long is your session timeout? Why limit login attempts to ***one every hour***. That seems like a good way to DoS the account. – schroeder May 28 '20 at 15:09
  • 3
    What are you trying to protect against? – schroeder May 28 '20 at 15:09
  • 2
    Is this for authentication or for ***registration***? Please tell me a user does not do this every time. If for authentication, why are you sending the code using the same channel to communicate and log in? That's not 2FA... – schroeder May 28 '20 at 15:11
  • 2
    Why a CAPTCHA for every login? If you are only allowing one login per hour, that's a huge development overhead for no increase in protection. – schroeder May 28 '20 at 15:18
  • 1
    This is quite confusing, you should consider all questions asked by @schroeder. It looks overcomplicated with not much reason for it. 2FA is used to mitigate password theft and brute forcing, CAPTCHA is used to avoid automated attacks and CSRF is used to avoid replay attacks. All different things meant for different purposes. Mixing them all in one bag does not get you more security. – Pedro May 28 '20 at 15:23
  • Step #4 looks like what would happen when a user sets up 2FA, but it's not at all obvious if that is what is happening? – Pedro May 28 '20 at 15:23
  • @schroeder it is for a login into my system. No registration. Im just wondering, how can I make sure my 2FA with google authenticator is safe? Right now step 4 is basically useless it seems because if the hacker knows the username and password and get forwarded to the 2FA section, its basically useless. – Asperger May 28 '20 at 17:00
  • Right. So why are you doing it? It's not a use case for 2FA apps at all. Just use 2FA as it's meant to be used... – schroeder May 28 '20 at 18:50

1 Answers1

2

Using a QR code in the process you described basically just ensures that the user has a cell phone, if I understand it right. So, yes, your suspicion is correct.

The way that apps like Google Authenticator are used is that the QR code is generated one time, when the account is set up (or when the user chooses to enable 2FA). The QR code is a way of sharing a secret between an app on your phone and the backend server. Once the secret has been shared with the app, the QR code is not used again (unless you need to register another app with the 2FA system and keep both apps active).

From that point on, Google Authenticator will continually generate a new temporary code every minute based on that secret. The login process will present an entry point for the temporary code. When you enter a temporary code (during the time when it is valid), it proves that you have a device that knows the secret.


While we're at it, step one is a little goofy also. For starters, I'm not sure how you would rate-limit attempts. Would you do it based on IP address? One IP address can have many users. It would be annoying if I couldn't login for the next hour because my roommate just logged in.

Beyond that, it adds one more password. A passphrase is the same factor as a password. It adds security, a little bit, but is it enough to justify memorizing something else? Why not require the password to be longer instead?

Fire Quacker
  • 2,432
  • 1
  • 19
  • 29
  • Ya the idea was to block ip addresses after a number of attemps and you are also right about the pass phrase at the beginning. Thank you for your advice. – Asperger May 28 '20 at 17:09
  • Is there any way to secure the F2A process? I could ask the user to input the secret in a form which generates the QR Code if the secret corresponds with the secret stored in the corresponding user table row. They can then scan the QR-Code. Will that make things any better in your opinion? The user would get a new secret after every succesful login. – Asperger May 28 '20 at 17:18
  • The secret is usually generated by the server. Something super, super, super strong. I don't have details in front of me, but I would imagine something like a hundred characters, all random. Keep in mind the secret is a seed used to generate short temporary codes, so the users don't need to remember what it is or know it at all. That's all handled by the app. Theoretically, you could brute-force it, if you had unlimited access to the login system (ie, no rate-limits) and a lot of free time. A _lot_ of free time. – Fire Quacker May 28 '20 at 17:25
  • @Asperger "_As indicated in the algorithm requirement section, keys SHOULD be chosen at random or using a cryptographically strong pseudorandom generator properly seeded with a random value... Keys SHOULD be of the length of the HMAC output to facilitate interoperability.... We also RECOMMEND storing the keys securely in the validation system, and, more specifically, encrypting them using tamper-resistant hardware encryption and exposing them only when required_" - [RFC 6238](https://tools.ietf.org/html/rfc6238#section-5.1) – Fire Quacker May 28 '20 at 17:38
  • Ok after reading everything I think I understand. So, say the user logs into his account. After that he could say have the option to activate 2FA. The backend creates a secret key based on HMAC and a SHA Algorithm. Key is stored in the user table row. Based on the key and issuer name, the backend generates a QR-Code. Once scanned by the authenticator app on the phone the user gets a temporary 6 digit code to login. The QR-Code cant be used again once scanned. Is my interpretation correct? 6 digits dont seem a lot, even if they change every 30 seconds. – Asperger May 28 '20 at 18:33
  • For some reason the OTP with 6 digits seems so flawed if there are no rate limits to stop brute forcing. Google authenticator only has 6 digits which really sucks. 8 or even more digits would be mote secure. :/ – Asperger May 28 '20 at 18:42
  • @Asperger To be clear, the QR code is just for transferring the seed during setup. From then on the app and the server are "synchronized". They will both continually calculate a new 6 digit code every 30 seconds. So you don't use the QR code again because you don't need to, not because it's invalid. Now, concerning brute-forcing the 6 digit code, that is discussed in [this answer by AndrolGenhald](https://security.stackexchange.com/a/185917/129883). – Fire Quacker May 28 '20 at 20:46
  • As far as in concerned only the authenticator app generates the validation code. The server would simply black list already used validation codes so they cant be used twice. AndrolGenhalds article is incredible by the way, thanks! – Asperger May 28 '20 at 21:23