Episode ini membahas keamanan kode C++: memory safety dan mitigasi buffer overflow, penggunaan aman C string API dibanding std::string, input validation dengan boundary checks, serta cybersecurity best practices untuk aplikasi produksi.

C++ memberi kalian kontrol penuh atas memori — dan kontrol penuh berarti kesalahan kecil bisa menjadi celah keamanan yang parah. Sebagian besar kerentanan di aplikasi C/C++ berakar pada pengelolaan memori yang salah: buffer overflow, use-after-free, dan format string yang tidak aman.
Episode 14 mengajarkan secure coding: mengapa buffer overflow terjadi dan bagaimana mencegahnya, mengapa std::string lebih aman daripada array char[], cara memvalidasi dan mensanitasi input, mitigasi yang disediakan compiler, serta best practices cybersecurity yang diterapkan di industri.
Buffer overflow terjadi saat program menulis lebih banyak data daripada kapasitas buffer. Data meluap ke memori di sekitarnya, dan penyerang bisa memanfaatkannya untuk menimpa alamat kembali fungsi atau menjalankan kode sendiri. Contoh klasik:
cat > rentan.cpp <<'EOF'
#include <iostream>
#include <cstring>
int main() {
char nama[8];
std::cout << "Masukkan nama: ";
std::cin >> nama;
std::cout << "Halo " << nama << "\n";
}
EOF
g++ -std=c++20 rentan.cpp -o rentanstd::cin >> nama menulis ke buffer char nama[8] tanpa memeriksa panjang input. Jika pengguna mengetik lebih dari 7 karakter, data meluap dan menimpa memori lain — undefined behavior yang bisa menjadi kerentanan. Kode seperti ini tidak boleh muncul di produksi.
Aturan utama: jangan pernah membiarkan data masuk ke buffer tanpa memeriksa ukuran. Gunakan tipe yang tahu panjangnya sendiri. std::string, std::vector, dan kontainer STL mengelola batasnya secara otomatis, sehingga overflow tidak mungkin terjadi lewat API normal.
Fungsi C klasik strcpy, strcat, sprintf, dan gets tidak memeriksa batas tujuan. gets bahkan sudah dihapus dari standar C karena tidak mungkin dipakai aman. Ganti semuanya dengan std::string dan std::string_view:
cat > aman.cpp <<'EOF'
#include <iostream>
#include <string>
int main() {
const char* raw = "teks panjang melebihi buffer";
char buffer[8];
// strcpy(buffer, raw); // overflow!
std::string aman = raw;
std::cout << "Panjang: " << aman.size() << "\n";
std::cout << aman << "\n";
}
EOF
g++ -std=c++20 aman.cpp -o aman
./amanstd::string aman = raw menyalin string dengan ukuran yang dikelola sendiri — aman tanpa memikirkan batas buffer. std::string_view memberikan tampilan non-owning ke string yang sama, berguna untuk parameter fungsi tanpa menyalin.
printf klasik membawa risiko format string vulnerability: jika format diterima dari pengguna, penyerang bisa membaca dan menulis memori lewat specifier seperti %x dan %n. Jangan pernah melempar input pengguna langsung ke printf. Di C++20, std::format adalah pengganti yang aman:
#include <format>
// std::print("Nama: {}\n", nama); // C++23, format amanstd::format("Nama: {} ...)" memisahkan format dari argumen dan memvalidasi keduanya saat kompilasi, menghilangkan seluruh kelas bug format.
Semua input yang datang dari luar — pengguna, jaringan, file — tidak bisa dipercaya. Validasi dulu sebelum dipakai: panjang, rentang, dan format:
cat > validasi.cpp <<'EOF'
#include <iostream>
#include <string>
#include <cctype>
bool valid_umur(const std::string& s) {
if (s.empty() || s.size() > 3) return false;
for (char c : s) {
if (!std::isdigit(c)) return false;
}
return true;
}
int main() {
std::string input;
std::cin >> input;
if (valid_umur(input)) {
int umur = std::stoi(input);
std::cout << "Umur valid: " << umur << "\n";
} else {
std::cerr << "Input tidak valid\n";
}
}
EOF
g++ -std=c++20 validasi.cpp -o validasi
./validasivalid_umur(s) memeriksa input kosong, panjang maksimal, dan bahwa semua karakter adalah digit sebelum mengonversinya. Pola ini — validasi sebelum konversi — mencegah std::stoi melempar atau menghasilkan nilai di luar ekspektasi.
Saat mengakses elemen kontainer dengan indeks yang mungkin tidak valid, pakai at() yang melempar std::out_of_range, bukan operator[] yang tanpa pemeriksaan. Untuk iterasi, range-based for dan iterator menghilangkan kebutuhan indeks manual sama sekali — dan dengan itu, kebutuhan boundary check.
Compiler modern menyediakan mitigasi yang bisa diaktifkan lewat bendera: PIE (-fPIE) untuk address space randomization, stack protector (-fstack-protector-strong) untuk mendeteksi corupsi stack, dan Fortify (-D_FORTIFY_SOURCE=2) untuk memeriksa buffer pada fungsi libc:
g++ -std=c++20 -fstack-protector-strong -D_FORTIFY_SOURCE=2 \
-Wl,-z,relro,-z,now app.cpp -o appBendera -fstack-protector-strong memasang canary untuk mendeteksi overflow stack, dan -Wl,-z,relro,-z,now membuat segmen GOT read-only setelah linking. Kombinasikan semua ini di build release untuk mempersulit eksploitasi.
Gunakan AddressSanitizer dan UndefinedBehaviorSanitizer selama pengembangan untuk menangkap bug sebelum dirilis. -fsanitize=address,undefined mendeteksi overflow, use-after-free, dan perilaku undefined lainnya. Di CI, tambahkan static analysis clang-tidy dengan pemeriksaan keamanan. Episode 11 dan 19 akan memperdalam kedua hal ini.
Warning
Mitigasi compiler menaikkan biaya eksploitasi, bukan menghilangkan bug. Pengganti utama tetap kode yang aman: std::string, boundary checks, dan validasi input.
Beberapa prinsip keamanan yang berlaku universal di kode C++:
Sanitasi berarti membersihkan input dari karakter atau struktur yang berbahaya sebelum dipakai — misalnya menghindari injection saat membangun query atau log. Di C++, hindari membuat string eksekusi dinamis; pilih mekanisme terstruktur.
Inti yang harus dibawa pulang:
std::string, std::string_view, dan kontainer STL alih-alih array mentah.strcpy, strcat, sprintf, dan gets.Di episode 15 selanjutnya kita akan membahas performance dan optimization — profiling dengan perf dan Valgrind, optimasi memory layout dan cache friendliness, compiler optimization flags, inline dan loop unrolling, serta tradeoff antara readability dan performance.