Integraciones

Paquetes listos para Laravel, Symfony, WordPress y WoltLab Suite, y la guía para crear el suyo propio.

Integración Laravel

1. Añadir el repositorio

Añada el repositorio de CaptchaCore a su composer.json:

{
    "repositories": [
        {
            "type": "composer",
            "url": "https://captchacore.eu/packages"
        }
    ]
}

2. Instalar el paquete

composer require captchacore/laravel

# optional
php artisan vendor:publish --tag=captchacore-config
php artisan vendor:publish --tag=captchacore-lang

Laravel registra el ServiceProvider, el alias del middleware, el componente Blade y la facade mediante auto-discovery. Solo necesita publicar la configuración si desea cambiar valores fuera de .env.

3. Configurar .env

CAPTCHACORE_URL=https://api.captchacore.eu
CAPTCHACORE_SITE_KEY=cc_pub_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
CAPTCHACORE_SECRET_KEY=cc_sec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

# optional
CAPTCHACORE_TIMEOUT=3             # Sekunden
CAPTCHACORE_FAIL_OPEN=true        # bei Störung des Dienstes durchlassen
CAPTCHACORE_WIDGET_MODE=          # interactive | visible | invisible
CAPTCHACORE_WIDGET_THEME=         # auto | light | dark
CAPTCHACORE_WIDGET_COLOR=         # z. B. #34d399
CAPTCHACORE_WIDGET_LABEL=
CAPTCHACORE_WIDGET_BRAND=
CAPTCHACORE_WIDGET_SIZE=          # normal | compact
CAPTCHACORE_WIDGET_LANG=          # de | en | fr | es …
CAPTCHACORE_WIDGET_ENDPOINT=eu    # eu | global
CAPTCHACORE_WIDGET_CDN_URL=       # eigener Widget-Host (optional)

El paquete no emite las opciones del widget que se dejan vacías: el widget utiliza entonces la configuración del área de cliente y el idioma de la página.

Componente Blade

<form method="post" action="/contact">
    @csrf
    <!-- … -->
    <x-captchacore::widget form-type="contact" />

    @error('captchacore')
        <p class="error">{{ $message }}</p>
    @enderror

    <button type="submit">Absenden</button>
</form>

El tipo de formulario del widget y de la comprobación debe ser el mismo: form-type="contact" corresponde a captchacore:contact o new CaptchaCoreToken('contact'). Cada tipo tiene su propia dificultad; un tipo distinto se rechaza.

Middleware

use App\Http\Controllers\ContactController;
use Illuminate\Support\Facades\Route;

Route::post('/contact', [ContactController::class, 'send'])
    ->middleware('captchacore:contact');

Se comprueban POST, PUT, PATCH y DELETE. En caso de rechazo, el middleware redirige de vuelta con los datos introducidos y el mensaje bajo la clave captchacore. Las peticiones mediante fetch() o XHR reciben JSON con estado 422 y errors.captchacore; para ello envíe Accept: application/json (ejemplo en «Enviar formularios con fetch()»).

Regla de validación

use CaptchaCore\Laravel\Rules\CaptchaCoreToken;

'captchacore_token' => [new CaptchaCoreToken('contact')],

La regla se aplica también sin token: required no es necesario y generaría un segundo mensaje. El mensaje se encuentra bajo el nombre del campo: @error('captchacore_token').

Facade

use CaptchaCore\Laravel\Facades\CaptchaCore;

$result = CaptchaCore::verify($request->input('captchacore_token'), 'comment');

if (! $result->passed()) {
    return back()->withInput()->withErrors(['captchacore' => __('captchacore::messages.failed')]);
}

Mensajes, logs y proxies

  • Los mensajes se incluyen en alemán, inglés, español y francés y siguen el idioma de la aplicación. Puede adaptarlos tras vendor:publish --tag=captchacore-lang en lang/vendor/captchacore.
  • Los rechazos se registran como info con motivos y puntuación de riesgo, las caídas con fail-open como warning; el token en sí nunca se registra.
  • Detrás de un balanceador de carga, CDN o proxy inverso, configure TrustProxies ($middleware->trustProxies(at: …)); de lo contrario, CaptchaCore verá la IP del proxy para todos los visitantes.
  • CSP estricta: el componente toma el nonce de Vite (Vite::useCspNonce()) o nonce="…" y lo añade a la etiqueta script.

Tests

use CaptchaCore\Laravel\Facades\CaptchaCore;

CaptchaCore::fake();   // jede Prüfung besteht; wird bei jedem App-Start zurückgesetzt
$this->post('/contact', ['captchacore_token' => 'fake'])->assertRedirect();

Bundle de Symfony

Para Symfony 6.4 LTS, 7.x y 8.x. Cuatro vías de integración: FormType, Validator-Constraint, atributos de controlador y un Security-Badge para el inicio de sesión.

1. Instalar

Añada el repositorio de CaptchaCore a su composer.json:

{
    "repositories": [
        { "type": "composer", "url": "https://captchacore.eu/packages" }
    ]
}
composer require captchacore/captchacore-bundle

