remix-auth-totp icon indicating copy to clipboard operation
remix-auth-totp copied to clipboard

[ Question ] About brute-force protection.

Open mackermans opened this issue 8 months ago • 9 comments

Hi @dev-xo,

I am wondering how this strategy protects against brute-force guessing attacks if the attempts are tracked in the cookie instead of server-side state.

My concern is the following case:

  1. Request a magic link (cookie is created)
  2. Store the cookie value
  3. Guess the 6-char code
  4. Reset the cookie value to its state in step 2 (which resets the attempts value as if the previous guess never happened)
  5. Repeat 2-4 until you guess the correct code within the expiry timeframe

I saw #43 but I must be missing something. Would appreciate some clarification!

mackermans avatar Mar 18 '25 14:03 mackermans

Hello @mackermans,

I think the flow you showcased looks correct. We transitioned from Database storage to Session storage some time ago, with the intention of keeping the strategy a bit simpler to implement (and I think we achieved it).

A downside could be brute-forcing by resetting the cookie after each attempt, and I'm not sure if the strategy would be able to handle that in any way possible without giving it more control, such as database storage, session tracking (server-side), etc.


With that said, in order to offer a solution/suggestion:

  • Ideally, you can handle this situation with "Server Rate Limiting," checking how many requests to a single route have been made from the same client and blocking those after X attempts, or similar.

You can easily handle some stops on the client/server side, ideally keeping the strategy just to handle authentication.


Also, it would be great to improve the strategy over time, allowing Database again, session tracking, or simply handling this situation internally, or making it optional for users who might want to handle database, not only session.

dev-xo avatar Mar 18 '25 22:03 dev-xo

Thanks @dev-xo, that's fair enough, I'll implement rate limiting then.

I think it would help if there was a hook point where we could read cookie ID and email, so people could track failed attempts however they see fit (whether database, Redis, or in-memory) and fail verification if some threshold is exceeded. This would allow implementing a strategy like OWASP's Slow Down Online Guessing Attacks with Device Cookies.

mackermans avatar Mar 19 '25 17:03 mackermans

That looks really interesting, @mackermans (checked the shared link).

Been some time since I checked the inners of the Strategy, but we are currently able to get the email, although it would be interesting to read the Cookie ID or provide some extra data that might be useful for the developer.

Not sure if you have the time to check on the source-code and come up with that hook, otherwise I can look into it the moment I have a bit of time.


With that said, the Strategy is still open for improvements, so if you have the time or come up with an idea for it, feel free to let me know.

dev-xo avatar Mar 19 '25 17:03 dev-xo

Can't work on this at the moment unfortunately, but will try to find some time. Appreciate your openness to improvements @dev-xo 🙌

mackermans avatar Mar 20 '25 10:03 mackermans

Closing this for now, @mackermans.

Thanks for the feedback and the little chat! If I get some time, I'll try to expose some hooks and look into the talk we had.

dev-xo avatar Mar 21 '25 00:03 dev-xo

I was surprised (and confused) to see while I was integrating this that I could spam the continue button from the /verify examples, which definitely led me down the rabbit hole.

I've looked into the code and I don't see attempts being incremented aside from the magic links path (in _decryptUrlParams here), which correct me if I'm wrong doesn't even need to track attempts right? If maxAttempts worked before then this should definitely be treated as a critical regression. Also just a thought, should the attempts be encrypted alongside TOTP data?

Please let me know your thoughts.

Until the attempts issue get sorted out, I will probably be disabling manual code submissions if I decide to continue using this package. Thanks for it btw!


p.s. I'm all in for getting database-backed tracking again as well! I don't think personally it was a good idea to ditch it entirely, since the redundant hash could instead be replaced by a database ID. Maybe extracting it to a storage plugin or adapter that can easily be customized or even just an store and resolve hook (cookie as default when undefined) would be nice.

decanTyme avatar Sep 20 '25 02:09 decanTyme

Hello @decanTyme, thanks for reaching out.

