Bayangkan kamu bekerja di sebuah klinik. Rekam medis pasien ditaruh di lemari tanpa kunci, dan siapa pun yang lewat boleh membuka, membaca, bahkan mencoret isinya. Ngeri, kan? Nah, itulah yang terjadi pada aplikasi web yang tidak aman. Diagnosis, keluhan, sampai alamat pasien bisa bocor hanya karena satu form yang lupa dijaga.
Di artikel ke-14 ini kamu akan belajar keamanan aplikasi web PHP secara praktis: mengacak password dengan hash, membatasi hak akses pengguna, serta menangkal SQL Injection dan XSS. Semua dipraktikkan di XAMPP dengan data pasien fiktif, jadi aman dicoba berkali-kali. Siap memasang gembok di aplikasi pasienmu?
🎓 Capaian Sub-CPMK 9
Kamu mampu menerapkan authentication, validasi, dan perlindungan dasar terhadap aplikasi web pengelolaan data kesehatan.
Mengapa Keamanan Aplikasi Web PHP Wajib untuk Data Pasien?
Data kesehatan termasuk data paling sensitif. UU Pelindungan Data Pribadi (UU No. 27 Tahun 2022) menggolongkannya sebagai data pribadi yang bersifat spesifik. Jadi, sebagai calon profesional MIK, tugasmu bukan cuma membuat aplikasi yang jalan, tetapi aplikasi yang menjaga amanah. Kabar baiknya, ada empat pertahanan dasar yang bisa kamu pasang hari ini. Semuanya terangkum dalam satu rumus:
Siapa kamu (login) · Apa yang boleh kamu lakukan (hak akses) · Apakah kiriman kamu aman (prepared statement) · Apakah tampilan kamu aman (htmlspecialchars)
Setiap ancaman punya padanan di dunia klinik. Simak tabel ini sebelum kita praktik:
Ancaman
Analogi di klinik
Dampak
Pertahanan
Password polos
Kunci ditempel di pintu
Akun dokter bisa dipakai orang lain
password_hash()
Hak akses longgar
Semua staf pegang kunci ruang arsip
Petugas pendaftaran bisa menghapus pasien
Cek role di server
SQL Injection
Menyelipkan perintah palsu ke petugas
Seluruh tabel pasien bocor
Prepared statement
XSS
Selebaran jebakan di papan pengumuman
Script jahat jalan di browser staf
htmlspecialchars()
🔥 Fakta Menarik
Di daftar OWASP Top 10 edisi 2021, Broken Access Control berada di peringkat pertama (A01), sedangkan Injection, yang mencakup SQL Injection dan XSS, ada di peringkat ketiga (A03). Tiga dari empat pertahanan di artikel ini langsung menjawab daftar tersebut.
Siapkan Lab Praktik: Database, Folder, dan Koneksi
1
Nyalakan XAMPP
Buka XAMPP Control Panel, lalu klik Start pada Apache dan MySQL sampai keduanya berwarna hijau.
2
Siapkan tabel di phpMyAdmin
Buka http://localhost/phpmyadmin, pilih database db_klinik, masuk tab SQL, lalu jalankan kode berikut. Tabel users berasal dari Artikel 13.
📄 phpMyAdmin → tab SQL
CREATE TABLE IF NOT EXISTS pasien (
id INT AUTO_INCREMENT PRIMARY KEY,
no_rm VARCHAR(10) NOT NULL,
nama VARCHAR(100) NOT NULL,
keluhan VARCHAR(255)
);
INSERT INTO pasien (no_rm, nama, keluhan) VALUES
('RM-001', 'Budi Santoso (fiktif)', 'Demam 3 hari'),
('RM-002', 'Sari Wulandari (fiktif)', 'Batuk kronis');
ALTER TABLE users MODIFY password VARCHAR(255) NOT NULL;
ALTER TABLE users ADD role ENUM('admin','dokter','petugas') NOT NULL DEFAULT 'petugas';
⚠️ Perhatian
Kalau kolom role sudah ada dari Artikel 13, lewati baris ALTER TABLE ... ADD role agar tidak muncul error Duplicate column name.
3
Buat folder kerja dan file koneksi
Buat folder C:\xampp\htdocs\klinik, buka di Sublime Text, lalu simpan file berikut.
Keamanan Aplikasi Web PHP Dimulai dari Hash Password
Hash itu seperti blender: buah masuk, jadi jus, dan jus tidak bisa dikembalikan jadi buah. Fungsi password_hash() mengubah password jadi deretan karakter acak yang tidak bisa dibalik, lengkap dengan salt otomatis. Untuk mengecek login, PHP memakai password_verify().
1
Lihat hasil hash
Simpan sebagai hash_demo.php, lalu buka http://localhost/klinik/hash_demo.php.
Hasil yang diharapkan: teks berawalan $2y$10$ sepanjang 60 karakter. Refresh halaman, hasilnya berubah terus. Itu normal karena salt-nya acak.
2
Simpan password dalam bentuk hash
Ubah proses registrasi di register.php. Perhatikan role diisi tetap petugas dan tidak diambil dari form. Kalau tidak, siapa pun bisa mendaftar sebagai admin!
📄 register.php (bagian proses)
<?php
require 'koneksi.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$hash = password_hash($_POST['password'], PASSWORD_DEFAULT);
$stmt = mysqli_prepare($koneksi,
"INSERT INTO users (username, password, role) VALUES (?, ?, 'petugas')");
mysqli_stmt_bind_param($stmt, 'ss', $username, $hash);
mysqli_stmt_execute($stmt);
echo 'Registrasi berhasil. Silakan login.';
}
// Form HTML tetap sama seperti Artikel 13
3
Verifikasi saat login
Ubah login.php agar membandingkan password dengan password_verify(), lalu simpan role ke session.
📄 login.php (bagian proses)
<?php
require 'koneksi.php';
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$stmt = mysqli_prepare($koneksi,
"SELECT id, password, role FROM users WHERE username = ?");
mysqli_stmt_bind_param($stmt, 's', $username);
mysqli_stmt_execute($stmt);
$user = mysqli_fetch_assoc(mysqli_stmt_get_result($stmt));
if ($user && password_verify($_POST['password'], $user['password'])) {
session_regenerate_id(true);
$_SESSION['id_user'] = $user['id'];
$_SESSION['role'] = $user['role'];
header('Location: cari_pasien.php');
exit;
}
$error = 'Username atau password salah.';
}
4
Cek hasilnya di phpMyAdmin
Daftar user baru, lalu buka tabel users. Kolom password harus berisi teks $2y$10$..., bukan password aslinya. Login pun harus berhasil.
💡 Tips
Jangan pakai md5() atau sha1() untuk password. Keduanya terlalu cepat dihitung sehingga mudah ditebak mesin. Dokumentasi resmi PHP menganjurkan password_hash() untuk menyimpan password.
Keamanan Aplikasi Web PHP: Atur Hak Akses Pengguna Berdasarkan Role
Di rumah sakit, kartu akses petugas farmasi tidak bisa membuka ruang arsip rekam medis. Prinsipnya sama: setelah login, tiap role hanya boleh melakukan hal yang sesuai tugasnya. Kita rancang dulu pembagiannya:
Fitur
admin
dokter
petugas
Lihat data pasien
✅
✅
✅
Tambah pasien
✅
❌
✅
Ubah keluhan
✅
✅
❌
Hapus pasien
✅
❌
❌
1
Buat satpam bernama auth.php
File ini berisi fungsi pengecekan login dan role. Ada juga fungsi e() yang kita pakai di bagian XSS nanti.
📄 auth.php
<?php
session_start();
function e($teks) {
return htmlspecialchars($teks ?? '', ENT_QUOTES, 'UTF-8');
}
function wajib_login() {
if (!isset($_SESSION['id_user'])) {
header('Location: login.php');
exit;
}
}
function wajib_role(array $boleh) {
wajib_login();
if (!in_array($_SESSION['role'], $boleh, true)) {
http_response_code(403);
exit('Akses ditolak: role kamu tidak boleh membuka halaman ini.');
}
}
2
Pasang satpam di halaman sensitif
Contohnya halaman hapus pasien yang hanya boleh dibuka admin.
📄 hapus_pasien.php
<?php
require 'auth.php';
require 'koneksi.php';
wajib_role(['admin']); // hanya admin
$id = (int) $_GET['id'];
$stmt = mysqli_prepare($koneksi, "DELETE FROM pasien WHERE id = ?");
mysqli_stmt_bind_param($stmt, 'i', $id);
mysqli_stmt_execute($stmt);
header('Location: cari_pasien.php');
exit;
Hasil yang diharapkan: login sebagai petugas lalu buka hapus_pasien.php?id=1, maka muncul pesan Akses ditolak. Data pasien tetap aman.
⚡ Insight Penting
Cek role selalu di sisi server. Menyembunyikan tombol hapus di HTML hanya kosmetik, karena orang bisa mengetik URL-nya langsung. Catatan: di aplikasi nyata, penghapusan sebaiknya lewat POST plus token CSRF, topik yang bisa kamu dalami sendiri.
Keamanan Aplikasi Web PHP dari SQL Injection: Pakai Prepared Statement
Bayangkan kamu bicara lisan ke petugas: "Cari pasien bernama Budi, atau tampilkan semua pasien." Kalau petugas menurut mentah-mentah, bocorlah semuanya. Prepared statement itu seperti formulir bersekat: isian nama hanya dianggap data, tidak pernah dianggap perintah.
🔬 Analisis: Query Rentan vs Query Aman
❌ Rentan
"... LIKE '%$cari%'"
Input ' OR 1=1 -- mengubah logika query, sehingga semua pasien tampil.
✅ Aman
"... LIKE ?"
Input yang sama hanya dicari sebagai teks nama. Hasilnya: tidak ada pasien ditemukan.
1
Buat halaman pencarian yang aman
Simpan sebagai cari_pasien.php. Perhatikan tanda ? sebagai penampung data dan bind_param yang mengisinya.
Cari Budi: satu baris muncul. Lalu cari ' OR 1=1 -- (ada spasi di akhir): hasilnya kosong, artinya serangan gagal.
⚠️ Perhatian
Uji coba serangan hanya boleh di localhost milikmu sendiri dengan data fiktif. Menguji payload di sistem orang lain tanpa izin adalah pelanggaran hukum.
Tangkal XSS di Aplikasi Web PHP dengan htmlspecialchars
XSS mirip selebaran jebakan di papan pengumuman klinik: siapa pun yang membaca ikut terkena. Penyerang menyimpan kode <script> di kolom keluhan, dan browser petugas yang membukanya akan menjalankan kode itu. Obatnya sederhana: ubah karakter spesial menjadi teks biasa sebelum ditampilkan.
Di cari_pasien.php, ubah sementara baris keluhan seperti ini, lalu refresh.
📄 cari_pasien.php (bandingkan)
<td><?= $p['keluhan'] ?></td> <!-- RENTAN: popup muncul -->
<td><?= e($p['keluhan']) ?></td> <!-- AMAN: tampil sebagai teks -->
3
Kembalikan versi aman
Pakai kembali e(). Hasil yang diharapkan: tidak ada popup, dan kolom keluhan menampilkan tulisan <script>alert('XSS')</script> apa adanya.
💡 Tips
Biasakan dua kebiasaan ini: validasi saat input masuk (misalnya format No. RM dengan preg_match) dan escape saat data keluar (lewat e()). Salah satu saja tidak cukup.
Error yang Sering Muncul & Cara Mengatasinya
Gejala
Penyebab
Solusi
Login selalu gagal setelah pakai password_verify()
Kolom password masih pendek, atau user lama masih berisi password polos
Jalankan ALTER ... VARCHAR(255), lalu daftar ulang user lama lewat register.php
Cannot modify header information - headers already sent
Ada spasi atau teks sebelum <?php, atau file tersimpan dengan BOM
Pastikan <?php di baris pertama. Di Sublime Text: File → Save with Encoding → UTF-8
Number of variables doesn't match number of parameters
Jumlah tanda ? tidak sama dengan jumlah huruf tipe di bind_param
Samakan jumlahnya, misalnya dua ? berarti 'ss'
Call to undefined function mysqli_stmt_get_result()
Driver mysqlnd tidak aktif
Pakai XAMPP versi terbaru, lalu restart Apache
Admin tetap kena Akses ditolak
$_SESSION['role'] kosong karena belum diisi saat login
Cek login.php, pastikan role disimpan, lalu logout dan login ulang
✍️ Latihan Mandiri
Role dokter. Buat ubah_keluhan.php yang hanya bisa dibuka admin dan dokter. Buktikan dengan login sebagai petugas sampai muncul pesan 403.
Tambah pasien aman. Buat tambah_pasien.php dengan prepared statement. Tolak nama kosong dan No. RM yang tidak berformat RM- diikuti angka (gunakan preg_match).
Uji XSS. Masukkan dua payload XSS berbeda ke data fiktif, lalu pastikan semua kolom di halaman daftar pasien tampil aman. Tangkap layar sebagai bukti laporan praktikum.
🏁 Kesimpulan
Aplikasi pasienmu kini punya empat lapis pertahanan: password disimpan dengan hash, hak akses dijaga per role di server, SQL Injection ditutup prepared statement, dan XSS dipadamkan htmlspecialchars(). Ingat, keamanan aplikasi web PHP itu kebiasaan, bukan fitur tambahan. Setiap menerima input, tanya: "aman dimasukkan?" Setiap menampilkan data, tanya: "aman ditampilkan?"
Artikel ini bagian dari seri Pemrograman Web D4 MIK. Di Artikel 15, semua fitur ini kita uji dan dirapikan. Bagaimana hasil praktikmu? Ceritakan di kolom komentar, dan bagikan ke temanmu yang sedang belajar PHP!
Bayangkan kamu masuk ke rumah sakit tanpa ada satu pun petugas yang menanyakan identitasmu. Siapa saja bisa membuka rekam medis, mengubah diagnosis, bahkan menghapus data kunjungan. Ngeri, kan? Itulah yang terjadi pada aplikasi web tanpa sistem masuk. Di artikel ke-13 seri Pemrograman Web D4 MIK ini, kamu akan membangun login PHP session dari nol: registrasi user, login, proteksi halaman, sampai logout. Semuanya kamu praktikkan langsung di XAMPP dengan studi kasus klinik dan data pasien fiktif. Hasil akhirnya: hanya petugas yang punya akun yang bisa membuka halaman data pasien.
Artikel ini mengunci Sub-CPMK 9 (CPMK 4): kamu mampu menerapkan authentication, validasi, dan perlindungan dasar pada aplikasi web pengelolaan data kesehatan.
Login PHP Session: Cara Kerja Authentication di Balik Layar
Protokol HTTP itu pelupa. Setiap kali browser meminta halaman, server menganggapnya pengunjung baru. Jadi setelah kamu berhasil login di login.php, server tidak otomatis ingat kamu waktu kamu pindah ke dashboard.php. Di sinilah session bekerja.
Anggap saja loket pendaftaran rumah sakit. Setelah petugas memeriksa identitasmu, kamu dikasih gelang pasien bernomor unik. Di setiap ruangan, perawat cukup melihat gelang itu tanpa menanyakan KTP lagi. Dalam PHP, gelang itu adalah session ID yang disimpan browser dalam cookie bernama PHPSESSID. Catatan isinya (kamu siapa) disimpan di server, bukan di browser.
📐 Konsep Utama
Authentication = membuktikan siapa kamu (username + password). Authorization = menentukan boleh apa kamu (dibahas di Artikel 14).
Alur login PHP session: verifikasi password → simpan penanda di $_SESSION → cek penanda itu di setiap halaman terlindungi.
BROWSER SERVER (PHP)
|-- POST login.php ---------->| cek username, password_verify()
|<-- Set-Cookie: PHPSESSID ---| $_SESSION["user_id"] = 5
|-- GET dashboard.php ------->| cookie dibawa, session dikenali
|<-- halaman dashboard -------|
|-- GET logout.php ---------->| session_destroy()
|<-- redirect ke login.php ---|
🔍 Analisis: Cookie vs Session
🍪 Cookie Disimpan di browser. Pengguna bisa melihat dan mengubahnya. Cocok untuk hal ringan, misalnya preferensi tampilan. Jangan simpan data sensitif di sini.
🗄️ Session Datanya disimpan di server. Browser hanya memegang ID-nya. Cocok untuk status login, nama petugas, dan penanda hak akses.
⚡ Insight Penting
Isi $_SESSION tidak pernah dikirim ke browser. Yang dikirim hanya session ID. Karena itu pengguna tidak bisa mengubah dirinya sendiri dari “petugas biasa” menjadi “admin” lewat browser. Tapi kalau ID itu dicuri, orang lain bisa menyamar jadi kamu. Makanya nanti kita pakai session_regenerate_id().
Praktik 1: Registrasi User untuk Login PHP Session
Sebelum ada yang bisa login, harus ada akun. Kita buat aplikasi mini “Klinik Sehat Sentosa” (nama fiktif). Ikuti langkahnya berurutan, ya.
1
Siapkan XAMPP dan folder kerja
Buka XAMPP Control Panel, klik Start pada Apache dan MySQL. Lalu buat folder klinik_auth di C:\xampp\htdocs\. Semua file di artikel ini disimpan di folder itu dengan Sublime Text.
2
Buat tabel users di phpMyAdmin
Buka http://localhost/phpmyadmin, pilih tab SQL, tempel kode berikut, lalu klik Kirim.
📄 query.sql
CREATE DATABASE IF NOT EXISTS db_klinik;
USE db_klinik;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
nama_lengkap VARCHAR(100) NOT NULL,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
✅Hasil yang diharapkan: tabel users muncul di database db_klinik dengan 5 kolom.
🔥 Fakta Menarik
Kolom password sengaja dibuat VARCHAR(255). Dokumentasi resmi PHP menyarankan panjang itu untuk hasil password_hash() karena algoritma bawaan bisa berganti seiring versi PHP. Kalau kolommu terlalu pendek, hash terpotong dan login akan selalu gagal.
3
Buat file koneksi.php
File ini dipanggil oleh semua halaman supaya koneksi database cukup ditulis sekali.
Validasi dasar kita lakukan di sini: kolom tidak boleh kosong, password minimal 8 karakter, konfirmasi harus sama, dan username tidak boleh kembar. Password disimpan dalam bentuk hash, bukan teks asli.
✅Hasil yang diharapkan: buka http://localhost/klinik_auth/register.php, daftarkan petugas fiktif (misalnya username petugas_ani). Di phpMyAdmin, kolom password berisi teks acak berawalan $2y$, bukan password aslinya.
💡 Tips
Perhatikan tanda ? pada query. Itu prepared statement: data dari form dikirim terpisah dari perintah SQL. Kebiasaan ini akan jadi tameng utama di Artikel 14 saat kita membahas SQL Injection.
Praktik 2: Login PHP Session dengan password_verify
Sekarang bagian serunya. Alurnya: ambil data user berdasarkan username, cocokkan password dengan password_verify(), lalu simpan penanda login di $_SESSION. Perhatikan juga pesan error yang sengaja dibuat sama untuk username salah maupun password salah, supaya penyerang tidak tahu username mana yang terdaftar.
5
Buat file login.php
Baris session_start() wajib berada paling atas, sebelum output apa pun.
Error headers already sent hampir selalu karena ada spasi, baris kosong, atau teks sebelum <?php. Fungsi session_start() dan header() hanya bisa jalan sebelum browser menerima output apa pun. Letakkan blok PHP di baris pertama file.
6
Uji coba login
Buka http://localhost/klinik_auth/login.php. Coba dua skenario: (a) password sengaja salah, (b) username dan password benar.
✅Hasil yang diharapkan: skenario (a) menampilkan pesan “Username atau password salah.” Skenario (b) memindahkanmu ke dashboard.php (halamannya kita buat di langkah berikutnya, jadi sementara akan muncul error file tidak ditemukan; itu normal).
Praktik 3: Proteksi Halaman dan Logout pada Login PHP Session
Login saja belum cukup. Kalau halaman dashboard.php tidak mengecek session, siapa pun yang mengetik alamatnya langsung bisa masuk. Ibarat pintu depan dijaga satpam tapi pintu belakang terbuka lebar. Solusinya: satu file “penjaga” yang dipanggil di setiap halaman terlindungi.
7
Buat file penjaga (auth.php)
Kalau penanda login tidak ada, pengunjung langsung diusir ke halaman login.
(1) Dalam keadaan belum login, ketik langsung http://localhost/klinik_auth/dashboard.php. (2) Login lalu buka dashboard. (3) Klik Logout, lalu tekan tombol Back di browser.
✅Hasil yang diharapkan: skenario 1 otomatis dialihkan ke login.php; skenario 2 menampilkan “Selamat datang, nama petugas”; skenario 3 tidak lagi menampilkan isi dashboard.
💡 Tips
Mau melihat “gelang pasien”-mu? Tekan F12 di browser, buka tab Application (atau Storage) lalu Cookies. Kamu akan menemukan PHPSESSID. Hapus cookie itu secara manual, lalu refresh dashboard. Kamu langsung ditendang ke halaman login.
Ini rangkuman fungsi penting yang baru saja kamu pakai. Simpan sebagai contekan:
Fungsi / Variabel
Tugasnya
Dipakai di
session_start()
Memulai atau melanjutkan session
login, auth, logout
$_SESSION[]
Menyimpan data milik satu pengguna di server
login, dashboard
password_hash()
Mengubah password jadi hash satu arah
register
password_verify()
Mencocokkan password input dengan hash
login
session_regenerate_id(true)
Mengganti session ID setelah login berhasil
login
session_destroy()
Menghapus data session di server
logout
Error yang Sering Muncul & Cara Mengatasinya
Tenang, error itu teman belajar. Ini yang paling sering muncul saat mahasiswa praktik login PHP session di laboratorium:
Pesan / Gejala
Penyebab
Solusi
Cannot modify header information - headers already sent
Ada output (spasi, HTML, echo) sebelum session_start() atau header()
Pindahkan blok PHP ke baris pertama. Di Sublime Text, simpan dengan File → Save with Encoding → UTF-8 (tanpa BOM).
Undefined array key "user_id"
Mengakses $_SESSION sebelum session_start() atau sebelum login
Panggil session_start() dulu, dan cek dengan isset().
Login selalu gagal padahal password benar
Kolom password terlalu pendek, atau password di database diisi manual tanpa hash
Ubah jadi VARCHAR(255), lalu daftar ulang lewat register.php.
Duplicate entry ... for key 'username'
Username sama sudah ada (kolom bersifat UNIQUE)
Pakai username lain, atau pastikan pengecekan di register.php berjalan.
Failed opening required 'koneksi.php'
Salah folder atau salah nama file
Pastikan semua file ada di satu folder htdocs\klinik_auth.
Setelah logout, tombol Back masih menampilkan dashboard
Browser menyimpan halaman di cache
Pastikan baris Cache-Control di auth.php ada, lalu refresh.
Latihan Mandiri: Kuatkan Authentication Klinikmu
Kerjakan di folder yang sama, lalu simpan tangkapan layar hasilnya sebagai bukti praktikum:
1
Catat waktu login terakhir. Tambahkan kolom last_login (DATETIME) di tabel users, isi dengan NOW() setiap login berhasil, dan tampilkan di dashboard.
2
Lindungi halaman data pasien. Buat daftar_pasien.php yang menampilkan 5 data pasien fiktif dari tabel pasien, dan kunci dengan auth.php. Buktikan halaman itu tidak bisa dibuka tanpa login.
3
Session timeout. Simpan waktu aktivitas terakhir di $_SESSION["waktu_terakhir"]. Jika selisihnya lebih dari 15 menit, otomatis logout. Ini praktik lazim untuk komputer bersama di loket pendaftaran.
Kamu sudah membangun siklus lengkap login PHP session: registrasi dengan password_hash(), login dengan password_verify(), penanda di $_SESSION, proteksi halaman lewat auth.php, dan logout yang membersihkan session sampai tuntas. Inti pelajarannya sederhana: browser hanya memegang “gelang”, sedangkan server yang menentukan siapa yang boleh masuk.
Tapi login baru satu lapis. Di Artikel 14, kita tambah lapisan berikutnya: hak akses per peran, hash password yang lebih dalam, serta perlindungan SQL Injection dan XSS. Bagian mana yang paling bikin kamu penasaran atau tersangkut? Ceritakan di kolom komentar, dan bagikan artikel ini ke teman satu kelompok praktikummu.
saifiahmada.com adalah blog belajar programming Indonesia, membahas lengkap materi bahasa pemrograman: code HTML, CSS, Bootstrap, Desain, PHP, MySQL, coding Java, Query, SQL, dan dunia linux