Als je een Metin2-server lanceert, is de eerste deur waar spelers doorheen gaan het registratieformulier op je website. Een goed gebouwd Metin2 registratiesysteem doet meer dan een gebruikersnaam en wachtwoord ophalen; het valideert invoer, slaat het wachtwoord op in precies het formaat dat het spel verwacht en schrijft veilig naar de account-tabel. In deze gids bouwen we vanaf nul een werkende, injectiebestendige registratiestroom met PHP en PDO.
Hoe de account-tabel werkt
In klassieke Metin2-bronnen leven lidmaatschapsgegevens in de account-tabel binnen een database die account heet. De spelkern (de game- en db-processen) authenticeert logins rechtstreeks tegen deze tabel. De kolommen die ons interesseren zijn:
login— de gebruikersnaam (meestal kort en uniek).password— het veld dat het spel leest. De klassieke bron verwacht de 41-tekens hash die dePASSWORD()-functie van MySQL produceert.social_id— een 7-cijferige beveiligingscode (een tweede wachtwoord) die wordt gebruikt voor acties zoals het verwijderen van personages.emailencreate_time— contactgegevens en registratietijd.
Een cruciaal detail: de PASSWORD()-functie is verwijderd in MySQL 8.0. De meeste Metin2-servers draaien op MariaDB of MySQL 5.x, dus de functie bestaat nog, maar je moet je versie controleren. Je bent verplicht het wachtwoord op te slaan met hetzelfde algoritme als de spelkern — anders kan een speler die zich op het web registreert niet inloggen in het spel.
Een veilige databaseverbinding
Gebruik vooral PDO met prepared statements. Door gebruikersinvoer van de query te scheiden, stop je SQL-injectie bij de bron. Houd de verbinding in een apart config.php-bestand:
<?php
// config.php
$host = '127.0.0.1';
$db = 'account';
$user = 'metin2_web'; // gebruiker met rechten alleen op de account-tabel
$pass = 'STERK_WACHTWOORD';
$dsn = "mysql:host=$host;dbname=$db;charset=utf8mb4";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
Maak een aparte MySQL-gebruiker voor het web en geef die alleen de privileges die nodig zijn op account.account. Zelfs als de webserver gecompromitteerd raakt, mag de aanvaller niet bij je hele speldatabase kunnen.
Het registratieformulier en invoervalidatie
Begin met een eenvoudig maar toereikend HTML-formulier. Het echte werk is het valideren van de binnenkomende data aan de serverkant — vertrouw nooit op controles aan de clientkant.
<form method="post" action="register.php">
<input name="login" placeholder="Gebruikersnaam" required>
<input name="email" type="email" placeholder="E-mail" required>
<input name="password" type="password" placeholder="Wachtwoord" required>
<input name="password2" type="password" placeholder="Herhaal wachtwoord" required>
<input type="hidden" name="csrf" value="<?= $_SESSION['csrf'] ?>">
<button type="submit">Registreren</button>
</form>
Controleer aan de serverkant elk veld één voor één:
<?php
session_start();
require 'config.php';
// CSRF-controle
if (!hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf'] ?? '')) {
http_response_code(400);
exit('Ongeldig verzoek.');
}
$login = trim($_POST['login'] ?? '');
$email = trim($_POST['email'] ?? '');
$pw = $_POST['password'] ?? '';
$pw2 = $_POST['password2'] ?? '';
$errors = [];
if (!preg_match('/^[a-zA-Z0-9]{4,16}$/', $login)) {
$errors[] = 'Gebruikersnaam moet 4-16 letters/cijfers zijn.';
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Voer een geldig e-mailadres in.';
}
if (strlen($pw) < 6 || $pw !== $pw2) {
$errors[] = 'Wachtwoord moet minstens 6 tekens zijn en beide velden moeten overeenkomen.';
}
login beperken tot een strikt patroon (alleen letters en cijfers) voorkomt zowel compatibiliteitsproblemen met de spelclient als onverwachte tekens.
Controleer op botsingen van gebruikersnamen
Om te voorkomen dat twee mensen zich met dezelfde gebruikersnaam registreren, doe je een query voordat je schrijft. Gebruik ook hier een prepared statement:
if (!$errors) {
$stmt = $pdo->prepare('SELECT id FROM account WHERE login = ? LIMIT 1');
$stmt->execute([$login]);
if ($stmt->fetch()) {
$errors[] = 'Die gebruikersnaam is al in gebruik.';
}
}
Maak dit toch niet je enige verdedigingslinie: voeg een UNIQUE-index toe aan de login-kolom van de account-tabel. Als twee verzoeken op hetzelfde moment binnenkomen, blokkeert de beperking op databaseniveau het duplicaat.
Hash het wachtwoord en schrijf naar de account-tabel
In de laatste stap maken we het record aan. Het wachtwoord binnen de SQL hashen met PASSWORD() garandeert het formaat dat het spel verwacht. Genereer een willekeurige 7-cijferige code voor social_id:
if (!$errors) {
$social = sprintf('%07d', random_int(0, 9999999));
$sql = 'INSERT INTO account (login, password, social_id, email, create_time)
VALUES (?, PASSWORD(?), ?, ?, NOW())';
$stmt = $pdo->prepare($sql);
$stmt->execute([$login, $pw, $social, $email]);
header('Location: register.php?ok=1');
exit;
}
Let op dat we het platte wachtwoord als parameter doorgeven en MySQL het hashen laten doen. Zo blijft de veiligheid van prepared statements behouden terwijl de hash byte-voor-byte compatibel is met het spel. Als je het wachtwoord zelf in PHP hasht (bijvoorbeeld met password_hash()) en dat opslaat, herkent de spelkern het niet — een veelgemaakte fout. password_hash()/bcrypt is alleen zinvol als je een puur webgebaseerd accountsysteem bouwt dat losstaat van het spel.
Voor je naar productie gaat
Zodra de stroom werkt, voeg deze beveiligingslagen toe:
- Rate limiting: blokkeer meer dan een paar registraties per minuut vanaf hetzelfde IP om bots te vertragen.
- HTTPS verplicht: wachtwoorden mogen nooit in platte tekst reizen.
- CAPTCHA: voeg hCaptcha/reCAPTCHA toe tegen geautomatiseerde aanmeldingen.
- Foutmaskering: toon nooit technische fouten aan de gebruiker; log details aan de serverkant.
Veelgestelde vragen
Waarom het wachtwoord in MySQL hashen in plaats van in PHP?
Omdat de Metin2-spelkern het inlogwachtwoord vergelijkt met de hash die de PASSWORD()-functie van MySQL produceert. Als je niet hetzelfde algoritme gebruikt, kan een speler die zich op het web registreert het spel niet binnen. Tenzij je web en spel gescheiden systemen zijn, is overeenkomst verplicht.
Wat als mijn server de PASSWORD()-functie niet heeft?
MySQL 8.0 heeft die verwijderd. De oplossing is overstappen naar MariaDB of MySQL 5.7 (die de meeste Metin2-installaties al gebruiken), of de hash mappen die je kern ondersteunt (sommige forks leveren aangepaste schema's). In elk geval is de referentie de auth-code van je spelkern.
Waar dient social_id voor, mag ik het leeg laten?
social_id is de beveiligingscode in het spel (een tweede wachtwoord), gevraagd bij kritieke acties zoals het verwijderen van een personage. In plaats van het leeg te laten, is de veiligste aanpak een willekeurige waarde toewijzen bij registratie en de speler die laten wijzigen vanuit zijn paneel.
Een veilige registratie en accountpaneel nodig voor je Metin2-server? Ik kan het van begin tot eind bouwen met validatie, e-mailbevestiging en paneelintegratie — neem contact op.