Indexing of hash-value column vs INT IDs for session tokens
0
votes
1
answer
161
views
Let's say:
- your application should use session IDs generated with a CSPRNG, which shall authenticate your user sessions
- your system uses PHP 8.2 and MariaDB 10.6
Which of the approaches below performs better:
Approach A (Index on hashed session ID):
- the PK column of your sessions table is the one which holds the output of PHP's
password_hash
function, applied to a value generated via a CSPRNG.
- upon every incoming request, you hash the session ID delivered by the client, and query the DB by that hash.
Approach B (Index on decrypted INT session ID):
- the PK column of your sessions table is a simple integer, which is encrypted before being delivered to your client.
- upon every incoming request, you decrypt the session ID delivered by the client.
- if the decryption fails, someone tampered with the token --> return 401
- if the decryption succeeds, you query by the accordingly obtained session ID.
Is the performance hit you get from indexing hashes vs INTs negligible in such a case?
I personally also like the fact that approach B provides a way to verify if someone tampered with the token, and avoids the DB-roundtrip if so. Do you see an approach to do so using hashes, as in approach A?
**UPDATE**
So let's say you have the following table:
CREATE TABLE sessdata (
sid_hash BINARY(60) NOT NULL PRIMARY KEY,
sdata JSON NOT NULL
);
With this, the simple insertion and subsequent attempted retrieval via hash using PHP and PDO fails:
$pdo = new PDO(
dsn : "mysql:host=localhost;dbname=$db_name;charset=utf8mb4",
username: $db_user,
password: $db_p,
options : [
// Throw PDO Exception in case of any error
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// Fetch the obtained result as associative array by default
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
$raw_binary_sid = random_bytes(32);
$hash = password_hash(
password: $raw_binary_sid,
algo : PASSWORD_DEFAULT
);
// Keep this somewhere in your storage
$base64_urlsafe_nopadding = sodium_bin2base64(
string: $raw_binary_sid,
id : SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
);
$stmt = $pdo->prepare("INSERT INTO sessdata (sid_hash,sdata) VALUES (:hash,JSON_ARRAY())");
$stmt->execute(
[
'hash' => $hash
]
);
// Then in a separate PHP Session try to retrieve it, doesnt work
$hash = password_hash(
password: sodium_base642bin(
$base64_urlsafe_nopadding,
SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
),
algo : PASSWORD_DEFAULT
);
$stmt = $pdo->prepare("SELECT * FROM sessdata WHERE sid_hash = :hash");
$stmt->execute(
[
'hash' => $hash
]
);
$stmt->fetchAll(); // empty array = no records found
What am I missing here?
**UPDATE 2**
Okay got the problem. PDO by default binds parameter to a query using the PDO::PARAM_STR
data type / string data type. If I bind the parameter as PDO::PARAM_LOB
data type instead during the insert and select operations, it all works.
So in the end there are two final questions:
1. Is it correct that queries using the above-mentioned code will perform much faster than if I would index the hash column with the VARCHAR(60)
data type?
2. Are truncated inserts impossible; e.g. will the 60-character long strings generated in PHP via password_hash('password',PASSWORD_BCRYPT)
always match exactly the length specified via BINARY(60)
? Or is it wiser to use VARBINARY(60)
to avoid danger of eventually padded / truncated values?
Asked by DevelJoe
(163 rep)
Jun 4, 2023, 08:33 PM
Last activity: Jun 6, 2023, 10:52 PM
Last activity: Jun 6, 2023, 10:52 PM