Added recaptcha
This commit is contained in:
45
README.md
45
README.md
@@ -4,26 +4,47 @@
|
|||||||
|
|
||||||
## Anpassung der Useranmeldung
|
## Anpassung der Useranmeldung
|
||||||
|
|
||||||
#### Registrierung ohne Captcha und E-Mail-Verifikation
|
#### Registrierung mit Google reCAPTCHA v3
|
||||||
|
|
||||||
**Beschreibung:**
|
**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:**
|
**Technische Umsetzung:**
|
||||||
- Entfernung des Captcha-Features aus dem Registrierungsprozess
|
- Integration von Google reCAPTCHA v3 (unsichtbar, keine Checkbox)
|
||||||
- Deaktivierung der E-Mail-Verifizierung
|
- Score-basierte Validierung (0.0 = Bot, 1.0 = Mensch)
|
||||||
- Automatische Aktivierung neuer Benutzerkonten
|
- Blockierung bei Score unter 0.5
|
||||||
- Vereinfachung des Registrierungsformulars
|
- Konfiguration über `RECAPTCHA_SITE_KEY` und `RECAPTCHA_SECRET_KEY`
|
||||||
|
|
||||||
**Admin-Funktion:**
|
**Code-Beispiel (View):**
|
||||||
- Nur Administratoren können über das gleiche Formular neue Benutzer anlegen
|
```html
|
||||||
- Sicherstellung, dass nur autorisierte Personen Benutzerkonten erstellen können
|
<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 Platzhalter -->
|
||||||
[📸 Screenshot: Vereinfachtes Registrierungsformular]
|
[📸 Screenshot: Registrierungsformular]
|
||||||
[📸 Screenshot: Admin-Benutzererstellung]
|
[📸 Screenshot: reCAPTCHA Badge]
|
||||||
[📸 Screenshot: Automatisch aktivierter Benutzer]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -70,12 +70,10 @@ return array(
|
|||||||
'DB_PORT' => '3306',
|
'DB_PORT' => '3306',
|
||||||
'DB_CHARSET' => 'utf8',
|
'DB_CHARSET' => 'utf8',
|
||||||
/**
|
/**
|
||||||
* Configuration for: Captcha size
|
* Configuration for: Google reCAPTCHA v2
|
||||||
* The currently used Captcha generator (https://github.com/Gregwar/Captcha) also runs without giving a size,
|
|
||||||
* so feel free to use ->build(); inside CaptchaModel.
|
|
||||||
*/
|
*/
|
||||||
'CAPTCHA_WIDTH' => 359,
|
'RECAPTCHA_SITE_KEY' => 'recaptcha-site-key',
|
||||||
'CAPTCHA_HEIGHT' => 100,
|
'RECAPTCHA_SECRET_KEY' => 'recaptcha-secret-key',
|
||||||
/**
|
/**
|
||||||
* Configuration for: Cookies
|
* Configuration for: Cookies
|
||||||
* 1209600 seconds = 2 weeks
|
* 1209600 seconds = 2 weeks
|
||||||
|
|||||||
@@ -22,23 +22,25 @@ class RegisterController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
// only admins can access registration; reuse existing admin auth check
|
if (Session::userIsLoggedIn()) {
|
||||||
Auth::checkAdminAuthentication();
|
Redirect::to('index');
|
||||||
|
return;
|
||||||
|
}
|
||||||
$this->View->render('register/index');
|
$this->View->render('register/index');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Register page action
|
|
||||||
* POST-request after form submit
|
|
||||||
*/
|
|
||||||
public function register_action()
|
public function register_action()
|
||||||
{
|
{
|
||||||
// enforce admin-only for registration
|
if (Session::userIsLoggedIn()) {
|
||||||
Auth::checkAdminAuthentication();
|
Redirect::to('index');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
RegistrationModel::registerNewUser();
|
if (RegistrationModel::registerNewUser()) {
|
||||||
|
Redirect::to('login');
|
||||||
Redirect::to('admin/index');
|
} else {
|
||||||
|
Redirect::to('register');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,15 +15,13 @@ class RegistrationModel
|
|||||||
*/
|
*/
|
||||||
public static function registerNewUser($isAdmin = false)
|
public static function registerNewUser($isAdmin = false)
|
||||||
{
|
{
|
||||||
// clean the input
|
|
||||||
$user_name = strip_tags(Request::post('user_name'));
|
$user_name = strip_tags(Request::post('user_name'));
|
||||||
$user_email = strip_tags(Request::post('user_email'));
|
$user_email = strip_tags(Request::post('user_email'));
|
||||||
// Use 'user_password' if provided (admin registration), otherwise 'user_password_new'
|
$user_password_new = Request::post('user_password_new');
|
||||||
$user_password_new = $isAdmin ? Request::post('user_password_new') : Request::post('user_password_new');
|
$user_password_repeat = $user_password_new;
|
||||||
$user_password_repeat = $user_password_new; // no repeat field
|
|
||||||
|
|
||||||
// validate using existing validators and messages
|
|
||||||
$valid = true;
|
$valid = true;
|
||||||
|
if (!self::validateRecaptcha()) { $valid = false; }
|
||||||
if (!self::validateUserName($user_name)) { $valid = false; }
|
if (!self::validateUserName($user_name)) { $valid = false; }
|
||||||
if (!self::validateUserEmail($user_email, $user_email)) { $valid = false; }
|
if (!self::validateUserEmail($user_email, $user_email)) { $valid = false; }
|
||||||
if (!self::validateUserPassword($user_password_new, $user_password_repeat)) { $valid = false; }
|
if (!self::validateUserPassword($user_password_new, $user_password_repeat)) { $valid = false; }
|
||||||
@@ -77,12 +75,35 @@ class RegistrationModel
|
|||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function validateRecaptcha()
|
||||||
* Validates the username
|
{
|
||||||
*
|
$recaptcha_response = Request::post('g-recaptcha-response');
|
||||||
* @param $user_name
|
|
||||||
* @return bool
|
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)
|
public static function validateUserName($user_name)
|
||||||
{
|
{
|
||||||
if (empty($user_name)) {
|
if (empty($user_name)) {
|
||||||
@@ -90,7 +111,6 @@ class RegistrationModel
|
|||||||
return false;
|
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)) {
|
if (!preg_match('/^[a-zA-Z0-9]{2,64}$/', $user_name)) {
|
||||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_DOES_NOT_FIT_PATTERN'));
|
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_DOES_NOT_FIT_PATTERN'));
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -37,6 +37,9 @@
|
|||||||
<div class="link-forgot-my-password">
|
<div class="link-forgot-my-password">
|
||||||
<a href="<?php echo Config::get('URL'); ?>login/requestPasswordReset">I forgot my password</a>
|
<a href="<?php echo Config::get('URL'); ?>login/requestPasswordReset">I forgot my password</a>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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(); ?>
|
<?php $this->renderFeedbackMessages(); ?>
|
||||||
|
|
||||||
<!-- login box on left side -->
|
|
||||||
<div class="login-box" style="width: 50%; display: block;">
|
<div class="login-box" style="width: 50%; display: block;">
|
||||||
<h2>Register a new account</h2>
|
<h2>Register a new account</h2>
|
||||||
|
|
||||||
<!-- register form -->
|
<form method="post" action="<?php echo Config::get('URL'); ?>register/register_action" id="register-form">
|
||||||
<form method="post" action="<?php echo Config::get('URL'); ?>register/register_action">
|
|
||||||
<input type="text" pattern="[a-zA-Z0-9]{2,64}" name="user_name" placeholder="Username (letters/numbers, 2-64 chars)" required />
|
<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="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="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" />
|
<input type="submit" value="Register" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
|||||||
Reference in New Issue
Block a user