Hash vs encryption — one-way transformation vs reversible transformation
Beginner5 min read·eng-19-005
interviewsecurity
Concept
Hash vs encryption — two fundamentally different operations with different purposes, outputs, and reversibility.
Hashing:
- One-way: Cannot be reversed. Hash → original input is computationally infeasible.
- Deterministic: Same input → same hash. Always.
- Fixed-length output: Input of any size → fixed-size hash (SHA-256 = 64 hex chars).
- Use case: Storing passwords, verifying file integrity, digital signatures.
- Not "crackable" — but pre-computed rainbow tables can reverse weak hashes. Solution: salt (see eng-19-006).
Encryption:
- Two-way (reversible): Encrypted data can be decrypted with the key.
- Requires a key: Without the key, ciphertext is useless.
- Variable-length output: Usually similar size to input (plus padding/IV).
- Use case: Storing data that must be retrieved (credit card numbers, API keys, PII).
- Symmetric: Same key for encrypt and decrypt (AES). Asymmetric: Public key encrypts, private key decrypts (RSA, used in HTTPS).
The critical rule: NEVER store passwords with encryption (even if you hold the key, a breach exposes all passwords). ALWAYS use hashing with a modern password-specific algorithm (bcrypt, argon2, scrypt).
Password hashing algorithms: bcrypt (slow by design — resists brute force), argon2id (PHP 7.2+, winner of Password Hashing Competition), scrypt. NOT SHA-256/MD5 — those are fast (bad for passwords, easy to brute-force).
Laravel: Hash::make($password) uses bcrypt by default. Hash::check($plaintext, $hash) to verify.
Code Example
php
<?php
// HASHING — one-way, for passwords and integrity
$password = 'secret123';
// Hash with bcrypt (default in Laravel)
$hash = Hash::make($password);
// Output: $2y$12$randomsalt...hashed (different each time due to salt)
$hash2 = Hash::make($password);
// $hash !== $hash2 — same input, different hash! (because random salt)
// Verify password
Hash::check('secret123', $hash); // true
Hash::check('wrongpass', $hash); // false
// Cannot reverse $hash to get 'secret123' — one-way
// Laravel uses argon2id when configured:
// config/hashing.php → 'driver' => 'argon2id'
password_hash($password, PASSWORD_ARGON2ID); // PHP native
// SHA-256 (fast, NOT for passwords)
hash('sha256', 'file contents'); // file integrity check
// Same as: openssl dgst -sha256 file.pdf
// These are FAST — bad for passwords (easy to brute-force millions/second)
// ENCRYPTION — two-way, for data you need to retrieve
$cardNumber = '4111111111111111';
// Laravel Crypt (AES-256-CBC with HMAC)
$encrypted = Crypt::encryptString($cardNumber);
// Output: eyJpdiI6...base64 (different each time due to random IV)
$decrypted = Crypt::decryptString($encrypted);
// $decrypted === '4111111111111111' — reversible!
// Eloquent encrypted cast (transparent encrypt/decrypt)
class User extends Model
{
protected $casts = [
'bank_account_number' => 'encrypted', // auto-encrypt on save, decrypt on access
'ssn' => 'encrypted',
];
}
$user->bank_account_number = '12345678'; // encrypted in DB
echo $user->bank_account_number; // '12345678' — decrypted transparently
// WRONG: encrypting passwords (reversible — if key leaks, all passwords exposed)
$storedWrong = Crypt::encryptString($password); // DON'T DO THIS
// RIGHT: hashing passwords
$storedRight = Hash::make($password); // DO THIS