Added recaptcha

This commit is contained in:
2026-01-12 11:05:01 +01:00
parent a4d386f2c5
commit 01861f08c6
6 changed files with 101 additions and 45 deletions

View File

@@ -4,26 +4,47 @@
## Anpassung der Useranmeldung
#### Registrierung ohne Captcha und E-Mail-Verifikation
#### Registrierung mit Google reCAPTCHA v3
**Beschreibung:**
Die Registrierung wurde so angepasst, dass Benutzer ohne Captcha und E-Mail-Verifikation registriert werden können. Benutzer werden nach der Registrierung automatisch aktiviert.
Die Registrierung wurde mit Google reCAPTCHA v3 abgesichert. reCAPTCHA v3 arbeitet unsichtbar im Hintergrund und analysiert das Benutzerverhalten, um Bots zu erkennen. E-Mail-Verifizierung ist deaktiviert - Benutzer werden nach der Registrierung automatisch aktiviert.
**Technische Umsetzung:**
- Entfernung des Captcha-Features aus dem Registrierungsprozess
- Deaktivierung der E-Mail-Verifizierung
- Automatische Aktivierung neuer Benutzerkonten
- Vereinfachung des Registrierungsformulars
- Integration von Google reCAPTCHA v3 (unsichtbar, keine Checkbox)
- Score-basierte Validierung (0.0 = Bot, 1.0 = Mensch)
- Blockierung bei Score unter 0.5
- Konfiguration über `RECAPTCHA_SITE_KEY` und `RECAPTCHA_SECRET_KEY`
**Admin-Funktion:**
- Nur Administratoren können über das gleiche Formular neue Benutzer anlegen
- Sicherstellung, dass nur autorisierte Personen Benutzerkonten erstellen können
**Code-Beispiel (View):**
```html
<script src="https://www.google.com/recaptcha/api.js?render=SITE_KEY"></script>
<script>
grecaptcha.ready(function() {
grecaptcha.execute('SITE_KEY', {action: 'register'}).then(function(token) {
document.getElementById('recaptcha-response').value = token;
});
});
</script>
```
**Code-Beispiel (Server-Validierung):**
```php
$response = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret=' . $secret_key . '&response=' . $recaptcha_response);
$response_data = json_decode($response);
if (!$response_data->success || $response_data->score < 0.5) {
// Bot erkannt oder Validierung fehlgeschlagen
}
```
**Vorteile von v3:**
- Keine Benutzerinteraktion erforderlich
- Bessere User Experience
- Intelligente Bot-Erkennung durch Verhaltensanalyse
```
<!-- Screenshot Platzhalter -->
[📸 Screenshot: Vereinfachtes Registrierungsformular]
[📸 Screenshot: Admin-Benutzererstellung]
[📸 Screenshot: Automatisch aktivierter Benutzer]
[📸 Screenshot: Registrierungsformular]
[📸 Screenshot: reCAPTCHA Badge]
```
---

View File

