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.

// Enkripsi dan dekripsi AES-256-GCM
import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
)

func encryptData(plaintext []byte, key []byte) (map[string]string, error) {
	if len(key) != 32 {
		return nil, errors.New("key harus 256 bit (32 byte)")
	}

	// Generate nonce baru untuk setiap enkripsi
	// KRITIS: jangan pernah reuse nonce dengan key yang sama!
	nonce := make([]byte, 12) // 96-bit nonce — standar GCM
	if _, err := rand.Read(nonce); err != nil {
		return nil, err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}

	// Enkripsi + autentikasi
	// GCM menghasilkan ciphertext + authentication tag (16 byte)
	ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)

	return map[string]string{
		"nonce":      base64.StdEncoding.EncodeToString(nonce),
		"ciphertext": base64.StdEncoding.EncodeToString(ciphertext),
		"algorithm":  "AES-256-GCM",
	}, nil
}

func decryptData(encrypted map[string]string, key []byte) ([]byte, error) {
	nonce, err := base64.StdEncoding.DecodeString(encrypted["nonce"])
	if err != nil {
		return nil, err
	}
	ciphertext, err := base64.StdEncoding.DecodeString(encrypted["ciphertext"])
	if err != nil {
		return nil, err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}

	// GCM otomatis memverifikasi authentication tag
	// Jika data dimodifikasi, error dikembalikan
	plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return nil, errors.New("dekripsi gagal: data mungkin telah dimodifikasi")
	}
	return plaintext, nil
}

// Penggunaan:
// key := make([]byte, 32) // 256-bit key — simpan dengan aman!
// plaintext := []byte("Data sensitif yang perlu dienkripsi")
// encrypted, _ := encryptData(plaintext, key)
// decrypted, _ := decryptData(encrypted, key)
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.

// 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)
import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
)

type EnvelopeEncryption struct {
	kekCipher cipher.AEAD
}

// NewEnvelopeEncryption menerima 256-bit Key Encryption Key dari KMS atau HSM.
func NewEnvelopeEncryption(kek []byte) (*EnvelopeEncryption, error) {
	if len(kek) != 32 {
		return nil, errors.New("KEK harus 256-bit")
	}
	block, err := aes.NewCipher(kek)
	if err != nil {
		return nil, err
	}
	aead, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}
	return &EnvelopeEncryption{kekCipher: aead}, nil
}

// Enkripsi data menggunakan envelope encryption.
// Setiap call menghasilkan DEK baru.
func (e *EnvelopeEncryption) Encrypt(plaintext []byte) (map[string]string, error) {
	// 1. Generate Data Encryption Key (DEK) baru
	dek := make([]byte, 32) // 256-bit DEK
	if _, err := rand.Read(dek); err != nil {
		return nil, err
	}

	// 2. Enkripsi data dengan DEK
	dataNonce := make([]byte, 12)
	if _, err := rand.Read(dataNonce); err != nil {
		return nil, err
	}
	dataBlock, _ := aes.NewCipher(dek)
	dataCipher, _ := cipher.NewGCM(dataBlock)
	encryptedData := dataCipher.Seal(nil, dataNonce, plaintext, nil)

	// 3. Enkripsi DEK dengan KEK (wrapping)
	keyNonce := make([]byte, 12)
	if _, err := rand.Read(keyNonce); err != nil {
		return nil, err
	}
	encryptedDek := e.kekCipher.Seal(nil, keyNonce, dek, nil)

	return map[string]string{
		"encrypted_dek":  base64.StdEncoding.EncodeToString(encryptedDek),
		"key_nonce":      base64.StdEncoding.EncodeToString(keyNonce),
		"encrypted_data": base64.StdEncoding.EncodeToString(encryptedData),
		"data_nonce":     base64.StdEncoding.EncodeToString(dataNonce),
	}, nil
}

// Decrypt data dari envelope.
func (e *EnvelopeEncryption) Decrypt(envelope map[string]string) ([]byte, error) {
	// 1. Unwrap DEK menggunakan KEK
	encryptedDek, _ := base64.StdEncoding.DecodeString(envelope["encrypted_dek"])
	keyNonce, _ := base64.StdEncoding.DecodeString(envelope["key_nonce"])
	dek, err := e.kekCipher.Open(nil, keyNonce, encryptedDek, nil)
	if err != nil {
		return nil, err
	}

	// 2. Decrypt data menggunakan DEK
	encryptedData, _ := base64.StdEncoding.DecodeString(envelope["encrypted_data"])
	dataNonce, _ := base64.StdEncoding.DecodeString(envelope["data_nonce"])
	dataBlock, _ := aes.NewCipher(dek)
	dataCipher, _ := cipher.NewGCM(dataBlock)
	return dataCipher.Open(nil, dataNonce, encryptedData, nil)
}

