Checksum Saja Tidak Cukup: Cara Memverifikasi Installer dengan SHA-256 dan Tanda Tangan OpenSSL

Artikel
Authors
Checksum Saja Tidak Cukup: Cara Memverifikasi Installer dengan SHA-256 dan Tanda Tangan OpenSSL

Kamu menjalankan satu perintah di server: unduh paket, ekstrak, pasang. Paketnya datang dari link resmi, lewat HTTPS, dan halaman unduhannya bahkan mencantumkan checksum SHA-256. Kamu cocokkan, hasilnya sama. Aman.

Belum tentu. Kalau orang yang bisa mengubah paket juga bisa mengubah halaman tempat checksum itu ditulis, checksum yang cocok hanya membuktikan satu hal: file yang kamu unduh sama dengan file yang dia taruh. Bukan sama dengan file yang dibuat pembuatnya.

Ini yang saya pikirkan waktu menulis installer HeyUrAssistant. Installer itu dijalankan pembeli di VPS miliknya sendiri, dengan akses root, lalu memasang asisten yang nantinya memegang akun WhatsApp dan data keluarga mereka. Kalau paket yang terpasang bukan paket asli, semua pengamanan lain di dalam aplikasi tidak ada artinya.

Artikel ini membahas empat pemeriksaan yang dijalankan installer itu sebelum memasang apa pun, kenapa setiap pemeriksaan ada, lalu cara menerapkannya di installer kamu sendiri, langkah demi langkah. Semua screenshot berasal dari demo yang saya jalankan dengan pemeriksaan yang sama persis.

Checksum dan tanda tangan menjawab pertanyaan yang berbeda

Checksum SHA-256 menjawab: apakah file ini utuh? SHA-256 mengubah isi file menjadi 64 karakter heksadesimal. Ubah satu baris saja, hasilnya berubah total. Checksum bagus untuk menangkap unduhan yang rusak atau terpotong, dan untuk menangkap perubahan kalau checksum yang kamu pegang berasal dari tempat yang tidak ikut diubah.

Masalahnya ada di kalimat terakhir itu. Checksum tidak punya pemilik. Siapa pun bisa menghitung checksum dari file apa pun. Kalau penyusup mengganti paket di server unduhan dan sekaligus mengganti checksum di halaman yang sama, keduanya cocok.

Tanda tangan digital menjawab pertanyaan lain: apakah file ini dibuat oleh pemegang kunci tertentu? Pembuat menandatangani paket dengan kunci privat yang hanya ada di tangannya. Siapa pun bisa memeriksa tanda tangan itu dengan kunci publik pasangannya, tapi tidak ada yang bisa membuat tanda tangan baru yang sah tanpa kunci privat. Penyusup yang mengubah paket tidak bisa menandatanganinya ulang.

Kecuali dia juga mengganti kunci publiknya. Kalau installer menerima kunci publik apa pun yang diberikan, penyusup cukup membuat pasangan kunci sendiri, menandatangani paket palsunya, lalu menyerahkan kunci publik miliknya. Tanda tangannya sah, untuk kunci yang salah. Karena itu kunci publik yang benar harus dipatok di dalam installer: installer tahu checksum kunci publik yang seharusnya, dan menolak kunci lain.

Ada satu celah lagi yang tidak ada hubungannya dengan keaslian. Arsip tar bisa berisi path seperti app/../../etc/sesuatu. Saat diekstrak dengan hak root, file itu bisa mendarat di luar folder tujuan. Paket yang ditandatangani dengan sah pun bisa membawa path seperti ini kalau proses build-nya keliru, jadi isi arsip tetap diperiksa sebelum diekstrak.

Yang dijalankan installer HeyUrAssistant

Versi pertama installer, 13 Agustus 2026, sudah mewajibkan checksum SHA-256 dan bisa memeriksa tanda tangan, tapi tanda tangannya masih opsional. Seminggu kemudian, 20 Agustus, saya mengeraskannya: tanda tangan menjadi wajib untuk mode --apply, dan checksum kunci publik dipatok di dalam script.

Urutan pemeriksaannya sekarang begini, dan semuanya terjadi sebelum ada satu file pun yang diekstrak ke server:

  1. Runtime Node.js diunduh dari nodejs.org dan dicocokkan dengan checksum yang dipatok per arsitektur (x64 dan arm64). Versi Node tidak diambil dari server pembeli.
  2. Paket rilis dicocokkan dengan checksum yang diberikan.
  3. Kunci publik dicocokkan dengan checksum yang dipatok di script.
  4. Tanda tangan paket diperiksa dengan openssl dgst -sha256 -verify. Kalau tanda tangan diunduh, URL-nya wajib HTTPS.
  5. Isi arsip diperiksa. Path yang mengandung .. atau . sebagai salah satu bagiannya membuat pemasangan berhenti.

