Page MenuHomePhabricator

Allow captchas to be stacked
Open, Needs TriagePublic


Advanced captchas like T158909, T87598 or T34695 need to be able to fall back to other captchas (T158909 because it can only detect humans, not bots; T87598 and T34695 because they keep from a dynamic pool of known-good answers which might get depleted). It would be nice to handle that at the configuration level instead of making it the captcha implementation's responsibility to handle falling back.

A nice way to handle it on top of T177133: Turn ConfirmEdit captcha implementations into a family of services would be to allow captchas to respond with OK/FAIL/PASS and have a "manager" implementation of the captcha service which takes a list of other services and calls them in order until one returns a non-PASS response. (In practice it should be a bit more complex than that, probably remembering the fallback level between requests via the session.)

Maybe even captcha throttling could be spun out into a separate "captcha" implementation which always returns PASS unless the throttle limit is hit. That would be one less responsibility the individual captcha implementations would have to care about. That would also allow more flexibility for things like T42496.

Event Timeline

It seems like for AICaptcha we'd want more nuanced interaction: for one thing, we might want to display the captcha and then ignore user errors instead of not displaying the captcha in the first place (because the captcha field is valuable as a fairly uniform source of typing), also we might want to show harder or easier captchas based on spambot risk level.

I think there are roughly three scenarios for falling back to another captcha type:

  1. The server tries to generate a captcha but fails so we need to fall back to the next captcha type. (This would happen for some types of third-party captchas when the third-party site is down, or for first-party captchas with a challenge pool when the pool is depleted.) The server needs to generate the page using the next captcha on the stack.
  2. The page with the captcha is rendered and sent to the client, but the captcha fails in some way that can be determined without submitting the form. (Maybe it tries to load a third-party captcha via AJAX and the site is down. Maybe the browser does not support some required web technology. Maybe the captcha check happens entirely on the client side, such as an AJAX submission. Maybe even the user requesting an audio captcha could be modeled as this, although there we'd want the user to be able to switch back and forth so it's not really a fallback.) Ideally, the client needs to be able to fall back to the next captcha without reloading the page. (Less ideally, we can just let the client submit and fail, which is scenario #3.)
  3. The captcha can fail on submission. (Since the form submission is the one that kicks off the user registration process, it needs some proof that the captcha was solved, even if that was decided dynamically on the client side. Checking that proof can always fail.) The server needs to generate the page using the next captcha on the stack, with some form error explaining what happened.

Scenario #1 and #3 are straightforward, but we need to track the state somehow - if captcha type A cannot be used, we present type B as a fallback and the user fails it, we want to show another B, not another A.

Scenario #2 would either require a dedicated captcha API, or all captchas would have to be included in the original page rendering, and the client would only switch which one is visible. All captcha types on the same page is worse for performance and less flexible. Also, many captchas have a "request another" functionality so the captcha API would be more useful (FancyCaptcha already has one, but each captcha type having its own API is not really sustainable). It seems like a non-trivial amount of work though. Possibly, which types of captchas to initially include in the HTML response could be left to the stack manager.