Akun yang baru dibuat hampir tidak bisa berbuat apa-apa: tidak bisa membaca satu tabel pun, tidak bisa membuat tabel, bahkan tidak bisa memilih database. Kekuasaan akun diberikan lewat privilege — hak untuk menjalankan jenis perintah tertentu pada objek tertentu. Memahami privilege berarti memahami dua dimensi sekaligus: apa yang boleh dilakukan (membaca, menulis, mengubah struktur) dan di mana boleh dilakukan (satu kolom, satu tabel, satu database, atau seluruh server). Pada chapter ini kita memetakan dua dimensi tersebut, mencoba pemberian hak pada empat tingkat cakupan yang berbeda, lalu memeriksa hasilnya dari dua sudut: SHOW GRANTS dan tabel information_schema.
Menyiapkan Database dan Akun Latihan #
Kita pakai satu skenario kecil yang sama untuk seluruh chapter: database belajar_aman dengan tabel produk untuk data toko, plus dua akun — aplikasi_toko untuk aplikasi kasir dan budi_analyst untuk analis data:
DROP DATABASE IF EXISTS belajar_aman;
CREATE DATABASE belajar_aman;
USE belajar_aman;
CREATE TABLE produk (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(60) NOT NULL,
kategori VARCHAR(30) NOT NULL,
harga DECIMAL(10,2) NOT NULL,
stok INT NOT NULL
);
INSERT INTO produk (nama, kategori, harga, stok) VALUES
('Kopi Arabika Gayo', 'Minuman', 85000.00, 40),
('Teh Melati Premium', 'Minuman', 45000.00, 120),
('Gula Aren Cair', 'Bahan', 32000.00, 75),
('Susu Kental Manis', 'Bahan', 18000.00, 200),
('Sirup Jeruk', 'Minuman', 27000.00, 0);
DROP USER IF EXISTS 'aplikasi_toko'@'localhost';
DROP USER IF EXISTS 'budi_analyst'@'%';
CREATE USER 'aplikasi_toko'@'localhost' IDENTIFIED BY 'SandiKuat2026!';
CREATE USER 'budi_analyst'@'%' IDENTIFIED BY 'SandiKuat2026!';Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changed
Query OK, 0 rows affected (0.00 sec)
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)Kedua akun sengaja kita biarkan tanpa hak apa pun dulu — kecuali GRANT USAGE bawaan, penanda “akun ada tapi tanpa privilege”.
Empat Tingkat Cakupan Privilege #
MySQL mengenal empat tingkat cakupan, dan penulisan objek pada klausa ON menentukan tingkat mana yang kita maksud:
| Tingkat | Format pada klausa ON | Berlaku untuk |
|---|---|---|
| Global | *.* | Seluruh server, semua database |
| Database | belajar_aman.* | Semua tabel dalam satu database |
| Tabel | belajar_aman.produk | Satu tabel atau view |
| Kolom | belajar_aman.produk + daftar kolom | Hanya kolom tertentu dalam satu tabel |
Kita praktikkan dari tingkat tabel dulu — memberi aplikasi_toko hak baca pada tabel produk, lalu menambah hak tulis:
GRANT SELECT ON belajar_aman.produk TO 'aplikasi_toko'@'localhost';
SHOW GRANTS FOR 'aplikasi_toko'@'localhost';Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
+------------------------------------------------------------------------+
| Grants for aplikasi_toko@localhost |
+------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `aplikasi_toko`@`localhost` |
| GRANT SELECT ON `belajar_aman`.`produk` TO `aplikasi_toko`@`localhost` |
+------------------------------------------------------------------------+
2 rows in set (0.00 sec)GRANT INSERT, UPDATE, DELETE ON belajar_aman.produk TO 'aplikasi_toko'@'localhost';
SHOW GRANTS FOR 'aplikasi_toko'@'localhost';Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
+------------------------------------------------------------------------------------------------+
| Grants for aplikasi_toko@localhost |
+------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `aplikasi_toko`@`localhost` |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `belajar_aman`.`produk` TO `aplikasi_toko`@`localhost` |
+------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)Perhatikan dua hal dari hasil di atas. Pertama, privilege baru tidak menimpa yang lama — INSERT, UPDATE, DELETE bergabung dengan SELECT yang sudah ada dalam satu baris GRANT SELECT, INSERT, UPDATE, DELETE ON belajar_aman.produk. Kedua, pemberian beberapa privilege sekaligus cukup ditulis dalam satu statement, dipisah koma. Sekarang kita naik ke tingkat database, misalnya agar aplikasi bisa membuat tabel baru di belajar_aman:
GRANT CREATE ON belajar_aman.* TO 'aplikasi_toko'@'localhost';
SHOW GRANTS FOR 'aplikasi_toko'@'localhost';Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
+------------------------------------------------------------------------------------------------+
| Grants for aplikasi_toko@localhost |
+------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `aplikasi_toko`@`localhost` |
| GRANT CREATE ON `belajar_aman`.* TO `aplikasi_toko`@`localhost` |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `belajar_aman`.`produk` TO `aplikasi_toko`@`localhost` |
+------------------------------------------------------------------------------------------------+
3 rows in set (0.00 sec)Baris GRANT CREATE ON belajar_aman.* muncul terpisah dari hak tingkat tabel — server mencatat keduanya pada baris grant yang berbeda di tabel sistem. Tingkat terakhir, kolom: budi_analyst hanya boleh membaca empat dari lima kolom — stok sengaja ditutup karena itu data internal gudang:
GRANT SELECT (id, nama, kategori, harga) ON belajar_aman.produk TO 'budi_analyst'@'%';
SHOW GRANTS FOR 'budi_analyst'@'%';Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
+---------------------------------------------------------------------------------------------------+
| Grants for budi_analyst@% |
+---------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `budi_analyst`@`%` |
| GRANT SELECT (`harga`, `id`, `kategori`, `nama`) ON `belajar_aman`.`produk` TO `budi_analyst`@`%` |
+---------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)Notasi SELECT (id, nama, kategori, harga) pada hasil menandakan hak baca yang hanya melekat pada empat kolom itu. Untuk akun ini, query SELECT stok FROM belajar_aman.produk akan ditolak, sedangkan SELECT nama, harga FROM ... berjalan normal.
Tabel Privilege Populer #
Sebelas privilege berikut menutupi hampir seluruh kebutuhan aplikasi dan operasional harian:
| Privilege | Fungsi | Cocok untuk akun |
|---|---|---|
SELECT | Membaca baris data | Aplikasi baca, analitik, pelaporan |
INSERT | Menambah baris baru | Aplikasi transaksi |
UPDATE | Mengubah isi baris | Aplikasi transaksi |
DELETE | Menghapus baris | Aplikasi transaksi, moderasi |
CREATE | Membuat database, tabel, view, index | Migrasi skema, akun pengembang |
DROP | Menjatuhkan database, tabel, view | Migrasi skema (sementara!) |
ALTER | Mengubah struktur tabel | Migrasi skema |
INDEX | Membuat dan menghapus index | Tuning performa |
REFERENCES | Membuat foreign key | Migrasi skema |
ALL [PRIVILEGES] | Kombinasi semua privilege di atas | Hanya akun administratif penuh |
ALL PRIVILEGES adalah pintasan yang nyaman sekaligus berbahaya: satu kata yang membuka seluruh baris tabel di atas — termasuk DROP atas database tempat akun itu bekerja. Kita bedah risikonya lebih jauh di chapter least privilege.
Privilege Administratif versus Privilege Data #
Keluarga privilege pertama (tabel di atas) mengurus data: baris, tabel, dan struktur skema. Keluarga kedua mengurus server itu sendiri — dan inilah yang hanya boleh melekat pada akun administrator. Contoh klasiknya: akun dba_shift untuk petugas operasional yang perlu menyegarkan cache dan mengamati koneksi:
DROP USER IF EXISTS 'dba_shift'@'localhost';
CREATE USER 'dba_shift'@'localhost' IDENTIFIED BY 'SandiKuat2026!';
GRANT RELOAD, PROCESS ON *.* TO 'dba_shift'@'localhost';
SHOW GRANTS FOR 'dba_shift'@'localhost';Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
+---------------------------------------------------------+
| Grants for dba_shift@localhost |
+---------------------------------------------------------+
| GRANT RELOAD, PROCESS ON *.* TO `dba_shift`@`localhost` |
+---------------------------------------------------------+
1 row in set (0.00 sec)RELOAD mengizinkan perintah FLUSH (misalnya FLUSH PRIVILEGES atau FLUSH TABLES), sedangkan PROCESS mengizinkan SHOW PROCESSLIST melihat seluruh koneksi milik user lain — dua kekuatan operasional yang tidak menyentuh data bisnis sama sekali. Privilege administratif lawas SUPER masih diterima MySQL 8.4, tetapi arah resminya sudah bermigrasi ke dynamic privileges yang jauh lebih granular (misalnya BINLOG_ADMIN atau REPLICATION_SLAVE_ADMIN), sehingga akun baru sebaiknya meminta granular itu alih-alih SUPER yang serba bisa.
Membaca Privilege dari information_schema #
SHOW GRANTS adalah tampilan ringkas untuk satu akun; untuk laporan lintas akun atau pemantauan otomatis, sumber resminya adalah tabel-tabel information_schema — satu tabel per tingkat cakupan. Kita tanyai ketiganya untuk akun-akun latihan kita:
SELECT PRIVILEGE_TYPE, IS_GRANTABLE
FROM information_schema.TABLE_PRIVILEGES
WHERE GRANTEE = '''aplikasi_toko''@''localhost'''
AND TABLE_SCHEMA = 'belajar_aman'
ORDER BY PRIVILEGE_TYPE;Output yang Dihasilkan:
+----------------+--------------+
| PRIVILEGE_TYPE | IS_GRANTABLE |
+----------------+--------------+
| DELETE | NO |
| INSERT | NO |
| SELECT | NO |
| UPDATE | NO |
+----------------+--------------+
4 rows in set (0.00 sec)SELECT PRIVILEGE_TYPE
FROM information_schema.SCHEMA_PRIVILEGES
WHERE GRANTEE = '''aplikasi_toko''@''localhost'''
AND TABLE_SCHEMA = 'belajar_aman'
ORDER BY PRIVILEGE_TYPE;Output yang Dihasilkan:
+----------------+
| PRIVILEGE_TYPE |
+----------------+
| CREATE |
+----------------+
1 row in set (0.00 sec)SELECT COLUMN_NAME
FROM information_schema.COLUMN_PRIVILEGES
WHERE GRANTEE = '''budi_analyst''@''%'''
AND TABLE_SCHEMA = 'belajar_aman'
ORDER BY COLUMN_NAME;Output yang Dihasilkan:
+-------------+
| COLUMN_NAME |
+-------------+
| harga |
| id |
| kategori |
| nama |
+-------------+
4 rows in set (0.00 sec)Hasil pertama memperlihatkan empat hak tingkat tabel milik aplikasi_toko (DELETE, INSERT, SELECT, UPDATE), hasil kedua satu hak tingkat database (CREATE), dan hasil ketiga empat kolom yang boleh dibaca budi_analyst. Perhatikan format nilai kolom GRANTEE: string 'aplikasi_toko'@'localhost' itu sendiri dikutip penuh sehingga tanda kutipnya harus digandakan — idiom yang terlihat aneh tapi wajib. Data information_schema ini identik dengan isi SHOW GRANTS, hanya saja berbentuk tabel yang bisa di-JOIN, di-ORDER BY, dan dimasukkan ke tool audit.
Membersihkan Latihan #
Tiga akun latihan dan database uji kita bereskan agar chapter ini aman diulang:
DROP USER IF EXISTS 'aplikasi_toko'@'localhost';
DROP USER IF EXISTS 'budi_analyst'@'%';
DROP USER IF EXISTS 'dba_shift'@'localhost';
DROP DATABASE IF EXISTS belajar_aman;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)Referensi #
- Privileges Provided by MySQL — MySQL 8.4 Reference Manual
- GRANT Statement — MySQL 8.4 Reference Manual
- The INFORMATION_SCHEMA Table Reference — MySQL 8.4 Reference Manual
Peta privilege kini lengkap di tangan kita: jenis-jenis hak, empat tingkat cakupannya, dan dua cara memeriksanya. Tetapi memberi dan mencabut hak itu sendiri — siklus GRANT dan REVOKE beserta jebakannya — adalah keterampilan tersendiri yang kita praktikkan di chapter berikutnya, termasuk momen ketika REVOKE justru meledak dengan error karena hak yang diminta dicabut memang tidak pernah diberikan.