Tanpa checksum, tanpa kunci publik, atau tanpa tanda tangan, mode --apply menolak berjalan sama sekali. Setiap kegagalan menghentikan script dengan pesan yang jelas, dan folder sementara dibersihkan lewat trap.

Untuk membuktikan setiap pemeriksaan benar-benar menangkap kasusnya, saya menyalin keempat pemeriksaan paket itu ke script kecil verify.sh, lalu menjalankannya terhadap lima skenario:

Lima skenario, satu lolos dan empat ditolak
Lima skenario, satu lolos dan empat ditolak

Skenario 3 adalah alasan tanda tangan ada: checksum di halaman unduhan ikut diganti, jadi pemeriksaan checksum lolos, tapi tanda tangan pembuat tidak cocok dengan paket yang diubah. Skenario 4 adalah alasan kunci publik dipatok: penyusup menandatangani paket palsunya dengan kunci miliknya sendiri, dan yang menolaknya adalah checksum kunci publik.

Memasang empat pemeriksaan di installer kamu

Demo ini memakai bash, sha256sum, dan OpenSSL 3 di Linux. Paket contohnya hanya berisi satu file JavaScript.

Langkah 1: buat pasangan kunci, sekali saja

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out pembuat.pem
openssl pkey -in pembuat.pem -pubout -out pembuat.pub.pem

pembuat.pem adalah kunci privat. Simpan di luar repo dan di luar server unduhan. Kalau kunci ini bocor, semua pemeriksaan di artikel ini tidak lagi berarti.

Langkah 2: di sisi pembuat, buat checksum dan tanda tangan setiap rilis

sha256sum release.tar.gz > release.tar.gz.sha256
openssl dgst -sha256 -sign pembuat.pem -out release.sig release.tar.gz
sha256sum pembuat.pub.pem
Checksum paket, tanda tangan, dan checksum kunci publik
Checksum paket, tanda tangan, dan checksum kunci publik

Perintah ketiga hanya dijalankan sekali. Hasilnya adalah nilai yang akan dipatok di installer. Di installer HeyUrAssistant, dua perintah pertama ada di script build rilis, jadi setiap paket otomatis punya file .sha256 dan .sig di sebelahnya.

Langkah 3: lihat sendiri bedanya checksum dan tanda tangan

Ubah satu baris di paket, lalu bandingkan:

Satu baris berubah, checksum dan tanda tangan sama-sama menolak
Satu baris berubah, checksum dan tanda tangan sama-sama menolak

Paket yang diubah hanya menambah satu panggilan fungsi di index.js, dan checksumnya berubah total. Tanda tangan lama juga tidak berlaku untuk paket itu. Bedanya, checksum baru bisa dibuat siapa pun, sedangkan tanda tangan baru hanya bisa dibuat pemegang pembuat.pem.

Langkah 4: tulis pemeriksaan di installer, urutannya penting

#!/usr/bin/env bash
set -u
file=$1; sha=$2; sig=$3; pub=$4; pinned_pub_sha=$5

printf '%s  %s\n' "$sha" "$file" | sha256sum --check --status \
  || { echo "GAGAL: checksum paket tidak cocok"; exit 1; }
printf '%s  %s\n' "$pinned_pub_sha" "$pub" | sha256sum --check --status \
  || { echo "GAGAL: checksum kunci publik tidak cocok"; exit 1; }
openssl dgst -sha256 -verify "$pub" -signature "$sig" "$file" >/dev/null 2>&1 \
  || { echo "GAGAL: tanda tangan tidak valid"; exit 1; }
tar -tzf "$file" | grep -Eq '(^|/)\.\.?(/|$)' \
  && { echo "GAGAL: arsip berisi path tidak aman"; exit 1; }
echo "OK: paket asli, boleh dipasang"

Tiga hal yang sering terlewat di sini:

  • sha256sum --check dipakai, bukan membandingkan string sendiri. Format input-nya dua spasi di antara checksum dan nama file.
  • Kunci publik diperiksa sebelum dipakai untuk memeriksa tanda tangan. Kalau urutannya dibalik, kamu sudah memercayai kunci yang belum terbukti.
  • tar -tzf hanya membaca daftar isi, tidak mengekstrak. Pemeriksaan path terjadi sebelum ada file yang ditulis.

