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]
    end
Perbandingan 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.

← Sebelumnya: Brute Force   Berikutnya: Firewall →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact