Skip to content

Passwords

imshooter edited this page Oct 24, 2026 · 1 revision

A password is never stored. A hash of it is, made slow on purpose so that a stolen table cannot be guessed back. The component hashes and verifies on two threads of its own, apart from the connections, so neither the game nor the queries wait.

Argon2id

MySQL_HashArgon2 hashes with Argon2id, the winner of the Password Hashing Competition. Its defaults are the least costs OWASP recommends, 19 MiB of memory and two passes, which take some tens of milliseconds.

new Statement:gAddAccount;

forward OnPasswordHashed(playerid, const hash[]);

stock Register(playerid, const password[]) {
    if (strlen(password) < 8) {
        return SendClientMessage(playerid, -1, "The password needs eight characters or more.");
    }

    MySQL_HashArgon2(password, "OnPasswordHashed", playerid);

    return 1;
}

public OnPasswordHashed(playerid, const hash[]) {
    new
        name[MAX_PLAYER_NAME + 1]
    ;

    if (hash[0] == EOS) {
        return SendClientMessage(playerid, -1, "The account could not be made.");
    }

    GetPlayerName(playerid, name, sizeof (name));
    MySQL_Execute(gAddAccount, "", 0, name, hash);

    return 1;
}

A hash fits in MYSQL_HASH_LENGTH characters, and a VARCHAR(128) column holds it.

bcrypt

MySQL_HashBcrypt hashes with bcrypt, as $2b$, for a database shared with a website or a program that reads only bcrypt. Its default cost is 12. Only the first 72 bytes of a password count.

Verifying

MySQL_VerifyPassword reads the algorithm, the costs and the salt from the hash, so it verifies any Argon2 or bcrypt hash, made by this component or by any other library.

forward OnPasswordChecked(playerid, bool:match);

stock Login(playerid, const password[], const hash[]) {
    MySQL_VerifyPassword(password, hash, "OnPasswordChecked", playerid);
}

public OnPasswordChecked(playerid, bool:match) {
    if (!match) {
        return Kick(playerid);
    }

    SpawnPlayer(playerid);

    return 1;
}

The public is called on a later server tick. A player who left meanwhile may have been replaced by another in the same slot, so a script that cares passes a value that tells them apart, such as an index into its own records.

Raising the costs

The costs are stored in each hash, so they can be raised at any time. The hashes made before keep verifying with their own costs, and a script may hash the password again with the new costs when a player logs in.

With the defaults, an Argon2id hash takes about 36 ms on a desktop processor. Each step of the bcrypt cost doubles its time. Argon2id grows with its memory and passes. Two hashes run at once, and the next wait in a queue, so a server where many players log in at the same moment answers them in turn.

Clone this wiki locally