-
Notifications
You must be signed in to change notification settings - Fork 0
Security
James Brucker edited this page Jun 16, 2025
·
5 revisions
Passwords should be stored as salted hashes. Good hashing algorithms, from strongest to weakest, are:
-
Argon2 (especially Argon2id) — Strongest
- Winner of the Password Hashing Competition; memory-hard, highly configurable, and resistant to GPU/ASIC attacks.
-
scrypt
- Also memory-hard and resistant to hardware attacks, but less flexible and slightly older than Argon2.
-
bcrypt
- Very well-tested and widely used, but less resistant to modern hardware attacks than Argon2 and scrypt.
-
PBKDF2 — Weakest
- Not memory-hard; more vulnerable to brute-force attacks with specialized hardware, but still secure with high iteration counts. But why choose it? A "high iteration count" today may not be good enough in a few years.
Applicability to an application using FastAPI + Postgres on backend, Vue.js on frontend.
- First choice should be Argon2, specifically, Argon2id.
- Supported by the
argon2-cffilibrary in Python. - In FastAPI use the
argon2-cffilibrary for hashing and verifying passwords. - Store the hash (and salt, if not embedded) in the PostgreSQL database, as
BYTEAfor raw binary orTEXTfor encoded strings.
- Second choice should be scrypt, which is provided by Python's
hashlibmodule.
Example:import os.urandom() import hashlib my_salt = os.urandom(16) # 16 binary bytes hash = hashlib.scrypt( password=b'my_password', # "bytes" object, at most 1024 bytes salt=my_salt, # "bytes" object, recommended 16+ random bytes n=16384, # cpu/memory cost factor r=8, # blocksize p=1 # parallelization factor ) # hash is a "bytes" object. To get a hex representation: hex_encoded = hash.hex() # Get in base64 form, which is more compact than hex (88 bytes vs 128 bytes): import base64 b64_encoded = base64.standard_b64encode(hash) # use base64.standard_b64decode() to get back the original hash "bytes"
The values of n, r, and p are not necessarily secure. This 2017 post has a clear, short explanation of the purpose of each parameter.
| Param | Typical value | Meaning |
|---|---|---|
n |
16384 | Iteration count, affects time and memory. Must be power of 2. |
r |
8 | Block size, linearly increases both memory and cpu use. |
p |
1 | Parallelization. Number of core to use. |
The memory required to compute the scrypt hash is:
Memory required = 128 * n * r * p Bytes
# Example = 128 * 16384 * 8 * 1 = 16 MBOn a laptop computer with 1.6 GHz Intel Core i5 and 8GB of memory running Ubuntu 20.04, the Python scrypt using
the above parameters required 0.06 seconds for a 16-char password.
When I doubled r to r=16, Python 3.11 threw an out of memory error.