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-langenlang/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()) ononce="…"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
- Instale el plugin: en WordPress, vaya a
Plugins → Añadir nuevoy busque «CaptchaCore» (entrada en el directorio de WordPress). Las actualizaciones llegan entonces desde WordPress. Como alternativa, suba el ZIP desde su área de cliente. - Actívalo en Plugins
- Abra Ajustes > CaptchaCore
- Introducir la URL del servicio, la site key y la secret key
- Elegir el modo del widget, el tema y el color
- 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.
- Adquiera el paquete en la tienda de plugins e instálelo en el ACP en
Paquetes → Instalar paquete - Introduzca las credenciales en
Opciones → Seguridad → Anti-spam → CaptchaCore: URL del servicio, site key y secret key - Elija la apariencia: modo, tema, color de acento e idioma. «auto» sigue el idioma del foro.
- 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.
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.
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.
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.
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.
Tener en cuenta la carga posterior
Si su sistema sustituye formularios mediante AJAX, llame después a CaptchaCoreV2.attach().
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.