// Rotasi KEK: decrypt dengan KEK lama, re-encrypt DEK dengan KEK baru.
// Data tidak perlu di-decrypt dan re-encrypt!
func (e *EnvelopeEncryption) RotateKek(newKek []byte, envelope map[string]string) (map[string]string, error) {
	oldEncryptedDek, _ := base64.StdEncoding.DecodeString(envelope["encrypted_dek"])
	oldKeyNonce, _ := base64.StdEncoding.DecodeString(envelope["key_nonce"])
	dek, err := e.kekCipher.Open(nil, oldKeyNonce, oldEncryptedDek, nil)
	if err != nil {
		return nil, err
	}

	// Re-encrypt DEK dengan KEK baru
	newKekBlock, _ := aes.NewCipher(newKek)
	newKekCipher, _ := cipher.NewGCM(newKekBlock)
	newKeyNonce := make([]byte, 12)
	if _, err := rand.Read(newKeyNonce); err != nil {
		return nil, err
	}
	newEncryptedDek := newKekCipher.Seal(nil, newKeyNonce, dek, nil)

	// encrypted_data dan data_nonce tidak berubah
	envelope["encrypted_dek"] = base64.StdEncoding.EncodeToString(newEncryptedDek)
	envelope["key_nonce"] = base64.StdEncoding.EncodeToString(newKeyNonce)
	return envelope, nil
}

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.

// Field database yang dienkripsi secara transparan
// Padanan Go dari TypeDecorator SQLAlchemy: tipe yang mengimplementasikan
// interface Scanner dan Valuer dari database/sql
import (
	"crypto/sha256"
	"database/sql"
	"database/sql/driver"
	"encoding/hex"
	"encoding/json"
	"os"
)

type EncryptedString struct {
	Value string
	key   []byte
}

// Enkripsi sebelum simpan ke database.
func (e EncryptedString) Value() (driver.Value, error) {
	if e.Value == "" {
		return nil, nil
	}
	encrypted, err := encryptData([]byte(e.Value), e.key)
	if err != nil {
		return nil, err
	}
	return json.Marshal(encrypted)
}

// Decrypt saat baca dari database.
func (e *EncryptedString) Scan(value interface{}) error {
	if value == nil {
		e.Value = ""
		return nil
	}
	var encrypted map[string]string
	if err := json.Unmarshal(value.([]byte), &encrypted); err != nil {
		return err
	}
	plaintext, err := decryptData(encrypted, e.key)
	if err != nil {
		return err
	}
	e.Value = string(plaintext)
	return nil
}

// Konfigurasi
var fieldEncryptionKey = []byte(os.Getenv("FIELD_ENCRYPTION_KEY"))

type User struct {
	ID          int64
	Email       string
	PhoneNumber EncryptedString // Field sensitif yang dienkripsi
	TaxID       EncryptedString
	DateOfBirth EncryptedString

	// TIDAK dienkripsi — dibutuhkan untuk search/filter
	// Untuk field yang perlu di-search, simpan hash terpisah
	PhoneHash string
}

type UserRepository struct{ db *sql.DB }

func (r UserRepository) CreateUser(email string, phone, taxID, dob *string) error {
	user := User{Email: email}
	if phone != nil {
		// dienkripsi otomatis oleh EncryptedString
		user.PhoneNumber = EncryptedString{Value: *phone, key: fieldEncryptionKey}
		// Simpan hash untuk keperluan search
		sum := sha256.Sum256([]byte(*phone))
		user.PhoneHash = hex.EncodeToString(sum[:])
	}
	// ... insert user; field terenkripsi diserialisasi oleh Value()
	return nil
}

// Cari user berdasarkan nomor HP.
func (r UserRepository) FindByPhone(phone string) (*User, error) {
	sum := sha256.Sum256([]byte(phone))
	hash := hex.EncodeToString(sum[:])
	// ... SELECT * FROM users WHERE phone_hash = ? DAN scan dengan EncryptedString
	return nil, nil
}
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
// Menggunakan golang.org/x/crypto/argon2 (password KDF yang teruji)
import (
	"crypto/rand"
	"encoding/base64"
	"fmt"

	"golang.org/x/crypto/argon2"
)

