使用数据库功能更快地执行用户身份验证的方法

Faster way to perform user authentication using database functions

有没有更快的方法来使用数据库函数来保护对用户进行身份验证? crypt() 函数似乎花了很多时间。

Table:

id (bigserial) pkey
username (text) indexed
password (text) indexed [password is the hash: crypt('12345678', gen_salt('bf', 8))]

普通测试:

test_db=# explain analyze SELECT id FROM users WHERE id = 1 AND password='12345678' LIMIT 1;
                                                                QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.28..8.29 rows=1 width=8) (actual time=0.029..0.029 rows=0 loops=1)
   ->  Index Scan using users_idx_password01 on users  (cost=0.28..8.29 rows=1 width=8) (actual time=0.028..0.028 rows=0 loops=1)
         Index Cond: (password = '12345678'::text)
         Filter: (id = 1)
 Total runtime: 0.037 ms
(5 rows)

Crypt() 测试:

test_db=# explain analyze SELECT id FROM users WHERE id = 1 AND password=(crypt('12345678',password)) LIMIT 1;
                                                           QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.28..8.30 rows=1 width=8) (actual time=7.534..7.534 rows=1 loops=1)
   ->  Index Scan using users_pkey on users  (cost=0.28..8.30 rows=1 width=8) (actual time=7.534..7.534 rows=1 loops=1)
         Index Cond: (id = 1)
         Filter: (password = crypt('12345678'::text, password))
 Total runtime: 7.550 ms
(5 rows)

我不完全确定 postgres 如何处理这样的查询,但在我看来,对每一行都执行了 crypt 函数。当您考虑使用适当的密码哈希函数时,故意慢,这样的查询将变得每一行都变慢。

处理此类查询的方法是,仅搜索用户,然后验证密码哈希:

SELECT password FROM users WHERE username = "carfield"

之后您在代码中测试找到的密码哈希。这也允许为每个密码处理唯一的盐,这是必须的。合适的散列函数是 BCrypt、PBKDF2 或 SCrypt,它们都有成本因素。通常编程环境已经提供了功能,这是 PHP:

中的示例
$isPasswordCorrect = password_verify($password, $existingHashFromDb);