Encryption #
Enkripsi adalah fondasi dari kepercayaan dalam sistem digital. Tanpa enkripsi, data yang dikirimkan melalui jaringan bisa dibaca oleh siapapun yang menyadap traffic. Data yang tersimpan di database yang bocor bisa langsung dibaca oleh attacker. Credential yang tersimpan tanpa enkripsi menjadi bom waktu menunggu insiden.
Memahami enkripsi dari perspektif developer web bukan berarti memahami matematika di baliknya — itu domain kriptografer. Yang perlu dipahami adalah: kapan enkripsi diperlukan, algoritma mana yang aman digunakan saat ini, cara menggunakan library yang tersedia dengan benar, dan bagaimana mengelola kunci enkripsi dengan aman. Penggunaan enkripsi yang salah — algoritma yang sudah tidak aman, kunci yang lemah, implementasi yang ada celahnya — bisa lebih berbahaya dari tidak mengenkripsi sama sekali karena memberikan false sense of security.
Dua Kategori Enkripsi: Symmetric dan Asymmetric #
graph LR
subgraph Symmetric["Symmetric Encryption"]
A1[Plaintext] -->|Encrypt dengan key| B1[Ciphertext]
B1 -->|Decrypt dengan key yang SAMA| C1[Plaintext]
D1[Satu kunci untuk encrypt dan decrypt]
end
subgraph Asymmetric["Asymmetric Encryption"]
A2[Plaintext] -->|Encrypt dengan Public Key| B2[Ciphertext]
B2 -->|Decrypt dengan Private Key| C2[Plaintext]
D2[Public key bisa dibagikan\nPrivate key tetap rahasia]
end
subgraph UseCases["Kapan Digunakan"]
E1[Symmetric: enkripsi data,\nenkripsi at rest, kecepatan tinggi]
E2[Asymmetric: TLS handshake,\ntanda tangan digital, key exchange]
endPerbandingan Symmetric vs Asymmetric:
┌─────────────────┬──────────────────────────┬──────────────────────────┐
│ Aspek │ Symmetric (AES) │ Asymmetric (RSA/ECC) │
├─────────────────┼──────────────────────────┼──────────────────────────┤
│ Kunci │ Satu kunci untuk semua │ Pasangan public/private │
│ Kecepatan │ Sangat cepat │ Lambat (1000x lebih lambat│
│ Key distribution│ Harus aman exchange dulu │ Public key bisa bebas bagikan│
│ Ukuran kunci │ 128/256 bit │ 2048/4096 bit (RSA) │
│ Use case │ Enkripsi bulk data │ Key exchange, digital sign│
│ Contoh │ AES-256-GCM │ RSA, ECDSA, X25519 │
└─────────────────┴──────────────────────────┴──────────────────────────┘
Dalam praktik, keduanya digunakan bersama:
→ Asymmetric untuk key exchange (TLS handshake)
→ Symmetric untuk enkripsi data aktual (lebih cepat)
→ Inilah cara TLS bekerja: hybrid approach
TLS/HTTPS: Enkripsi Data in Transit #
Enkripsi data yang sedang dikirimkan melalui jaringan adalah kebutuhan dasar yang tidak bisa ditawar. HTTP polos memungkinkan siapapun di jaringan yang sama untuk membaca dan memodifikasi traffic.
# Konfigurasi Nginx untuk TLS yang aman
server {
listen 443 ssl http2;
server_name app.contoh.com;
# Certificate dan private key
ssl_certificate /etc/ssl/certs/app.contoh.com.crt;
ssl_certificate_key /etc/ssl/private/app.contoh.com.key;
# Hanya TLS 1.2 dan 1.3 — TLS 1.0 dan 1.1 sudah deprecated
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher suite yang kuat — prioritaskan forward secrecy
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off; # TLS 1.3 handles this
# Session resumption — performa tanpa mengorbankan keamanan
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # matikan session tickets (forward secrecy)
# OCSP Stapling — validasi certificate lebih cepat
ssl_stapling on;
ssl_stapling_verify on;
# HSTS — paksa HTTPS untuk 1 tahun ke depan
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Redirect HTTP ke HTTPS
# (di server block port 80 yang terpisah)
}
server {
listen 80;
server_name app.contoh.com;
return 301 https://$host$request_uri;
}
Yang perlu dipastikan untuk TLS yang aman:
✓ TLS 1.2 minimum, TLS 1.3 direkomendasikan
✗ TLS 1.0 dan 1.1 sudah deprecated dan harus dinonaktifkan
✓ Forward Secrecy: gunakan cipher ECDHE atau DHE
Jika private key bocor di masa depan, traffic lama tidak bisa di-decrypt
✗ RSA key exchange tanpa forward secrecy — jika key bocor, semua traffic historis bisa di-decrypt
✓ Certificate dari CA yang terpercaya
✓ Certificate diperbarui sebelum expired (Let's Encrypt: 90 hari)
✓ HSTS untuk mencegah SSL stripping attack
✓ OCSP stapling untuk validasi certificate yang lebih cepat
Tools untuk verifikasi:
→ SSL Labs (ssllabs.com/ssltest): A+ rating adalah target
→ Mozilla SSL Configuration Generator untuk config yang direkomendasikan
Enkripsi Data at Rest: Melindungi Data yang Tersimpan #
Data yang tersimpan — di database, di disk, di backup — perlu dilindungi jika media penyimpanan dikompromikan.
Enkripsi Symmetric dengan AES-256-GCM #
AES-256-GCM adalah standar enkripsi symmetric yang direkomendasikan. GCM (Galois/Counter Mode) memberikan authenticated encryption — ia tidak hanya mengenkripsi data tapi juga memverifikasi integritasnya.
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import base64
def encrypt_data(plaintext: bytes, key: bytes) -> dict:
"""
Enkripsi data dengan AES-256-GCM.
Returns dict dengan nonce dan ciphertext yang bisa disimpan bersama.
"""
if len(key) != 32:
raise ValueError("Key harus 256 bit (32 byte)")
# Generate nonce baru untuk setiap enkripsi
# KRITIS: jangan pernah reuse nonce dengan key yang sama!
nonce = os.urandom(12) # 96-bit nonce — standar untuk GCM
aesgcm = AESGCM(key)
# Encrypt + authenticate
# GCM menghasilkan ciphertext + authentication tag (16 byte)
ciphertext = aesgcm.encrypt(nonce, plaintext, None)
return {
'nonce': base64.b64encode(nonce).decode(),
'ciphertext': base64.b64encode(ciphertext).decode(),
'algorithm': 'AES-256-GCM',
}
def decrypt_data(encrypted: dict, key: bytes) -> bytes:
"""
Decrypt data yang dienkripsi dengan encrypt_data().
Raise exception jika data telah dimodifikasi (integrity check).
"""
nonce = base64.b64decode(encrypted['nonce'])
ciphertext = base64.b64decode(encrypted['ciphertext'])
aesgcm = AESGCM(key)
try:
# GCM otomatis memverifikasi authentication tag
# Jika data dimodifikasi, InvalidTag exception dilempar
return aesgcm.decrypt(nonce, ciphertext, None)
except Exception:
raise ValueError("Dekripsi gagal: data mungkin telah dimodifikasi")
# Penggunaan:
key = os.urandom(32) # 256-bit key — simpan dengan aman!
plaintext = b"Data sensitif yang perlu dienkripsi"
encrypted = encrypt_data(plaintext, key)
# encrypted = {
# 'nonce': 'base64...',
# 'ciphertext': 'base64...',
# 'algorithm': 'AES-256-GCM'
# }
decrypted = decrypt_data(encrypted, key)
assert decrypted == plaintext
Prinsip penting untuk AES-GCM:
✓ Gunakan AES-256 (256-bit key) — bukan AES-128
✓ Gunakan GCM mode — memberikan enkripsi + authentication
✗ Jangan gunakan ECB mode — tidak aman, pola data terlihat di ciphertext
✗ Jangan gunakan CBC tanpa MAC — rentan terhadap padding oracle attack
Nonce (IV):
✓ Generate nonce baru secara random untuk SETIAP enkripsi
✗ Jangan reuse nonce dengan key yang sama — ini merusak keamanan GCM secara fatal
✓ Simpan nonce bersama ciphertext — nonce tidak rahasia, tapi harus unik
Authenticated Encryption:
✓ GCM memberikan authentication tag yang memverifikasi integritas
→ Data yang dimodifikasi akan gagal decrypt
→ Melindungi dari tampering dan bit flipping attacks
Envelope Encryption: Praktik Terbaik untuk Key Management #
Envelope encryption memisahkan data encryption key (DEK) dari key encryption key (KEK). DEK mengenkripsi data, KEK mengenkripsi DEK. Ini memungkinkan rotasi key tanpa harus re-enkripsi semua data.
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
class EnvelopeEncryption:
"""
Envelope encryption pattern:
KEK (Key Encryption Key) → mengenkripsi DEK
DEK (Data Encryption Key) → mengenkripsi data
KEK disimpan di secure location (HSM, KMS)
DEK disimpan bersama data yang dienkripsi (dalam bentuk terenkripsi)
"""
def __init__(self, kek: bytes):
"""kek: 256-bit Key Encryption Key dari KMS atau HSM."""
if len(kek) != 32:
raise ValueError("KEK harus 256-bit")
self.kek = kek
self._kek_cipher = AESGCM(kek)
def encrypt(self, plaintext: bytes) -> dict:
"""
Enkripsi data menggunakan envelope encryption.
Setiap call menghasilkan DEK baru.
"""
# 1. Generate Data Encryption Key (DEK) baru
dek = os.urandom(32) # 256-bit DEK
# 2. Enkripsi data dengan DEK
data_nonce = os.urandom(12)
data_cipher = AESGCM(dek)
encrypted_data = data_cipher.encrypt(data_nonce, plaintext, None)
# 3. Enkripsi DEK dengan KEK (wrapping)
key_nonce = os.urandom(12)
encrypted_dek = self._kek_cipher.encrypt(key_nonce, dek, None)
# DEK bisa di-hapus dari memory sekarang
dek = b'\x00' * 32 # zero out
return {
'encrypted_dek': base64.b64encode(encrypted_dek).decode(),
'key_nonce': base64.b64encode(key_nonce).decode(),
'encrypted_data': base64.b64encode(encrypted_data).decode(),
'data_nonce': base64.b64encode(data_nonce).decode(),
}
def decrypt(self, envelope: dict) -> bytes:
"""Decrypt data dari envelope."""
# 1. Unwrap DEK menggunakan KEK
encrypted_dek = base64.b64decode(envelope['encrypted_dek'])
key_nonce = base64.b64decode(envelope['key_nonce'])
dek = self._kek_cipher.decrypt(key_nonce, encrypted_dek, None)
# 2. Decrypt data menggunakan DEK
encrypted_data = base64.b64decode(envelope['encrypted_data'])
data_nonce = base64.b64decode(envelope['data_nonce'])
data_cipher = AESGCM(dek)
return data_cipher.decrypt(data_nonce, encrypted_data, None)
def rotate_kek(self, new_kek: bytes, envelope: dict) -> dict:
"""
Rotasi KEK: decrypt dengan KEK lama, re-encrypt DEK dengan KEK baru.
Data tidak perlu di-decrypt dan re-encrypt!
"""
# Decrypt DEK dengan KEK lama
old_encrypted_dek = base64.b64decode(envelope['encrypted_dek'])
old_key_nonce = base64.b64decode(envelope['key_nonce'])
dek = self._kek_cipher.decrypt(old_key_nonce, old_encrypted_dek, None)
# Re-encrypt DEK dengan KEK baru
new_kek_cipher = AESGCM(new_kek)
new_key_nonce = os.urandom(12)
new_encrypted_dek = new_kek_cipher.encrypt(new_key_nonce, dek, None)
return {
**envelope, # encrypted_data dan data_nonce tidak berubah
'encrypted_dek': base64.b64encode(new_encrypted_dek).decode(),
'key_nonce': base64.b64encode(new_key_nonce).decode(),
}
Enkripsi Field Sensitif di Database #
Tidak semua data perlu dienkripsi di database — enkripsi menambah kompleksitas dan mengurangi kemampuan query. Fokus pada field yang benar-benar sensitif.
from sqlalchemy import Column, String, TypeDecorator
import json
class EncryptedString(TypeDecorator):
"""
SQLAlchemy type untuk field yang dienkripsi secara transparan.
Nilai dienkripsi sebelum disimpan, didekripsi saat dibaca.
"""
impl = String
cache_ok = True
def __init__(self, encryption_key: bytes, *args, **kwargs):
self.key = encryption_key
super().__init__(*args, **kwargs)
def process_bind_param(self, value, dialect):
"""Enkripsi sebelum simpan ke database."""
if value is None:
return None
encrypted = encrypt_data(value.encode(), self.key)
return json.dumps(encrypted)
def process_result_value(self, value, dialect):
"""Decrypt saat baca dari database."""
if value is None:
return None
encrypted = json.loads(value)
return decrypt_data(encrypted, self.key).decode()
# Konfigurasi
FIELD_ENCRYPTION_KEY = bytes.fromhex(os.environ['FIELD_ENCRYPTION_KEY'])
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True, nullable=False)
# Field sensitif yang dienkripsi
phone_number = db.Column(
EncryptedString(FIELD_ENCRYPTION_KEY, 500),
nullable=True
)
tax_id = db.Column(
EncryptedString(FIELD_ENCRYPTION_KEY, 500),
nullable=True
)
date_of_birth = db.Column(
EncryptedString(FIELD_ENCRYPTION_KEY, 500),
nullable=True
)
# TIDAK dienkripsi — dibutuhkan untuk search/filter
# email di-hash untuk search (lihat bawah)
# name mungkin tidak perlu enkripsi
# Untuk field yang perlu di-search, simpan hash terpisah
phone_hash = db.Column(db.String(64), index=True, nullable=True)
class UserRepository:
def create_user(self, email, phone=None, tax_id=None, dob=None):
user = User(
email=email,
phone_number=phone, # dienkripsi otomatis oleh EncryptedString
tax_id=tax_id,
date_of_birth=dob,
)
if phone:
# Simpan hash untuk keperluan search
user.phone_hash = hashlib.sha256(
phone.encode()
).hexdigest()
db.session.add(user)
db.session.commit()
return user
def find_by_phone(self, phone: str):
"""Cari user berdasarkan nomor HP."""
phone_hash = hashlib.sha256(phone.encode()).hexdigest()
return User.query.filter_by(phone_hash=phone_hash).first()
Field mana yang perlu dienkripsi di database:
✓ Wajib dienkripsi:
→ Nomor kartu kredit / payment instrument
→ Nomor KTP, paspor, atau identitas lainnya
→ Data medis atau kesehatan
→ Nomor rekening bank
→ PIN atau secret yang bisa dipakai untuk transaksi
✓ Sangat disarankan:
→ Nomor telepon
→ Tanggal lahir
→ Alamat lengkap
→ Data biometrik
✓ Pertimbangkan berdasarkan regulasi:
→ Email (GDPR, beberapa jurisdiksi)
→ IP address (bisa dianggap PII)
✗ Biasanya tidak perlu:
→ Username (biasanya public)
→ Preferensi aplikasi
→ Timestamp dan metadata
→ Data yang memang public
Hashing vs Enkripsi: Pilihan yang Tepat #
Hashing dan enkripsi adalah dua teknik yang berbeda dengan tujuan yang berbeda. Memilih yang salah bisa merusak keamanan.
Enkripsi: reversible dengan key
Data → [Encrypt dengan key] → Ciphertext → [Decrypt dengan key] → Data
Kapan digunakan: butuh mendapatkan data asli kembali
Contoh: nomor kartu kredit, PII yang perlu diproses
Hashing: one-way, tidak bisa di-reverse
Data → [Hash] → Hash value (tidak bisa kembali ke Data)
Kapan digunakan: verifikasi tanpa menyimpan data asli
Contoh: password, token verifikasi
Penggunaan yang salah:
✗ Enkripsi password (bisa di-decrypt jika key bocor)
✗ Hash nomor kartu kredit untuk disimpan (tidak bisa diproses)
✓ Hash password dengan bcrypt/Argon2
✓ Enkripsi nomor kartu kredit dengan AES-256-GCM
# Password hashing — BUKAN enkripsi
from argon2 import PasswordHasher
ph = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=2)
def store_password(password: str) -> str:
"""Hash password — tidak bisa di-reverse ke password asli."""
return ph.hash(password)
def verify_password(stored_hash: str, password: str) -> bool:
try:
ph.verify(stored_hash, password)
return True
except:
return False
# Enkripsi data sensitif — BUKAN hashing
def store_credit_card(card_number: str, key: bytes) -> dict:
"""Enkripsi — bisa di-decrypt untuk memproses pembayaran."""
return encrypt_data(card_number.encode(), key)
Key Management: Tantangan Terbesar #
Enkripsi yang benar bisa gagal total karena key management yang buruk. Kunci enkripsi adalah “kunci” dari seluruh sistem keamanan.
# ANTI-PATTERN: hardcode kunci di source code
SECRET_KEY = "mysecretkey123" # JANGAN — source code bisa bocor di git
# ANTI-PATTERN: kunci yang terlalu pendek atau lemah
key = b"password" # 64-bit — mudah di-brute force
# ANTI-PATTERN: kunci yang sama untuk semua tujuan
MASTER_KEY = os.urandom(32) # satu kunci untuk semua → satu bocor, semua bocor
# BENAR: kunci dari environment variable atau secret manager
import os
def get_encryption_key() -> bytes:
"""Ambil kunci dari environment atau secret manager."""
key_hex = os.environ.get('ENCRYPTION_KEY')
if not key_hex:
raise EnvironmentError("ENCRYPTION_KEY tidak dikonfigurasi")
key = bytes.fromhex(key_hex)
if len(key) != 32:
raise ValueError("ENCRYPTION_KEY harus 256-bit (64 hex chars)")
return key
# Kunci yang berbeda untuk tujuan yang berbeda
def setup_keys():
return {
'user_pii_key': bytes.fromhex(os.environ['USER_PII_ENCRYPTION_KEY']),
'payment_key': bytes.fromhex(os.environ['PAYMENT_ENCRYPTION_KEY']),
'session_key': bytes.fromhex(os.environ['SESSION_ENCRYPTION_KEY']),
}
Key Management Best Practices:
Penyimpanan kunci:
✓ Environment variables (tidak di source code)
✓ Secret manager: AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager
✓ HSM (Hardware Security Module) untuk kunci yang sangat kritikal
✗ Hardcode di source code
✗ Disimpan di database yang sama dengan data terenkripsi
✗ Commit ke git repository
Pemisahan kunci:
✓ Kunci berbeda untuk tujuan berbeda (PII, payment, session)
✓ Kunci berbeda untuk environment berbeda (dev, staging, prod)
✓ Envelope encryption: DEK untuk data, KEK untuk DEK
Rotasi kunci:
✓ Rotasi berkala (misal setiap 90 hari untuk KEK)
✓ Rotasi segera jika ada indikasi kompromi
✓ Envelope encryption memudahkan rotasi (re-encrypt DEK saja)
✗ Kunci yang tidak pernah dirotasi adalah risiko yang terus bertumbuh
Akses:
✓ Prinsip least privilege — hanya proses yang perlu bisa akses kunci
✓ Audit log untuk semua akses ke kunci
✓ Rotasi akses credentials secara berkala
Enkripsi dalam Konteks Berbeda #
API Communication #
# Ketika mengkomunikasikan data sensitif via API:
# 1. TLS sudah mengenkripsi traffic — tidak perlu enkripsi payload lagi
# untuk kerahasiaan (double encryption tidak menambah keamanan)
# 2. Kecuali ada requirement end-to-end encryption spesifik
# Untuk tanda tangan digital (membuktikan keaslian, bukan kerahasiaan):
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
def generate_signing_keypair():
"""Generate Ed25519 keypair untuk digital signature."""
private_key = ed25519.Ed25519PrivateKey.generate()
public_key = private_key.public_key()
return private_key, public_key
def sign_payload(payload: bytes, private_key) -> bytes:
"""Tandatangani payload untuk membuktikan keaslian."""
return private_key.sign(payload)
def verify_signature(payload: bytes, signature: bytes, public_key) -> bool:
"""Verifikasi tanda tangan."""
try:
public_key.verify(signature, payload)
return True
except Exception:
return False
Backup Encryption #
# Enkripsi backup sebelum upload ke cloud storage
# Gunakan GPG dengan symmetric encryption
# Enkripsi backup:
gpg --symmetric \
--cipher-algo AES256 \
--batch \
--passphrase-file /secure/location/backup.passphrase \
database_backup.sql.gz
# Upload ke S3 (sudah terenkripsi):
aws s3 cp database_backup.sql.gz.gpg s3://backup-bucket/
# Verifikasi: bahkan jika S3 bucket dikompromikan,
# file masih terenkripsi dengan AES-256
Algoritma yang Sudah Tidak Aman #
Penting untuk mengetahui mana yang harus dihindari:
JANGAN gunakan untuk enkripsi baru:
DES / 3DES:
→ DES sudah di-crack sejak 1998
→ 3DES masih digunakan di legacy system tapi deprecated
→ Ganti dengan AES-256
RC4:
→ Ditemukan banyak kelemahan kriptografis
→ Sudah dilarang di TLS
MD5 dan SHA-1 untuk keamanan:
→ Collision sudah ditemukan (dua input berbeda menghasilkan hash sama)
→ Tidak aman untuk digital signature atau certificate
→ Masih OK untuk checksum non-security (seperti memverifikasi download)
RSA < 2048 bit:
→ 1024-bit RSA sudah dianggap tidak aman
→ Gunakan minimal 2048-bit, idealnya 4096-bit
ECB mode:
→ Pola dalam plaintext terlihat di ciphertext
→ Gunakan GCM atau CBC dengan HMAC
Yang direkomendasikan saat ini (2025):
Symmetric encryption: AES-256-GCM
Asymmetric encryption: RSA-4096, ECDSA (P-256 atau P-384), Ed25519
Key exchange: X25519 (ECDH dengan Curve25519)
Hashing: SHA-256, SHA-384, SHA-512 (bukan MD5/SHA-1)
Password hashing: Argon2id, bcrypt, scrypt
Digital signature: Ed25519, ECDSA P-256
TLS: 1.2 minimum, 1.3 recommended
Anti-Pattern yang Harus Dihindari #
# ✗ Anti-pattern 1: enkripsi password
encrypted_password = encrypt(password, key)
# Jika key bocor, semua password terdekripsi
# ✓ Solusi: hash dengan Argon2id, bcrypt, atau scrypt
# ✗ Anti-pattern 2: reuse nonce/IV
nonce = b'\x00' * 12 # static nonce — FATAL untuk GCM!
# Reuse nonce dengan key yang sama menghancurkan keamanan GCM
# ✓ Solusi: os.urandom(12) untuk setiap enkripsi
# ✗ Anti-pattern 3: kunci dari password yang lemah tanpa KDF
key = password.encode()[:32] # bukan key yang baik
# Password pendek → key lemah
# ✓ Solusi: gunakan PBKDF2/Argon2 untuk derive key dari password
# ✗ Anti-pattern 4: menyimpan kunci di database
# Kunci dan data terenkripsi di tempat yang sama
# Jika database bocor, keduanya ikut bocor
# ✓ Solusi: kunci di secret manager atau environment variable
# ✗ Anti-pattern 5: CBC mode tanpa authentication
# AES-CBC tanpa HMAC rentan padding oracle attack
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
# ✓ Solusi: gunakan AES-GCM yang memberikan authenticated encryption
# ✗ Anti-pattern 6: mengimplementasi sendiri algoritma kriptografi
def my_encrypt(data, key):
# Implementasi custom XOR atau sejenisnya
return bytes(a ^ b for a, b in zip(data, key * (len(data) // len(key) + 1)))
# Sangat tidak aman — gunakan library yang sudah teruji
# ✓ Solusi: cryptography (Python), BouncyCastle (Java), WebCrypto API (JS)
Checklist Encryption #
DATA IN TRANSIT:
□ Semua endpoint menggunakan HTTPS
□ TLS 1.2 minimum, TLS 1.3 direkomendasikan
□ TLS 1.0 dan 1.1 dinonaktifkan
□ Forward secrecy (ECDHE atau DHE cipher) diaktifkan
□ HSTS header dipasang dengan durasi yang cukup
□ Certificate diperbarui sebelum expired (monitoring otomatis)
□ SSL Labs rating A atau A+
DATA AT REST:
□ Field sensitif di database dienkripsi (PII, payment data, dll)
□ Backup dienkripsi sebelum disimpan di storage eksternal
□ Disk encryption aktif di server produksi
□ Algoritma yang digunakan: AES-256-GCM
KEY MANAGEMENT:
□ Kunci tidak ada di source code atau git repository
□ Kunci disimpan di environment variable atau secret manager
□ Kunci berbeda untuk environment berbeda (dev/staging/prod)
□ Kunci berbeda untuk tujuan berbeda (PII, payment, session)
□ Rotasi kunci dijadwalkan secara berkala
□ Audit log untuk akses ke kunci sensitif
ALGORITMA:
□ Tidak ada DES, 3DES, RC4, atau MD5 di code baru
□ Tidak ada ECB mode
□ Nonce/IV di-generate random untuk setiap operasi enkripsi
□ Tidak ada implementasi kriptografi custom
HASHING:
□ Password di-hash dengan Argon2id, bcrypt, atau scrypt
□ Data yang perlu diverifikasi (token) di-hash dengan SHA-256
□ Tidak ada MD5 atau SHA-1 untuk tujuan keamanan
COMPLIANCE:
□ Enkripsi memenuhi requirement regulasi yang berlaku (PCI-DSS, GDPR, dll)
□ Data retention dan deletion policy terdefinisi
Ringkasan #
- TLS adalah minimum — semua traffic harus HTTPS — HTTP polos memungkinkan siapapun di jaringan membaca dan memodifikasi data. TLS 1.3 dengan forward secrecy adalah standar yang harus dicapai.
- Gunakan AES-256-GCM untuk enkripsi symmetric — GCM memberikan authenticated encryption, melindungi dari tampering sekaligus memberikan kerahasiaan. Jangan gunakan ECB mode.
- Nonce harus unik untuk setiap enkripsi — reuse nonce dengan key yang sama secara fatal menghancurkan keamanan GCM. Selalu
os.urandom(12)untuk setiap operasi enkripsi.- Enkripsi dan hashing adalah dua hal yang berbeda — password di-hash (one-way), data sensitif yang perlu diproses di-enkripsi (reversible). Menggunakan yang salah membuka celah keamanan.
- Envelope encryption memudahkan key rotation — DEK mengenkripsi data, KEK mengenkripsi DEK. Rotasi KEK hanya perlu re-enkripsi DEK, bukan seluruh data.
- Kunci enkripsi tidak boleh ada di source code — gunakan environment variable atau secret manager. Kunci yang hardcode di git adalah kunci yang sudah bocor.
- Kunci yang berbeda untuk tujuan yang berbeda — satu kunci untuk PII, satu untuk payment, satu untuk session. Kompromi satu kunci tidak mengekspos semua data.
- Jangan implementasi kriptografi sendiri — gunakan library yang sudah teruji (cryptography, BouncyCastle, libsodium). Kriptografi custom hampir selalu mengandung kelemahan.
- Algoritma yang sudah tidak aman harus dihapus — DES, 3DES, RC4, MD5 untuk keamanan, SHA-1 untuk digital signature sudah tidak aman. Migrate ke AES-256-GCM dan SHA-256/SHA-3.
- Field sensitif di database perlu enkripsi — nomor kartu, identitas, data medis harus dienkripsi bahkan di dalam database. Enkripsi disk tidak cukup jika attacker mendapat akses ke database.