🔴 Neue Updates auf Gitea'; $links[] = $update_link; } return $links; } // === 2. Admin-Dashboard-Hinweis bei neuer Version === add_action('admin_init', 'wp_multi_2fa_check_update_notice'); function wp_multi_2fa_check_update_notice() { if (!current_user_can('update_plugins')) return; $transient = get_transient('wp_multi_2fa_gitea_version'); if ($transient === false) { $response = wp_remote_get('https://git.viper.ipv64.net/api/v1/repos/M_Viper/WP-Multi-2FA/releases/latest'); if (is_wp_error($response)) return; $data = json_decode(wp_remote_retrieve_body($response)); if (isset($data->tag_name)) { $latest = ltrim($data->tag_name, 'v'); // Entfernt evtl. "v" vor der Versionsnummer set_transient('wp_multi_2fa_gitea_version', $latest, 12 * HOUR_IN_SECONDS); } else { return; } } else { $latest = $transient; } if (!function_exists('get_plugin_data')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $plugin_data = get_plugin_data(__FILE__); $current_version = $plugin_data['Version']; if (version_compare($latest, $current_version, '>')) { add_action('admin_notices', function () use ($latest) { ?>

WP Multi 2FA: Eine neue Version () ist verfügbar. Jetzt ansehen auf Gitea.

Allgemein add_action('admin_init', [$this, 'register_settings_field']); // Session für 2FA-Code speichern if (!session_id()) { session_start(); } } private function get_user_2fa_data($user_id) { $all = get_option($this->option_name, []); return $all[$user_id] ?? null; } private function set_user_2fa_data($user_id, $data) { $all = get_option($this->option_name, []); $all[$user_id] = $data; update_option($this->option_name, $all); } public function show_2fa_settings($user) { if (!current_user_can('edit_user', $user->ID)) return; $data = $this->get_user_2fa_data($user->ID); $enabled = $data['enabled'] ?? false; $secret = $data['secret'] ?? null; $backup_codes = $data['backup_codes'] ?? []; if (!$secret && $enabled) { $secret = $this->generate_secret(); $data['secret'] = $secret; $backup_codes = $this->generate_backup_codes(); $data['backup_codes'] = $backup_codes; $this->set_user_2fa_data($user->ID, $data); } ?>

2-Faktor-Authentifizierung

>

Aktiviere die Zwei-Faktor-Authentifizierung für dein Konto.

QR-Code (für Authenticator-App) QR Code

Scanne diesen QR-Code mit einer App wie Google Authenticator oder Authy.

Backup-Codes

Bewahre diese Codes sicher auf. Jeder Code kann einmal zur Anmeldung verwendet werden.

get_user_2fa_data($user_id) ?? []; $enabled = isset($_POST['wp2fa_enabled']) && $_POST['wp2fa_enabled'] == '1'; if ($enabled && empty($data['secret'])) { $data['secret'] = $this->generate_secret(); $data['backup_codes'] = $this->generate_backup_codes(); } if (!$enabled) { // Deaktivieren: löschen $data = []; } else { $data['enabled'] = true; } $this->set_user_2fa_data($user_id, $data); } private function generate_secret() { $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; $secret = ''; for ($i=0; $i < 16; $i++) { $secret .= $chars[random_int(0, 31)]; } return $secret; } private function generate_backup_codes() { $codes = []; for ($i = 0; $i < 10; $i++) { $codes[] = strtoupper(bin2hex(random_bytes(4))); } return $codes; } private function get_qr_code_url($email, $secret) { $issuer = rawurlencode(get_bloginfo('name')); $label = rawurlencode(get_bloginfo('name') . ':' . $email); $otpauth = "otpauth://totp/{$label}?secret={$secret}&issuer={$issuer}"; $chl = urlencode($otpauth); return "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data={$chl}"; } // Nach Login prüfen, ob 2FA nötig public function after_login($user_login, $user) { if (!$this->needs_2fa($user->ID)) return; $_SESSION['wp2fa_user'] = $user->ID; wp_logout(); wp_redirect(site_url('/wp-login.php?action=wp2fa')); exit; } private function needs_2fa($user_id) { $data = $this->get_user_2fa_data($user_id); $admin_forced = get_option('wp2fa_admin_forced', false); return ($data['enabled'] ?? false) || $admin_forced; } // 2FA Handler: Formular verarbeiten und ggf. anzeigen (Popup) public function handle_2fa() { if (!isset($_GET['action']) || $_GET['action'] !== 'wp2fa') return; $user_id = $_SESSION['wp2fa_user'] ?? 0; if (!$user_id) { wp_redirect(wp_login_url()); exit; } $error = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $code = strtoupper(trim($_POST['wp2fa_code'] ?? '')); $data = $this->get_user_2fa_data($user_id); if (!$data) { wp_redirect(wp_login_url()); exit; } $secret = $data['secret'] ?? ''; $backup_codes = $data['backup_codes'] ?? []; if ($this->verify_code($secret, $code)) { // Erfolgreicher Authenticator-Code wp_set_auth_cookie($user_id); unset($_SESSION['wp2fa_user']); wp_redirect(admin_url()); exit; } elseif (in_array($code, $backup_codes, true)) { // Backup-Code erfolgreich, diesen Code löschen $backup_codes = array_diff($backup_codes, [$code]); $data['backup_codes'] = $backup_codes; $this->set_user_2fa_data($user_id, $data); wp_set_auth_cookie($user_id); unset($_SESSION['wp2fa_user']); wp_redirect(admin_url()); exit; } else { $error = 'Falscher Code!'; } } $this->show_2fa_form($error); exit; } private function show_2fa_form($error = '') { ?> 2-Faktor-Authentifizierung get_totp_code($secret, $timeSlice + $i); if ($calc === $code) return true; } return false; } private function get_totp_code($secret, $timeSlice) { $secretkey = $this->base32_decode($secret); $time = pack('N*', 0) . pack('N*', $timeSlice); $hash = hash_hmac('sha1', $time, $secretkey, true); $offset = ord(substr($hash, -1)) & 0x0F; $truncatedHash = substr($hash, $offset, 4); $code = unpack('N', $truncatedHash)[1] & 0x7FFFFFFF; $code = $code % 1000000; return str_pad($code, 6, '0', STR_PAD_LEFT); } private function base32_decode($b32) { $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; $b32 = strtoupper($b32); $l = strlen($b32); $n = 0; $j = 0; $binary = ''; for ($i = 0; $i < $l; $i++) { $n = $n << 5; // 5 bits per char $n = $n + strpos($alphabet, $b32[$i]); $j += 5; if ($j >= 8) { $j -= 8; $binary .= chr(($n & (0xFF << $j)) >> $j); } } return $binary; } // Registrierung des Admin-Feldes in Einstellungen > Allgemein public function register_settings_field() { register_setting('general', 'wp2fa_admin_forced', [ 'type' => 'boolean', 'description' => '2FA für alle Benutzer erzwingen', 'sanitize_callback' => [$this, 'sanitize_bool'], 'default' => false, ]); add_settings_field( 'wp2fa_admin_forced', '2FA für alle Benutzer erzwingen', [$this, 'render_admin_forced_field'], 'general' ); } public function render_admin_forced_field() { $forced = get_option('wp2fa_admin_forced', false); ?> />