Registre el bundle en config/bundles.php y cree la configuración como config/packages/captchacore.yaml: Ambos pasos son manuales: no existe una receta de Flex para el bundle.

// config/bundles.php
CaptchaCore\SymfonyBundle\CaptchaCoreBundle::class => ['all' => true],

# config/packages/captchacore.yaml
captchacore:
    service_url: '%env(CAPTCHACORE_URL)%'
    site_key: '%env(CAPTCHACORE_SITE_KEY)%'
    secret_key: '%env(CAPTCHACORE_SECRET_KEY)%'

2. Configurar .env

CAPTCHACORE_URL=https://api.captchacore.eu
CAPTCHACORE_SITE_KEY=cc_pub_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
CAPTCHACORE_SECRET_KEY=cc_sec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

FormType (recomendado)

use CaptchaCore\SymfonyBundle\Form\Type\CaptchaCoreType;

$builder
    ->add('email', EmailType::class)
    ->add('message', TextareaType::class)
    ->add('captcha', CaptchaCoreType::class, [
        'form_type' => 'contact',
    ]);

La vía recomendada para formularios HTML: un rechazo aparece como un error normal del formulario. El widget envía siempre su token como captchacore_token; el FormType lo adopta antes de la validación.

Atributos de controlador

use CaptchaCore\SymfonyBundle\Security\Attribute\RequiresCaptcha;

#[Route('/contact', methods: ['POST'])]
#[RequiresCaptcha(formType: 'contact')]
public function submit(Request $request): Response { /* ... */ }

Un rechazo termina con el estado 403 (en el log como info, no como error), adecuado para endpoints llamados mediante JavaScript. Con throwOnFailure: false se obtiene en su lugar JSON con estado 422. Para formularios HTML utilice el FormType. El atributo no protege el inicio de sesión, porque el firewall autentica antes del controlador.

Validator-Constraint (DTOs)

Para #[MapRequestPayload], Symfony necesita además symfony/serializer y symfony/property-access.

use CaptchaCore\SymfonyBundle\Validator\CaptchaCoreToken;

final class ContactDto {
    public function __construct(
        #[Assert\NotBlank]
        public string $email,

        #[CaptchaCoreToken(formType: 'contact')]
        public string $captchaToken,
    ) {}
}

Programático

use CaptchaCore\SymfonyBundle\Client\CaptchaCoreClient;

$result = $this->captcha->verify($token, 'login');

if ($result->blocked()) {
    throw new AccessDeniedHttpException();
}

// Properties: valid, riskScore, action, confidence, reasons, stepUp

Twig (independiente)

{{ captchacore_widget(form_type: 'contact') }}

@{# Strikte CSP: Nonce Ihrer Seite mitgeben #}
{{ captchacore_widget(form_type: 'contact', nonce: page_nonce) }}
{{ form_row(form.captcha, {nonce: page_nonce}) }}

Sin form_type se aplica default_form_type, igual que con el FormType. El script se incluye solo una vez por página, incluso con varios widgets o combinado con el FormType. El widget utiliza el nonce de su etiqueta script para su elemento style; permítalo en script-src y style-src-elem. Un host propio para el widget se define con widget.cdn_url.

Proteger el inicio de sesión

El form_login estándar de Symfony no está protegido: solo se comprueban los inicios de sesión que llevan un CaptchaCoreBadge. Sustituya form_login por un authenticator propio que añada el badge; el token se comprobará entonces antes de la contraseña.

// src/Security/LoginFormAuthenticator.php
namespace App\Security;

use CaptchaCore\SymfonyBundle\Security\CaptchaCoreAuthenticator;
use CaptchaCore\SymfonyBundle\Security\CaptchaCoreBadge;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\SecurityRequestAttributes;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

final class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
    use TargetPathTrait;

    public function __construct(private readonly UrlGeneratorInterface $urlGenerator) {}

    public function authenticate(Request $request): Passport
    {
        $payload = $request->getPayload();
        $username = $payload->getString('_username');
        $request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $username);

        return new Passport(
            new UserBadge($username),
            new PasswordCredentials($payload->getString('_password')),
            [
                new CaptchaCoreBadge(CaptchaCoreAuthenticator::extractToken($request) ?? '', 'login'),
                new CsrfTokenBadge('authenticate', $payload->getString('_csrf_token')),
                new RememberMeBadge(),
            ],
        );
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        $target = $this->getTargetPath($request->getSession(), $firewallName);

        return new RedirectResponse($target ?? $this->urlGenerator->generate('app_home'));
    }

    protected function getLoginUrl(Request $request): string
    {
        return $this->urlGenerator->generate('app_login');
    }
}
# config/packages/security.yaml — statt form_login
security:
    firewalls:
        main:
            custom_authenticator: App\Security\LoginFormAuthenticator
            logout:
                path: app_logout

