Setiap data yang masuk dari luar program — ketikan pengguna, isi berkas, paket jaringan, argumen command line — wajib diperlakukan sebagai tidak tepercaya sampai terbukti sebaliknya. Chapter integer-overflow-dan-konversi sudah menyinggung satu sisi persoalan: parsing angka yang ketat dengan std::from_chars. Chapter ini memperluasnya menjadi validator utuh yang memeriksa panjang, format, dan rentang dalam satu alur, memilih whitelist daripada blacklist, dan menerapkan prinsip fail-closed: bila pemeriksaan ragu, tolak. Contoh yang kita pakai adalah formulir pendaftaran sederhana yang menerima satu usia per baris — dan kita memberinya input jahil lebih dulu, baru satu input yang sah.
Empat Gerbang Setiap Input dari Luar #
Validasi yang benar memeriksa lebih dari satu hal. Angka dari luar program melewati empat gerbang berurutan: keberadaan (input kosong langsung ditolak), panjang (sebuah usia tidak mungkin lebih dari tiga digit), format (hanya karakter digit 0 sampai 9 yang diperbolehkan), dan rentang (aturan bisnis, misalnya 13 sampai 120 tahun). Melewatkan satu gerbang saja membuka celah: parser yang menerima "007" tetapi rentangnya tidak diperiksa, atau pemeriksaan rentang yang dijalankan sebelum format dipastikan. Pada chapter ini keempatnya ditumpuk di satu fungsi kecil sehingga tidak ada yang terlewat.
Dua strategi berdiri berhadapan. Blacklist mendaftar apa yang dilarang lalu menerima sisanya; whitelist mendaftar apa yang diperbolehkan lalu menolak sisanya. Blacklist selalu kalah panjang: penyerang hanya butuh satu variasi yang luput — spasi tersembunyi, tanda plus, leading zero, karakter Unicode yang menyerupai digit — untuk lolos. Whitelist membalik posisinya: kesalahan berarti satu input sah ikut tertolak, bukan kerentanan yang terbuka. Karena itu validator di chapter ini memakai whitelist penuh: usia harus tersusun dari digit, tanpa pengecualian apa pun.
Validator Teruji: Menolak Input Jahil, Menerima Input Sah #
Program berikut membaca satu usia per baris dari std::cin. Fungsi usiaValid menumpuk keempat gerbang; input jahil — teks, tanda minus, campuran digit dan huruf, bahkan usia di luar rentang — ditolak dengan menyebut inputnya, sementara satu-satunya input sah diterima. Perhatikan atribut stdin pada blok kode: input jahil masuk lebih dulu, baru input yang valid.
// pendaftaran.cpp — validasi menyeluruh: panjang, format, rentang; gagal = tolak.
#include <charconv>
#include <iostream>
#include <string>
bool usiaValid(const std::string& teks, int& hasil) {
if (teks.empty() || teks.size() > 3) return false; // panjang: 1-3 karakter
for (char c : teks) { // format: digit saja
if (c < '0' || c > '9') return false;
}
auto [ptr, ec] = std::from_chars(teks.data(), teks.data() + teks.size(), hasil);
if (ec != std::errc{} || ptr != teks.data() + teks.size()) return false;
return hasil >= 13 && hasil <= 120; // rentang aturan bisnis
}
int main() {
std::string baris;
while (std::getline(std::cin, baris)) {
int usia = 0;
if (usiaValid(baris, usia)) {
std::cout << "diterima: usia " << usia << "\n";
} else {
std::cout << "ditolak : '" << baris << "'\n"; // default: tolak (fail-closed)
}
}
std::cout << "selesai\n";
return 0;
}Output yang Dihasilkan:
ditolak : 'abc'
ditolak : '1x'
ditolak : '-5'
ditolak : ''
ditolak : '7'
ditolak : '130'
diterima: usia 25
selesaiBedah baris-per-baris:
- Baris 7–15 (
usiaValid) — seluruh keputusan ada di satu fungsi yang mengembalikanbool; hasil parsing keluar lewat parameter referensiint& hasildan hanya boleh dipakai bila fungsi mengembalikantrue. - Baris 8 (gerbang panjang) — input kosong atau lebih dari tiga karakter ditolak sebelum menyentuh parser; sebuah usia tidak butuh lebih panjang dari itu.
- Baris 9–11 (gerbang format, whitelist) — setiap karakter wajib berada di antara
'0'dan'9'; tanda minus, spasi, dan huruf berhenti di sini. Karena itu"-5"ditolak lebih awal — meskistd::from_charssesungguhnya mampu membaca bilangan negatif, kebijakan kita tidak mengizinkannya. - Baris 12–13 (
std::from_chars) — parsing yang ketat:ecmelaporkan kegagalan, danptr != akhirmenolak hasil parsial; tanpa exception, tanpa alokasi, tanpa pengaruh locale. - Baris 14 (gerbang rentang) — aturan bisnis 13 sampai 120 berdiri sebagai gerbang terakhir, terpisah dari format, sehingga kelak mudah diberi pesan penolakan yang spesifik.
- Baris 19–26 (loop
getlinedan cabangelse) — keputusannya default-menolak (fail-closed): cabangelsemencetak input yang ditolak apa adanya. Bila validator keliru, akibatnya input sah ikut tertolak — gangguan fitur, bukan kerentanan terbuka.
Bandingkan nasib tiap input pada output: abc dan 1x berhenti di gerbang format karena bukan murni digit, -5 berhenti di gerbang yang sama karena tanda minus tidak termasuk whitelist, '' gagal di gerbang panjang, sedangkan 7 dan 130 lolos format tetapi tertahan rentang. Hanya 25 yang menempuh keempat gerbang sampai tuntas.
Whitelist, Blacklist, dan Default yang Menolak #
Chapter integer-overflow-dan-konversi sudah mengontraskan from_chars dengan std::stoi; dalam konteks validator, kontras itu berubah menjadi keputusan kebijakan. Pola stoi tetap sah asalkan dua celahnya ditutup: exception std::invalid_argument dan std::out_of_range ditangkap, dan penerimaan parsial — "25tahun" menghasilkan 25 — dicek lewat parameter pos:
// stoi melompati spasi awal dan berhenti di karakter non-digit pertama.
try {
int usia = std::stoi(teks, &pos); // bisa melempar std::invalid_argument / std::out_of_range
if (pos != teks.size() || usia < 13 || usia > 120) {
return false; // toleransi stoi harus ditutup manual
}
return true;
} catch (const std::exception&) {
return false; // fail-closed: exception berarti tolak
}Dua perbedaan itu menjelaskan kenapa from_chars menjadi pilihan bawaan untuk validator: kontraknya lebih sempit sehingga lebih sedikit celah yang harus diingat, tanpa exception yang mudah terlupa, tanpa alokasi, dan tanpa pengaruh locale. std::stoi layak dipakai bila format longgar memang diinginkan — dan bila pemeriksaannya ditutup persis seperti di atas.
Dengan validator ini gerbang input sudah rapat: panjang, format, dan rentang diperiksa berlapis, penolakan bersifat default, dan parsing memakai alat yang paling ketat. Sisa persoalan berpindah ke dalam program: data yang sudah sah tetap bisa meninggalkan jejak yang tidak perlu — seperti rahasia yang menginap terlalu lama di memori. Itulah topik chapter berikutnya.
Referensi: cppreference — std::from_chars , cppreference — std::stoi .