Langkah 5: patok checksum kunci publik di installer

Di installer HeyUrAssistant, nilainya menjadi default variabel, jadi pembeli tidak perlu mengetik apa pun:

public_key_sha256="${HYA_PUBLIC_KEY_SHA256:-<checksum kunci publik dari langkah 2>}"

Kunci publiknya sendiri boleh diunduh dari mana saja, karena installer hanya menerima kunci yang checksum-nya sama dengan nilai ini.

Langkah 6: tolak berjalan kalau bahan pemeriksaan tidak lengkap

Pemeriksaan yang bisa dilewati dengan cara tidak memberi argumen sama saja dengan tidak ada. Untuk mode yang benar-benar memasang, wajibkan semuanya:

[ -n "$release_sha256" ] || { echo "--sha256 is required for --apply." >&2; exit 2; }
[ -n "$public_key_file" ] && [ -f "$public_key_file" ] || { echo "--public-key is required for --apply." >&2; exit 2; }
[ -n "$signature_url" ] || [ -n "$signature_file" ] || { echo "A release signature is required for --apply." >&2; exit 2; }
case "$signature_url" in https://*) ;; *) echo "Signature URL must use HTTPS." >&2; exit 2;; esac

Baris terakhir hanya berlaku saat tanda tangan diunduh lewat URL. Di installer aslinya, baris itu ada di dalam cabang yang mengunduh tanda tangan.

Checklist untuk installer kamu

  • Checksum SHA-256 dibuat otomatis di script build, bukan diketik manual.
  • Setiap rilis ditandatangani dengan kunci privat yang tidak pernah ada di server unduhan.
  • Checksum kunci publik dipatok di dalam installer.
  • Kunci publik diperiksa sebelum dipakai memeriksa tanda tangan.
  • Isi arsip diperiksa dengan tar -t sebelum diekstrak, dan path dengan .. ditolak.
  • Dependensi yang diunduh saat pemasangan, misalnya runtime, juga punya checksum yang dipatok.
  • Mode yang memasang menolak berjalan tanpa checksum, tanda tangan, dan kunci publik.
  • Semua URL unduhan wajib HTTPS.
  • Ada skenario uji untuk setiap kegagalan, bukan hanya jalur yang berhasil.

Kalau kamu hanya pengguna, bukan pembuat: saat sebuah proyek menyediakan file .sig atau .asc di sebelah paketnya, periksa tanda tangannya, dan ambil kunci publiknya dari sumber yang terpisah dari halaman unduhan, misalnya repo resmi atau dokumentasi yang sudah kamu kenal.

Apa yang tidak dijamin pemeriksaan ini

  • Semua ini bergantung pada kunci privat. Kalau kunci privat bocor, penyusup bisa menandatangani paket palsu yang lolos semua pemeriksaan. Mengganti kunci berarti merilis installer baru dengan checksum kunci yang baru.
  • Installer itu sendiri harus didapat dari sumber yang benar. Pemeriksaan ini melindungi paket yang diunduh installer. Kalau seseorang mengganti script installer-nya, dia bisa menghapus pemeriksaannya. Karena itu installer sebaiknya diambil dari repo atau rilis resmi, bukan dari salinan yang dikirim orang.
  • Demo ini menyederhanakan. verify.sh hanya berisi pemeriksaan paket. Installer aslinya juga memeriksa runtime Node.js, sumber tanda tangan, dan konfigurasi lain sebelum memasang.
  • Saat paket berbahaya di skenario 5 diperiksa, tar juga mencetak peringatan bahwa ia membuang awalan app/../../. Peringatan itu disembunyikan dari screenshot supaya fokus pada hasil pemeriksaan. tar versi GNU memang sudah membuang awalan seperti itu saat mengekstrak, tapi installer sebaiknya tidak bergantung pada perilaku satu versi tar.
  • Tanda tangan membuktikan siapa pembuatnya, bukan bahwa isinya bebas bug. Paket asli tetap bisa punya celah. Itu urusan tes dan review kode, bukan urusan verifikasi unduhan.

Setiap baris curl atau wget di script deploy yang langsung diikuti ekstrak atau eksekusi adalah tempat langkah 4 perlu dipasang.

Artikel terkait

Tulisan lain yang nyambung dengan topik ini.

Comments

Komentar akan tampil setelah disetujui.

Memuat komentar...