Di era serangan cyber yang makin canggih, nyimpen password dalam bentuk plain text atau pake algoritma hashing jadul kaya MD5/SHA1 itu udah kaya bunuh diri. bcrypt adalah solusi modern yang jadi standar industri buat password hashing. Artikel ini bakal ngebahas semua tentang bcrypt, dari cara kerja sampe implementasi di berbagai skenario.
Apa Itu bcrypt dan Kenapa Harus Pake Itu?
bcrypt adalah adaptive hashing function yang dirancang khusus buat password security. Dibuat tahun 1999 sama Niels Provos dan David Mazières, bcrypt udah bertahan lebih dari 20 tahun tanpa ada exploit praktis yang signifikan.
Keunggulan bcrypt dibanding Algoritma Lain
| Fitur | bcrypt | MD5/SHA1 | SHA256 |
|---|---|---|---|
| Salt otomatis | ✅ Built-in | ❌ Manual | ❌ Manual |
| Adaptive cost | ✅ Bisa di-tune | ❌ Fixed | ❌ Fixed |
| Resistant rainbow table | ✅ Tinggi | ❌ Rendah | ⚠️ Sedang |
| Speed | ⚠️ Lambat (by design) | ✅ Cepat | ✅ Cepat |
| Memory hard | ❌ Tidak | ❌ Tidak | ❌ Tidak |
Catatan Penting: bcrypt sengaja dibuat lambat. Kalo hashing cepat, attacker juga bisa brute-force cepat. bcrypt bikin proses hashing jadi “mahal” secara komputasi.
Cara Kerja bcrypt
- Salt generation: Generate salt random 16-byte (128-bit)
- Cost factor: Tentukan jumlah iterasi (2^cost, default 10 = 1.024 iterasi)
- Key derivation: Jalani Eksblowfish cipher berdasarkan password + salt
- Output: Hash string format
$2y$cost$salt+hash(60 karakter)
1. Instalasi bcrypt di Linux
Sebelum mulai, pastikan tools dan library bcrypt udah ready di sistem.
Install via Package Manager
Ubuntu/Debian:
# Install library C untuk bcrypt
sudo apt update
sudo apt install bcrypt -y
# Install Python bcrypt (paling sering dipake)
pip3 install bcrypt
# Install untuk Node.js
npm install bcrypt
# Install untuk PHP
sudo apt install php-bcrypt
CentOS/RHEL/Fedora:
# Install dependencies
sudo dnf install gcc python3-devel -y
# Install via pip
pip3 install bcrypt
# Atau pake yum kalo CentOS 7
sudo yum install python3-bcrypt
Arch Linux:
sudo pacman -S python-bcrypt
# atau
yay -S bcrypt
Verifikasi Instalasi
# Check Python bcrypt
python3 -c "import bcrypt; print(bcrypt.__version__)"
# Check command line tool
which bcrypt
bcrypt --help
2. Penggunaan bcrypt via Command Line
Kalo butuh hash password cepet tanpa bikin script, bisa pake tools command line.
Menggunakan htpasswd (Apache Utils)
# Install dulu
sudo apt install apache2-utils -y
# Generate hash bcrypt (default cost 5, bisa diatur)
htpasswd -bnBC 10 "" "password123" | tr -d ':\n'
# Output contoh: $2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
Menggunakan OpenSSL (Tidak native bcrypt, tapi alternative)
# OpenSSL gak support bcrypt native, tapi bisa pake script Python inline
python3 -c "
import bcrypt
import getpass
pwd = getpass.getpass('Enter password: ')
hash = bcrypt.hashpw(pwd.encode(), bcrypt.gensalt(rounds=12))
print(hash.decode())
"
Tools Online (Hanya untuk testing!)
# JANGAN pake tools online buat password production!
# Cuma buat testing/development aja
3. Implementasi bcrypt di Berbagai Bahasa
Ini yang paling sering dipake developer buat integrate bcrypt ke aplikasi.
Python (Paling Populer)
import bcrypt
# Generate hash baru
password = b"super_secret_password"
salt = bcrypt.gensalt(rounds=12) # Cost factor 12 (2^12 = 4096 iterasi)
hashed = bcrypt.hashpw(password, salt)
print(f"Hash: {hashed.decode()}")
# Output: $2b$12$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Verifikasi password
if bcrypt.checkpw(password, hashed):
print("Password match!")
else:
print("Password salah!")
Best Practice Python:
import bcrypt
def hash_password(plain_password: str) -> str:
"""Hash password dengan cost tinggi"""
# Encode string ke bytes
password_bytes = plain_password.encode('utf-8')
# Generate salt dengan cost 12 (seimbang antara security & speed)
salt = bcrypt.gensalt(rounds=12)
# Hash password
hashed = bcrypt.hashpw(password_bytes, salt)
return hashed.decode('utf-8')
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verifikasi password"""
password_bytes = plain_password.encode('utf-8')
hashed_bytes = hashed_password.encode('utf-8')
return bcrypt.checkpw(password_bytes, hashed_bytes)
# Usage
hash_stored = hash_password("user_password123")
is_valid = verify_password("user_password123", hash_stored)
Node.js
const bcrypt = require('bcrypt');
const saltRounds = 12;
// Async/await (Recommended)
async function hashPassword(password) {
try {
const salt = await bcrypt.genSalt(saltRounds);
const hash = await bcrypt.hash(password, salt);
return hash;
} catch (err) {
console.error('Error hashing:', err);
}
}
async function verifyPassword(password, hash) {
try {
const match = await bcrypt.compare(password, hash);
return match;
} catch (err) {
console.error('Error verifying:', err);
}
}
// Usage
hashPassword('myPassword123').then(hash => {
console.log('Hash:', hash);
verifyPassword('myPassword123', hash).then(result => {
console.log('Match:', result);
});
});
PHP
<?php
// PHP 5.5+ udah built-in support bcrypt via password_hash()
// Hashing
$password = 'user_password123';
$options = [
'cost' => 12, // Default 10
];
$hash = password_hash($password, PASSWORD_BCRYPT, $options);
echo "Hash: " . $hash . "\n";
// Output: $2y$12$...
// Verifikasi
if (password_verify($password, $hash)) {
echo "Password valid!\n";
} else {
echo "Password salah!\n";
}
// Check cost (untuk rehash kalo perlu)
$info = password_get_info($hash);
print_r($info);
// Rehash kalo cost berubah
if (password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 14])) {
$newHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 14]);
// Update database dengan $newHash
}
?>
Bash Script (Menggunakan Python Helper)
#!/bin/bash
# Fungsi hash password
hash_password() {
python3 -c "
import bcrypt
import sys
pwd = sys.argv[1].encode()
salt = bcrypt.gensalt(rounds=12)
print(bcrypt.hashpw(pwd, salt).decode())
" "$1"
}
# Fungsi verify password
verify_password() {
python3 -c "
import bcrypt
import sys
pwd = sys.argv[1].encode()
hash_str = sys.argv[2].encode()
result = bcrypt.checkpw(pwd, hash_str)
print('VALID' if result else 'INVALID')
" "$1" "$2"
}
# Usage
echo "Hashing password..."
HASH=$(hash_password "secret123")
echo "Hash: $HASH"
echo "Verifying..."
RESULT=$(verify_password "secret123" "$HASH")
echo "Result: $RESULT"
4. Konfigurasi Cost Factor (Work Factor)
Cost factor adalah jumlah iterasi (2^cost) yang menentukan seberapa “lambat” hashing berjalan.
Memilih Cost Factor yang Tepat
| Cost | Iterasi | Waktu (Modern CPU) | Use Case |
|---|---|---|---|
| 10 | 1,024 | ~50-100ms | Development, testing |
| 12 | 4,096 | ~200-300ms | Production standar |
| 14 | 16,384 | ~800ms-1s | High security |
| 16 | 65,536 | ~3-4 detik | Extreme security |
Benchmark Cost Factor
import bcrypt
import time
password = b"test_password"
for cost in [10, 11, 12, 13, 14]:
start = time.time()
salt = bcrypt.gensalt(rounds=cost)
hashed = bcrypt.hashpw(password, salt)
duration = time.time() - start
print(f"Cost {cost}: {duration:.3f}s")
Rekomendasi:
- Web login: Cost 10-12 (balance UX & security)
- API/Backend: Cost 12-14 (bisa tolerate latency)
- High security: Cost 14+ (admin panels, financial systems)
5. Database Integration
Cara nyimpen dan manage bcrypt hash di database.
Skema Database (PostgreSQL/MySQL)
-- Users table dengan field password hash
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL, -- bcrypt hash 60 chars
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Index untuk performance
CREATE INDEX idx_users_email ON users(email);
Python CRUD Example
import bcrypt
import psycopg2
from contextlib import contextmanager
class UserRepository:
def __init__(self, db_config):
self.db_config = db_config
@contextmanager
def get_cursor(self):
conn = psycopg2.connect(**self.db_config)
cursor = conn.cursor()
try:
yield cursor
conn.commit()
except Exception as e:
conn.rollback()
raise e
finally:
cursor.close()
conn.close()
def create_user(self, username, email, plain_password):
"""Create user dengan hashed password"""
hashed = bcrypt.hashpw(
plain_password.encode('utf-8'),
bcrypt.gensalt(rounds=12)
).decode('utf-8')
with self.get_cursor() as cur:
cur.execute("""
INSERT INTO users (username, email, password_hash)
VALUES (%s, %s, %s)
RETURNING id
""", (username, email, hashed))
return cur.fetchone()[0]
def authenticate_user(self, email, plain_password):
"""Verify login credentials"""
with self.get_cursor() as cur:
cur.execute("""
SELECT id, username, password_hash
FROM users
WHERE email = %s
""", (email,))
result = cur.fetchone()
if not result:
return None
user_id, username, stored_hash = result
if bcrypt.checkpw(
plain_password.encode('utf-8'),
stored_hash.encode('utf-8')
):
return {'id': user_id, 'username': username}
return None
6. Migrasi dari Algoritma Lama ke bcrypt
Kalo sistem kalian masih pake MD5/SHA1, wajib migrasi ke bcrypt.
Strategi Gradual Migration
import bcrypt
import hashlib
class PasswordMigrator:
def __init__(self):
self.legacy_salt = "old_system_salt" # Salt lama kalo ada
def migrate_user(self, user_id, email, old_hash, input_password):
"""
Cek apakah password match dengan hash lama,
kalo iya, rehash ke bcrypt dan update DB.
"""
# Verify dengan metode lama (contoh: MD5)
legacy_verify = hashlib.md5(
(input_password + self.legacy_salt).encode()
).hexdigest()
if legacy_verify == old_hash:
# Match! Rehash ke bcrypt
new_hash = bcrypt.hashpw(
input_password.encode(),
bcrypt.gensalt(rounds=12)
).decode()
# Update database
self.update_password_hash(user_id, new_hash)
return True
return False
def update_password_hash(self, user_id, new_hash):
# Query update ke database
pass
Dual-Hash Strategy
def authenticate(email, password):
user = get_user_by_email(email)
if not user:
return False
stored_hash = user.password_hash
# Cek prefix bcrypt
if stored_hash.startswith('$2'):
# Sudah bcrypt
return bcrypt.checkpw(password.encode(), stored_hash.encode())
else:
# Masih hash lama, coba verify dan migrate
if verify_legacy_hash(password, stored_hash):
# Rehash ke bcrypt
new_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
update_user_password(user.id, new_hash)
return True
return False
7. Security Best Practices
Do’s ✅
# 1. Selalu gunakan salt yang unique per password
# bcrypt otomatis handle ini
# 2. Gunakan cost factor minimal 10-12
bcrypt.gensalt(rounds=12)
# 3. Enforce password complexity di aplikasi layer
# Bukan di hashing layer!
# 4. Implement rate limiting buat prevent brute force
# Contoh: max 5 attempt per menit
# 5. Gunakan HTTPS/TLS untuk transmisi password
# Jangan pernah kirim password via HTTP plain!
Don’ts ❌
# 1. JANGAN pernah hash password lebih dari sekali (double hashing)
# SALAH: bcrypt.hashpw(bcrypt.hashpw(pwd, salt1), salt2)
# BENAR: bcrypt.hashpw(pwd, salt) sekali aja
# 2. JANGAN pake salt statis
# SALAH: bcrypt.hashpw(pwd, b"static_salt")
# BENAR: bcrypt.gensalt() generate random tiap kali
# 3. JANGAN truncate password sebelum hash
# bcrypt handle password panjang apa aja
# 4. JANGAN compare hash dengan string comparison biasa
# SALAH: if hash1 == hash2:
# BENAR: bcrypt.checkpw(pwd, hash)
8. Troubleshooting Common Issues
Error: “Invalid salt”
# Masalah: Salt bukan format bcrypt
# Solusi: Pastikan salt dari bcrypt.gensalt()
salt = bcrypt.gensalt() # ✅ Benar
salt = b"random_string" # ❌ Salah
Error: “ValueError: Invalid rounds”
# Cost factor harus antara 4-31
bcrypt.gensalt(rounds=35) # ❌ Error
bcrypt.gensalt(rounds=12) # ✅ Benar
Performance Issue
# Kalo hashing terlalu lambat di production
import bcrypt
# Benchmark dulu
import time
start = time.time()
bcrypt.hashpw(b"test", bcrypt.gensalt(rounds=12))
print(f"Time: {time.time() - start}")
# Kalo >500ms, turunin cost factor
# Tapi jangan di bawah 10 untuk production!
Kesimpulan
bcrypt adalah pilihan terbaik buat password hashing di aplikasi modern. Dengan fitur salt otomatis dan adaptive cost factor, bcrypt tetap relevan meski hardware makin cepat.
Checklist Implementasi:
- ✅ Install library bcrypt untuk bahasa pemrograman kalian
- ✅ Gunakan cost factor 10-12 untuk production
- ✅ Selalu verify pake
bcrypt.checkpw(), bukan compare string - ✅ Implement rate limiting di aplikasi layer
- ✅ Migrasi dari MD5/SHA1 secepatnya kalo masih pake
Ingat: Security itu layered. bcrypt cuma satu lapisan, jangan lupa HTTPS, rate limiting, input validation, dan security practices lainnya.
Stay secure