// Hash password — tidak bisa di-reverse ke password asli.
func storePassword(password string) string {
	salt := make([]byte, 16)
	if _, err := rand.Read(salt); err != nil {
		panic(err)
	}
	// Argon2id dengan parameter yang sama dengan contoh Python
	hash := argon2.IDKey([]byte(password), salt, 3, 64*1024, 2, 32)
	return fmt.Sprintf("$argon2id$v=19$m=65536,t=3,p=2$%s$%s",
		base64.RawStdEncoding.EncodeToString(salt),
		base64.RawStdEncoding.EncodeToString(hash))
}

// Verifikasi hash password tersimpan.
func verifyPassword(storedHash string, password string) bool {
	// ... parse hash, hitung ulang argon2.IDKey dengan salt tersimpan,
	// dan bandingkan secara constant time (misal subtle.ConstantTimeCompare)
	return false
}

// Enkripsi data sensitif — BUKAN hashing
// Enkripsi — bisa di-decrypt untuk memproses pembayaran.
func storeCreditCard(cardNumber string, key []byte) (map[string]string, error) {
	return encryptData([]byte(cardNumber), 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
// const secretKey = "mysecretkey123" // JANGAN — source code bisa bocor di git

// ANTI-PATTERN: kunci yang terlalu pendek atau lemah
// key := []byte("password") // 64-bit — mudah di-brute force

// ANTI-PATTERN: kunci yang sama untuk semua tujuan
// masterKey := make([]byte, 32) // satu kunci untuk semua → satu bocor, semua bocor

// BENAR: kunci dari environment variable atau secret manager
import (
	"encoding/hex"
	"fmt"
	"os"
)

// Ambil kunci dari environment atau secret manager.
func getEncryptionKey() ([]byte, error) {
	keyHex := os.Getenv("ENCRYPTION_KEY")
	if keyHex == "" {
		return nil, fmt.Errorf("ENCRYPTION_KEY tidak dikonfigurasi")
	}

	key, err := hex.DecodeString(keyHex)
	if err != nil {
		return nil, err
	}
	if len(key) != 32 {
		return nil, fmt.Errorf("ENCRYPTION_KEY harus 256-bit (64 hex chars)")
	}
	return key, nil
}

// Kunci yang berbeda untuk tujuan yang berbeda
func setupKeys() map[string][]byte {
	return map[string][]byte{
		"user_pii_key": []byte(os.Getenv("USER_PII_ENCRYPTION_KEY")),
		"payment_key":  []byte(os.Getenv("PAYMENT_ENCRYPTION_KEY")),
		"session_key":  []byte(os.Getenv("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 #

// Tanda tangan digital dengan Ed25519 (membuktikan keaslian, bukan kerahasiaan)
import (
	"crypto/ed25519"
	"crypto/rand"
)

// Generate keypair Ed25519 untuk digital signature.
func generateSigningKeypair() (ed25519.PrivateKey, ed25519.PublicKey) {
	publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		panic(err)
	}
	return privateKey, publicKey
}

// Tandatangani payload untuk membuktikan keaslian.
func signPayload(payload []byte, privateKey ed25519.PrivateKey) []byte {
	return ed25519.Sign(privateKey, payload)
}

// Verifikasi tanda tangan.
func verifySignature(payload []byte, signature []byte, publicKey ed25519.PublicKey) bool {
	return ed25519.Verify(publicKey, payload, signature)
}

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
// encryptedPassword := encrypt(password, key)
// Jika key bocor, semua password terdekripsi
// ✓ Solusi: hash dengan Argon2id, bcrypt, atau scrypt

// ✗ Anti-pattern 2: reuse nonce/IV
// nonce := make([]byte, 12) // static nonce — FATAL untuk GCM!
// Reuse nonce dengan key yang sama menghancurkan keamanan GCM
// ✓ Solusi: crypto/rand untuk setiap enkripsi

// ✗ Anti-pattern 3: kunci dari password yang lemah tanpa KDF
// key := []byte(password)[: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
// block, _ := aes.NewCipher(key)
// ciphertext := cipher.NewCBCEncrypter(block, iv).CryptBlocks(...)
// ✓ Solusi: gunakan AES-GCM yang memberikan authenticated encryption

// ✗ Anti-pattern 6: mengimplementasi sendiri algoritma kriptografi
func myEncrypt(data, key []byte) []byte {
	// Implementasi custom XOR atau sejenisnya
	out := make([]byte, len(data))
	for i := range data {
		out[i] = data[i] ^ key[i%len(key)]
	}
	return out
}
// Sangat tidak aman — gunakan library yang sudah teruji
// ✓ Solusi: crypto/aes (Go), 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