Skip to content

New PSR for standardizing CAPTCHA (CaptchaInterface) #1330

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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions captcha.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
CaptchaInterface for various Captcha services
=============================================

This document describes common interfaces to query data. These data can be from different sources, from in-memory data to databases, as well as filesystem files.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in [RFC 2119][].

The final implementations MAY decorate the objects with more
functionality than the one proposed but they MUST implement the indicated
interfaces/functionality first.

[RFC 2119]: http://tools.ietf.org/html/rfc2119

## 1. Specification

### 1.1 Definitions

* **CAPTCHA** - A Completely Automated Public Turing test to tell Computers and Humans Apart. A challenge-response test designed to distinguish human users from automated bots.
* **Provider** - A service that implements CAPTCHA functionality by:
* Generating challenges to distinguish humans from automated systems
* Validating user responses to these challenges
* Assessing interaction risk levels
* **Client Token** - A string value generated by the **Provider**'s client-side implementation, representing a single challenge attempt.
* MAY have **Provider**-specific format and expiration
* **Verification** - The process of validating a **Client Token** against the **Provider**'s service. MUST be performed server-side. MAY include client IP validation if required by **Provider**. MAY involve additional risk analysis (e.g., behavioral **SCORING**)
* **Successful Verification** - A determination that:
* The **Client Token** is valid and unexpired
* The challenge was completed satisfactorily
* (When applicable) **SCORING** meets implementation thresholds
* **Failed Verification** Occurs when:
* The token is invalid/expired
* The challenge response was incorrect
* The risk score indicates automated behavior
* Network/configuration errors prevent validation
* **SCORING** - An OPTIONAL provider-specific value indicating confidence in the human/bot determination:
* SHOULD be represented as float between 0.0 (likely bot) and 1.0 (likely human).
* MAY be represented as vice-versa (0.0 = no risk, 1.0 = maximum risk).
* MAY, but SHOULD NOT be represented in other than float formats, e.g. 0 - 100 (as percentages)
* SHOULD be accessible through CaptchaResponseInterface extensions
* Thresholds for success/failure are implementation-defined
* Threshold SHOULD be configurable via CaptchaInterface extensions

### 1.2 Goal
This specification establishes a standardized interface for CAPTCHA implementations in PHP with the primary objective of **enabling immediate, low-effort substitution of CAPTCHA providers during critical vendor lock-in scenarios**. Specifically addressing real-world crises where:
- Providers discontinue services or change pricing models abruptly
- Security vulnerabilities require emergency provider migration
- Sudden service bans

The interface shall achieve:
1. **Zero-Refactor Replacement**: Allow switching providers (e.g., Google reCAPTCHA -> hCaptcha -> Cloudflare Turnstile) through configuration/vendor changes only
2. **DI Container Readiness**: Enable dependency injection of any compliant implementation without call-site modifications
3. **Vendor Crisis Resilience**: Mitigate business continuity risks during:
- Access restrictions
- Provider API shutdowns
- Compliance requirement changes
4. **Cost Containment**: Eliminate:
- System-wide code refactoring during migrations
- Parallel implementation maintenance
- Provider-specific testing overhead

## 2. Interfaces

### 2.1 CaptchaInterface
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should now read:

### 2.1 CaptchaVerifierInterface

Due to the code change.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


```php
namespace Psr\Captcha;

/**
* Interface of Captcha service itself.
* MUST decide whether user passed the Captcha or not and return corresponding response.
* SHOULD contain method to configure SCORING threshold (if applicable by PROVIDER)
* SHOULD throw a CaptchaException as soon as possible if appears any non-user related error that prevents correct Captcha solving (e.g. network problems, incorrect secret token, e.g.)
*/
interface CaptchaInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there might be a need for challenge(string $token): string method which, given a token, generates the challenge for the user to solve.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk what did you mean by this, probably CaptchaInterface needs to be renamed to CaptchaVerifierInterface, 'cause that is its purpose - to verify that passed token is valid. But idk what else can be retrieved from $token, especially in string. Do you mean challenge is frontend rendered I am not a robot checkbox with traffic lights? If so, i'm not sure that service should interact with it - it's just a verifier

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, for example, given a token that is 24, challenge might be 20+4 or something like that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that interface is not quite for that purpose, but rather to verify already solved captcha task by its token using some external (or even internal, it doesnt actually matter) captcha service. Roughly, it's an interface to build SDK on

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Makes sense.

{
/**
* Verifies client token and decides whether verification was successful or not (is user a bot or not).
*
* @return CaptchaResponseInterface
* @throws CaptchaException if Captcha cannot be validated (e.g. due to network problems, incorrect secret token, etc.)
*/
public function verify(string $token): CaptchaResponseInterface;
}
```

### 2.2 CaptchaResponseInterface

```php
namespace Psr\Captcha;

/**
* Interface of the object that CaptchaInterface obliged to return on ::verify() method.
* MUST contain enough information to consistently say whether user succesfully passed Captcha or not.
* SHOULD contain actual user's SCORING
* MAY contain additional information (e.g., gathered from it's captcha-vendor service's verification endpoint) (i.e. message, errors, etc.)
*/
interface CaptchaResponseInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it an interface and not a bool?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean, why CaptchaInterface::verify() returns that instead of a bool? If yes, then answer is - 'cause you might want to implement additional methods to specific CaptchaResponse classes, like getScore(), getHost() (for instance, those fields implemented by Google ReCaptcha, hCaptcha, SmartCaptcha) or similar - to extend response data

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be mentioned in meta-document.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mentioned it in captcha-meta.md (added meta-document)

{
/**
* Return true/false depends on whether verification was successful or not (is user a bot or not).
*
* @return bool
*/
public function isSuccess(): bool;
}
```

### 2.3 CaptchaException

```php
namespace Psr\Captcha;

/**
* MUST be thrown from CaptchaInterface methods if Captcha test itself cannot be passed due to any reason that is not user-related - network problems, incorrect secret token, unable to parse request-response, etc.
*/
interface CaptchaException extends \RuntimeException
{
}
```