To be honest, been some time since I looked into the strategy, and probably will move the whole package to an archived repository, looking myself into better-auth or related alternatives, due to personal disappointments with how the Remix team handled Remix, the announcement of Remix v3, etc.

That said, remix-auth-totp has been through a few versions, and each one of those has been handled by mostly single persons who offered to look into them, and by me reviewing that and cleaning a bit the code, so probably features or security checks we had before may be missing in these new ones, due to the fact that I wasn't able to properly keep track of those, and the new people involved in the new versions.

The v1.0 of remix-auth-totp was database blocked, where the database was the source of truth (probably the safest option), but it was not that easy to integrate, for the simple fact that not everyone uses the same database, services, etc.


If you are up to come up with a solution or a small fix for the attempts issue, it may be great, as I do not have that much time right now to look into, although the fix should be simple (probably an if check?).

  • Also, any proposal or idea you may have (even move back into database-backed), let me know, as we can look into it.

Will try to get some time myself to look into the attempts issue, and if you are up to look into it or anything else, feel free to let me know.

Thanks again for reaching out!

dev-xo avatar Sep 21 '25 12:09 dev-xo

Thanks for the response! @dev-xo

I'd be glad to contribute fixes, and it's right on time with #hacktoberfest as well! So I might have some time on my hands for that, haha. I'll try making a PR that fixes the attempts bug within this month, I like the simplicity of this package and the remix-auth community as a whole. It just integrates so well with the Remix architecture imo.

Can you keep the package around tho? I've been searching for a strategy like this and all I can find other than this was remix-auth-otp, which I believe was also made by you (correct me if I'm wrong). I couldn't find the correct npm link for it tho, only this. There's also a similar email-link strategy but lacking email-code support.

Anyway, regarding the database storage, I have been completely thinking about it all wrong assuming the createCookieSessionStorage was used, my bad. I agree it shouldn't even be covered by the package because a better solution would be to just leverage a custom storage (via createSessionStorage), then setting appropriate values (e.g. session.set("attempts")), which then calls the createSessionStorage.updateData when a commitSession is done. In other words, the session storage and the app itself should be the one that handles the tracking.

I have done exactly this for our existing Passport app authenticator strategy that I migrated over to remix-auth. I'm curious about the security implications tho, but with secrets I don't think it poses much problems if it isn't leaked.


On a side note, I was shocked as well when I came back to work on Remix apps again that, well, there's a completely different v3 now for some reason. Tho I kinda get their decision to just merge Remix to React Router because they probably wanted the exact same features for RR as well. But it is a really great architecture, personally, I always had fun building with it so I don't really care as much.

decanTyme avatar Oct 06 '25 14:10 decanTyme

Happy to hear you got some time to look into it @decanTyme, and I should be the one to thank you for it!

The strategy and the package may simply remain active, although sadly I don't have that much time to keep improving it or even check for edge cases or security issues, although with the release of Remix v3, I may look back into it in order to keep supporting remix-auth and Remix v3 itself.

In my case, I personally moved to better-auth, as the integration for react-router@v7 is really simple (more or less like remix-auth). But if all you care about is a simple implementation or you simply like the architecture of remix-auth, then you can stick with it, as the Strategy works, and remix-auth itself is pretty great (Sergio did an amazing job).

The remix-auth-otp and email-link aren't mine, and I think remix-auth-totp (this Strategy) was initially motivated by remix-auth-otp, extending it a bit more and adapting it to @epic-web/totp from Kent C. Doods.

That said, if you have some time, I suggest having a look at the source code, and feel free to open a PR with the fixes you may consider or even some suggestions.

If I get some time, I will try to help with it; otherwise, I will simply review your proposals and merge them into the Strategy.

Again, thank you for taking the time or showing interest in it, as I think we will help some other folks who may be using the strategy (I noticed there are almost 4k weekly downloads), so hopefully we can keep this Strategy alive for Remix v3 too.

I'm here in case you want to let me know anything else or want to start a discussion in a proper PR. Thanks!

dev-xo avatar Oct 06 '25 15:10 dev-xo