@{# templates/security/login.html.twig, im <form method="post"> #}
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
{{ captchacore_widget(form_type: 'login') }}

app_login es la ruta de su página de inicio de sesión (GET muestra el formulario, POST lo gestiona el authenticator), app_home la página tras iniciar sesión. El ejemplo completo con controlador y plantilla se encuentra en el README del bundle.

Tests

CaptchaCoreClient::fake(VerificationResult::allow());
$this->client->request('POST', '/contact', [...]);
self::assertResponseIsSuccessful();

Plugin de WordPress

  1. Instale el plugin: en WordPress, vaya a Plugins → Añadir nuevo y busque «CaptchaCore» (entrada en el directorio de WordPress). Las actualizaciones llegan entonces desde WordPress. Como alternativa, suba el ZIP desde su área de cliente.
  2. Actívalo en Plugins
  3. Abra Ajustes > CaptchaCore
  4. Introducir la URL del servicio, la site key y la secret key
  5. Elegir el modo del widget, el tema y el color
  6. Activar los formularios deseados (login, registro, comentarios, etc.)

Sin necesidad de código. El plugin integra el widget automáticamente en todos los formularios de WordPress activados y verifica los tokens en el servidor.

WoltLab Suite

El paquete se distribuye a través de la tienda oficial de plugins de WoltLab. La instalación y todas las actualizaciones futuras pasan por ella: no hay una descarga propia en el área de cliente.

  1. Adquiera el paquete en la tienda de plugins e instálelo en el ACP en Paquetes → Instalar paquete
  2. Introduzca las credenciales en Opciones → Seguridad → Anti-spam → CaptchaCore: URL del servicio, site key y secret key
  3. Elija la apariencia: modo, tema, color de acento e idioma. «auto» sigue el idioma del foro.
  4. Seleccione CaptchaCore como captcha en Opciones → Seguridad → Anti-spam → Captcha: a partir de ahí se aplica al registro, al formulario de contacto y a todos los demás formularios del sistema
Ajuste Significado
Endpoint Endpoint estándar de la UE o mundial. La UE mantiene todos los datos de verificación dentro de la UE; el endpoint global acelera la respuesta para visitantes lejanos.
Modo interactive, visible o invisible: los mismos tres modos que en el resto, véase modos del widget.
Comportamiento ante una caída Si no se puede contactar con CaptchaCore, el plugin deja pasar el formulario por defecto y registra el error. Se puede desactivar si prefiere bloquear en caso de duda.

Un paquete para 6.1 y 6.2. El handler verifica cada token en el servidor contra la API V2; la comprobación adicional («step_up») la resuelve el propio widget y el visitante recibe un aviso propio en lugar de un error.

Crear una integración propia

¿Quiere llevar CaptchaCore a un sistema para el que aún no hay paquete, como Shopware, Joomla o un framework propio? El esfuerzo es de unas pocas horas. Este orden ha demostrado funcionar.

01

Servir el script

Incluya captchacore-v2.min.js desde el endpoint de la UE y pase la clave de sitio y la URL del servicio como atributos data. En un CMS esto corresponde al hook que registra los scripts del frontend. Cargue el script solo en páginas que contengan realmente un formulario protegido.

02

Marcar los formularios

Ponga data-captchacore en el formulario e inserte un contenedor vacío con data-captchacore-widget. Con data-form-type decide qué política se aplica. Varios formularios en una página no son problema: cada uno recibe su propio widget.

03

Comprobar el token en el servidor

Lea el campo captchacore_token de la petición y envíelo con su clave secreta a /api/v2/verify. Compruebe decision y no trate step_up como block.

04

Gestionar las caídas

Fije un tiempo de espera de unos tres segundos y decida conscientemente si una caída deja pasar o bloquea. Hágalo configurable en el complemento: la respuesta varía según el formulario.

05

Tener en cuenta la carga posterior

Si su sistema sustituye formularios mediante AJAX, llame después a CaptchaCoreV2.attach().

06

Ofrecer ajustes

La clave de sitio, la clave secreta, la selección de formularios protegidos, el aspecto y el comportamiento ante caídas deben estar en la pantalla de configuración de su complemento.

Plantillas de las que copiar

Todas las integraciones suministradas siguen el mismo patrón. Para un sistema basado en Symfony como Shopware 6, el bundle de Symfony es la plantilla más cercana; para un CMS clásico, el complemento de WordPress.

Comprobar antes de publicar

  • Formulario enviado sin JavaScript: ¿se aplica su regla de fallo?
  • Token usado dos veces: la segunda comprobación debe fallar, el nonce es de un solo uso.
  • Token enviado tras más de cinco minutos: el widget lo renueva solo, compruébelo en una prueba prolongada.
  • Dos formularios protegidos en una página: ambos deben funcionar de forma independiente.
  • Formulario cargado por AJAX: ¿aparece el widget?
  • La clave secreta no aparece en ningún lugar del HTML ni en JavaScript.
  • Una respuesta con decision=step_up: el usuario no debe acabar en un callejón sin salida.

¿Está creando una integración y quiere que la incluyamos aquí o que la mantengamos con usted? Póngase en contacto: facilitamos accesos de prueba y una persona de contacto.

← Atrás Primeros pasos Siguiente → Widget