@@ -70,12 +70,10 @@ return array(
'DB_PORT' => '3306',
'DB_CHARSET' => 'utf8',
/**
* Configuration for: Captcha size
* The currently used Captcha generator (https://github.com/Gregwar/Captcha) also runs without giving a size,
* so feel free to use ->build(); inside CaptchaModel.
* Configuration for: Google reCAPTCHA v2
*/
'CAPTCHA_WIDTH' => 359,
'CAPTCHA_HEIGHT' => 100,
'RECAPTCHA_SITE_KEY' => 'recaptcha-site-key',
'RECAPTCHA_SECRET_KEY' => 'recaptcha-secret-key',
/**
* Configuration for: Cookies
* 1209600 seconds = 2 weeks

View File

@@ -22,23 +22,25 @@ class RegisterController extends Controller
*/
public function index()
{
// only admins can access registration; reuse existing admin auth check
Auth::checkAdminAuthentication();
if (Session::userIsLoggedIn()) {
Redirect::to('index');
return;
}
$this->View->render('register/index');
}
/**
* Register page action
* POST-request after form submit
*/
public function register_action()
{
// enforce admin-only for registration
Auth::checkAdminAuthentication();
if (Session::userIsLoggedIn()) {
Redirect::to('index');
return;
}
RegistrationModel::registerNewUser();
Redirect::to('admin/index');
if (RegistrationModel::registerNewUser()) {
Redirect::to('login');
} else {
Redirect::to('register');
}
}
/**

View File

@@ -15,15 +15,13 @@ class RegistrationModel
*/
public static function registerNewUser($isAdmin = false)
{
// clean the input
$user_name = strip_tags(Request::post('user_name'));
$user_email = strip_tags(Request::post('user_email'));
// Use 'user_password' if provided (admin registration), otherwise 'user_password_new'
$user_password_new = $isAdmin ? Request::post('user_password_new') : Request::post('user_password_new');
$user_password_repeat = $user_password_new; // no repeat field
$user_password_new = Request::post('user_password_new');
$user_password_repeat = $user_password_new;
// validate using existing validators and messages
$valid = true;
if (!self::validateRecaptcha()) { $valid = false; }
if (!self::validateUserName($user_name)) { $valid = false; }
if (!self::validateUserEmail($user_email, $user_email)) { $valid = false; }
if (!self::validateUserPassword($user_password_new, $user_password_repeat)) { $valid = false; }
@@ -77,12 +75,35 @@ class RegistrationModel
return $return;
}
/**
* Validates the username
*
* @param $user_name
* @return bool
*/
public static function validateRecaptcha()
{
$recaptcha_response = Request::post('g-recaptcha-response');
if (empty($recaptcha_response)) {
Session::add('feedback_negative', 'reCAPTCHA verification failed. Please try again.');
return false;
}
$secret_key = Config::get('RECAPTCHA_SECRET_KEY');
$verify_url = 'https://www.google.com/recaptcha/api/siteverify';
$response = file_get_contents($verify_url . '?secret=' . $secret_key . '&response=' . $recaptcha_response);
$response_data = json_decode($response);
if (!$response_data->success) {
Session::add('feedback_negative', 'reCAPTCHA verification failed. Please try again.');
return false;
}
// v3 returns a score from 0.0 to 1.0 (1.0 = likely human, 0.0 = likely bot)
if (isset($response_data->score) && $response_data->score < 0.5) {
Session::add('feedback_negative', 'Registration blocked due to suspicious activity.');
return false;
}
return true;
}
public static function validateUserName($user_name)
{
if (empty($user_name)) {
@@ -90,7 +111,6 @@ class RegistrationModel
return false;
}
// if username is too short (2), too long (64) or does not fit the pattern (aZ09)
if (!preg_match('/^[a-zA-Z0-9]{2,64}$/', $user_name)) {
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_DOES_NOT_FIT_PATTERN'));
return false;

View File

@@ -37,6 +37,9 @@
<div class="link-forgot-my-password">
<a href="<?php echo Config::get('URL'); ?>login/requestPasswordReset">I forgot my password</a>
</div>
<div class="link-register" style="margin-top: 15px;">
<a href="<?php echo Config::get('URL'); ?>register" class="button">Register new account</a>
</div>
</div>
</div>
</div>

View File

@@ -1,18 +1,30 @@
<div class="container">
<script src="https://www.google.com/recaptcha/api.js?render=<?php echo Config::get('RECAPTCHA_SITE_KEY'); ?>"></script>
<!-- echo out the system feedback (error and success messages) -->
<div class="container">
<?php $this->renderFeedbackMessages(); ?>
<!-- login box on left side -->
<div class="login-box" style="width: 50%; display: block;">
<h2>Register a new account</h2>
<!-- register form -->
<form method="post" action="<?php echo Config::get('URL'); ?>register/register_action">
<form method="post" action="<?php echo Config::get('URL'); ?>register/register_action" id="register-form">
<input type="text" pattern="[a-zA-Z0-9]{2,64}" name="user_name" placeholder="Username (letters/numbers, 2-64 chars)" required />
<input type="text" name="user_email" placeholder="email address (a real address)" required />
<input type="password" name="user_password_new" pattern=".{6,}" placeholder="Password (6+ characters)" required autocomplete="off" />
<input type="hidden" name="g-recaptcha-response" id="recaptcha-response" />
<input type="submit" value="Register" />
</form>
</div>
</div>
<script>
document.getElementById('register-form').addEventListener('submit', function(e) {
e.preventDefault();
var form = this;
grecaptcha.ready(function() {
grecaptcha.execute('<?php echo Config::get('RECAPTCHA_SITE_KEY'); ?>', {action: 'register'}).then(function(token) {
document.getElementById('recaptcha-response').value = token;
form.submit();
});
});
});
</script>