Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Security flaw that should be noted #18

Open
Clemens-E opened this issue Feb 27, 2023 · 4 comments
Open

Security flaw that should be noted #18

Clemens-E opened this issue Feb 27, 2023 · 4 comments

Comments

@Clemens-E
Copy link

I really like this approach for stateless captcha, and it definitely has its use cases where you only want to prevent automatic and unintelligent spam.
After solving a captcha once, a user can simply generate as many valid JWT tokens as they want and bypass that captcha until the JWT for the captcha expires (which seems to be 1400m if I'm not mistaken)
You should maybe add a disclaimer in the repo, or implement a simple cache to store used captchas until they expiry (and set a shorter expiry), but repo seems abandoned

@vesolovski
Copy link

vesolovski commented Mar 4, 2023

I agree with your remark on the security flaw and there is one more flaw - the "/captcha" post request returns something like:

"validation":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhbnN3ZXIiOiJQUFlCNHYiLCJpYXQiOjE2Nzc5NDk3NzcsImV4cCI6MTY3ODAzMzc3N30.5vbMHRbhvSLyGuc6xLDBBaln-fsfyDUCbOwUVVuXtog"

which contains the answer and is easy to decode

see e.g.
https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhbnN3ZXIiOiJQUFlCNHYiLCJpYXQiOjE2Nzc5NDk3NzcsImV4cCI6MTY3ODAzMzc3N30.5vbMHRbhvSLyGuc6xLDBBaln-fsfyDUCbOwUVVuXtog

@Clemens-E
Copy link
Author

I never used this implementation, I just had a look for inspiration, my implementation uses AES/GCM.

But from reading through the code it looks like it shouldn't be able to decrypt ? Did you set CAPTCHA_TOKEN_SECRET like in the example?

@vesolovski
Copy link

vesolovski commented Mar 4, 2023

congrats - may I ask how did you manage to prevent the usage of the same captcha/hash multiple times? I am considering embegging expiration time or captcha counter in the hash. But it has flaws:

  • expiration time allows the use of the same captcha/hash for this period of time - maybe short but still
  • captcha counter e.g. 1, 2, 3, ..., 400 - each captcha render increases the number and previous numbers cannot be used. but for multiple users at the same time the counter might increase by another user making the captcha already loaded in html invalid for someone else (counter saved in some file - common for all users to make it stateless)

I implemented the restless-captcha and the above validation code was from my running implementation (as you can see it contains the answer easily decodeable). also in the readme it stands:

to verify the user's answer submit an object with the nonce, the answer and the validation string to /captcha/verify

{
nonce: '1223dsfdres-434-dd',
answer: 'mk4f8h',
validation: "ksafrp348573pfsldkfjlj.............."
};

so the validation code needs to be known by the client (thus the answer in it).

@Clemens-E
Copy link
Author

To be noted, my implementation was only an MVP, I generated a random captcha image with 4 digit answers.
I also generate a json, that has the answer and a generation timestamp, then I encrypt it with AES/GCM with a static private key defined in the application (and a strong random IV (IV is AES specific)).
The Client receives the image and the encrypted message. Then, the client hast to send back the encrypted message, and the answer to the captcha.
The Backend tries to decrypt the message, checks if it is expired (say, after 15 mins), and checks if the answers match.

You can now do nothing, but that leaves the loophole of generating as many keys as you want in that time period.
My solution would be to either store the "used" captchas in a simple array or map in memory, and remove expired captchas from it.
How you identify the used captchas is up to you, preferably you should add some kind of ID in to generate JSON?

The only issue with that is, It's not really stateless anymore, but it's definitely using less state, and when you have some random bot attack you don't actually have to store anything because they most likely won't solve the captcha anyway. Ram usage would be minimal for storing a bunch of IDs that are cleaned anyway.
I'm planning to use this in a system where restarts happen once a week max, and we only have two nodes. So, the possibility of exploiting this on a large scale is nonexistent (unless you have AI to solve the captchas, I guess). Even if you somehow manage to switch nodes, you would be able to use the same captcha only twice, and I don't care about that.

If you have many nodes, you probably want some simple Redis instance that automatically removes the used keys for you after they expired (you only have to store them for 15 mins max because they expired anyway) and the captcha can only really be used once.

I don't see any further loophole in this, but my implementation is only meant to protect from unintelligent bots, and not a human attacker.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants