0

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