Tutorial Membuat Aplikasi Scanner Keamanan PHP Sederhana dengan PHP Native

Tutorial membuat PHP Security Scanner

Pernahkah kamu membayangkan memiliki sebuah aplikasi yang bisa memeriksa source code PHP dan menemukan indikasi kelemahan keamanan secara otomatis?

Misalnya, ketika kamu mengembangkan website menggunakan PHP Native, aplikasi tersebut bisa memeriksa apakah terdapat penggunaan fungsi eval() yang berisiko, query SQL yang mencurigakan, penggunaan input pengguna tanpa penanganan yang tepat, hingga password yang ditulis langsung di dalam source code.

Menariknya, kita tidak harus selalu menggunakan aplikasi keamanan yang kompleks untuk mulai melakukan pemeriksaan tersebut. Dengan PHP Native, kita bisa membangun sebuah PHP Security Scanner sederhana yang memiliki kemampuan membaca file, menganalisis pola kode, dan menghasilkan laporan temuan keamanan.

Pada tutorial ini, kita akan membuat aplikasi scanner keamanan PHP menggunakan kemampuan bawaan PHP tanpa framework tambahan. Kita juga akan menggunakan pendekatan yang terinspirasi dari OWASP (Open Worldwide Application Security Project), khususnya konsep Static Application Security Testing atau SAST.

Jadi, selain memahami teori keamanan aplikasi web, kamu juga akan memiliki sebuah tools sederhana yang bisa dikembangkan menjadi bagian dari workflow pengembangan aplikasi.

1. Mengenal PHP Security Scanner

PHP Security Scanner merupakan tools yang digunakan untuk memeriksa source code PHP guna menemukan pola implementasi yang berpotensi menimbulkan masalah keamanan.

Berbeda dengan antivirus yang lebih berfokus pada pendeteksian malware, scanner yang akan kita buat memiliki tujuan lebih spesifik, yaitu memeriksa pola penulisan kode yang berisiko terhadap keamanan aplikasi.

Contohnya, sebuah aplikasi memiliki kode seperti berikut:

<?php

$username = $_GET['username'];

$query = "SELECT * FROM users
          WHERE username = '$username'";

$result = mysqli_query($conn, $query);

?>

Secara fungsional, kode tersebut mungkin berjalan normal. Namun, cara menggabungkan input pengguna langsung ke dalam query SQL dapat menimbulkan risiko SQL Injection.

Scanner yang akan kita buat akan mencari pola-pola seperti ini dan memberikan peringatan kepada developer agar bagian tersebut diperiksa kembali.

Apa saja yang bisa dideteksi?

Untuk versi pertama, kita akan membuat enam kategori pemeriksaan:

No. Kategori Objek Pemeriksaan
1 Dangerous Functions Penggunaan eval(), exec(), system(), dan shell_exec().
2 SQL Injection Indikasi input pengguna yang langsung digunakan dalam query SQL.
3 Potential XSS Output langsung dari parameter GET, POST, atau REQUEST.
4 Hardcoded Credentials Password, API key, atau secret yang ditulis langsung di source code.
5 Debug Exposure Konfigurasi debug yang berpotensi menampilkan informasi internal.
6 Command Execution Penggunaan fungsi yang dapat menjalankan perintah sistem.

Perlu dipahami bahwa pemeriksaan ini menggunakan pola sederhana atau regular expression. Karena itu, scanner belum mampu memahami seluruh alur data dan konteks program seperti tools SAST profesional.

2. Persiapan Tools dan Lingkungan Pengembangan

Sebelum mulai membuat aplikasi, pastikan komputer sudah memiliki beberapa komponen berikut:

  • PHP 8.x atau versi yang masih mendapatkan dukungan keamanan.
  • Apache atau web server lainnya.
  • Visual Studio Code atau editor kode lainnya.
  • Browser untuk mengakses aplikasi jika diperlukan.

Untuk tutorial ini, kita tidak membutuhkan MySQL karena scanner hanya membaca file source code dan tidak memerlukan database.

Kamu bisa menggunakan XAMPP sebagai lingkungan pengembangan lokal.

3. Membuat Struktur Folder Aplikasi

Buat sebuah folder proyek dengan nama php-security-scanner.

Susun struktur folder seperti berikut:

php-security-scanner/
│
├── scanner.php
│
└── src/
    ├── config.php
    ├── login.php
    ├── dashboard.php
    ├── users.php
    └── process.php

Penjelasan struktur:

  • scanner.php: File utama yang menjalankan proses pemindaian.
  • src/: Direktori yang berisi source code PHP yang akan diperiksa.
  • config.php: Contoh file konfigurasi aplikasi.
  • login.php: Contoh halaman autentikasi.
  • dashboard.php: Contoh halaman dashboard.
  • users.php: Contoh pengelolaan pengguna.
  • process.php: Contoh pemrosesan data.

Dalam tutorial ini, kita sengaja memisahkan file scanner dengan direktori source code yang diperiksa. Dengan begitu, scanner tidak akan memindai dirinya sendiri dan proses pemeriksaan lebih mudah dikontrol.

4. Membuat Mesin Scanner Menggunakan PHP

Sekarang kita masuk ke bagian utama, yaitu membuat mesin pemindai keamanan.

Buka file scanner.php, kemudian masukkan kode berikut:

<?php

/**
 * PHP SECURITY SCANNER
 * Simple Static Code Analysis
 * Version 1.0
 */

// Scanner hanya boleh dijalankan melalui CLI.
if (PHP_SAPI !== 'cli') {
    http_response_code(403);
    exit("Scanner hanya dapat dijalankan melalui terminal.");
}

// Direktori source code yang diperiksa.
$targetDir = __DIR__ . '/src';

// Direktori yang tidak perlu diperiksa.
$excludedDirs = [
    'vendor',
    'node_modules',
    '.git',
    'storage',
    'cache'
];

// Maksimal file yang diperiksa.
$maxFiles = 5000;

if (!is_dir($targetDir)) {
    exit("Direktori source code tidak ditemukan.");
}

// Daftar aturan pemeriksaan.
$rules = [

    [
        'name' => 'Dangerous Function',
        'severity' => 'HIGH',
        'pattern' => '/\beval\s*\(/i',
        'message' => 'Ditemukan penggunaan eval().',
        'recommendation' => 'Hindari eval() dan gunakan pendekatan yang lebih aman.'
    ],

    [
        'name' => 'Command Execution',
        'severity' => 'HIGH',
        'pattern' => '/\b(exec|system|shell_exec|passthru|proc_open|popen)\s*\(/i',
        'message' => 'Ditemukan fungsi eksekusi perintah sistem.',
        'recommendation' => 'Periksa sumber input dan hindari eksekusi perintah yang tidak diperlukan.'
    ],

    [
        'name' => 'Potential SQL Injection',
        'severity' => 'HIGH',
        'pattern' => '/\b(?:mysqli_query|mysql_query|query)\s*\([^;\r\n]{0,200}\$_(?:GET|POST|REQUEST)\b/i',
        'message' => 'Input pengguna terindikasi digunakan langsung dalam query.',
        'recommendation' => 'Gunakan prepared statement dan parameter binding.'
    ],

    [
        'name' => 'Potential XSS',
        'severity' => 'MEDIUM',
        'pattern' => '/\b(?:echo|print)\b.{0,200}\$_(?:GET|POST|REQUEST)\b/i',
        'message' => 'Input pengguna terindikasi ditampilkan langsung.',
        'recommendation' => 'Periksa output encoding menggunakan htmlspecialchars() sesuai konteks.'
    ],

    [
        'name' => 'Hardcoded Credentials',
        'severity' => 'HIGH',
        'pattern' => "/\b(?:password|passwd|db_pass|api_key|secret)\s*=\s*['\"][^'\"]{4,}['\"]/i",
        'message' => 'Ditemukan indikasi kredensial hardcoded.',
        'recommendation' => 'Simpan secret di environment variable atau secret manager.'
    ],

    [
        'name' => 'Debug Exposure',
        'severity' => 'MEDIUM',
        'pattern' => "/(?:display_errors\s*=\s*(?:1|on|true)|ini_set\s*\(\s*['\"]display_errors['\"]\s*,\s*['\"]?1)/i",
        'message' => 'Konfigurasi display_errors terindikasi aktif.',
        'recommendation' => 'Nonaktifkan tampilan error pada lingkungan produksi.'
    ]

];

// Menyiapkan iterator direktori.
$directory = new RecursiveDirectoryIterator(
    $targetDir,
    FilesystemIterator::SKIP_DOTS
);

// Filter file dan direktori.
$filter = new RecursiveCallbackFilterIterator(
    $directory,
    function ($current) use ($excludedDirs) {

        if ($current->isDir()) {
            return !in_array(
                $current->getFilename(),
                $excludedDirs,
                true
            );
        }

        return $current->isFile()
            && strtolower($current->getExtension()) === 'php';
    }
);

$iterator = new RecursiveIteratorIterator($filter);

$findings = [];
$totalFiles = 0;

// Mulai proses pemindaian.
echo PHP_EOL;
echo "====================================" . PHP_EOL;
echo "       PHP SECURITY SCANNER         " . PHP_EOL;
echo "====================================" . PHP_EOL;

foreach ($iterator as $file) {

    if ($totalFiles >= $maxFiles) {
        echo "Batas maksimal file tercapai." . PHP_EOL;
        break;
    }

    $totalFiles++;

    $filename = $file->getPathname();

    $lines = @file($filename);

    if ($lines === false) {
        continue;
    }

    foreach ($lines as $index => $line) {

        foreach ($rules as $rule) {

            if (preg_match($rule['pattern'], $line)) {

                $findings[] = [
                    'file' => str_replace(
                        $targetDir . DIRECTORY_SEPARATOR,
                        '',
                        $filename
                    ),
                    'line' => $index + 1,
                    'category' => $rule['name'],
                    'severity' => $rule['severity'],
                    'message' => $rule['message'],
                    'recommendation' => $rule['recommendation']
                ];

            }

        }

    }

}

// Menampilkan hasil.
echo "File diperiksa : " . $totalFiles . PHP_EOL;
echo "Temuan         : " . count($findings) . PHP_EOL;

echo "------------------------------------" . PHP_EOL;

if (empty($findings)) {

    echo "Tidak ditemukan pola mencurigakan." . PHP_EOL;

} else {

    foreach ($findings as $index => $finding) {

        echo PHP_EOL;
        echo "Temuan #" . ($index + 1) . PHP_EOL;

        echo "File     : "
            . $finding['file'] . PHP_EOL;

        echo "Baris    : "
            . $finding['line'] . PHP_EOL;

        echo "Kategori : "
            . $finding['category'] . PHP_EOL;

        echo "Risiko   : "
            . $finding['severity'] . PHP_EOL;

        echo "Masalah  : "
            . $finding['message'] . PHP_EOL;

        echo "Solusi   : "
            . $finding['recommendation'] . PHP_EOL;

        echo "------------------------------------" . PHP_EOL;

    }

}

echo PHP_EOL;
echo "Pemindaian selesai." . PHP_EOL;

?>

Selamat! Kita sudah memiliki mesin scanner sederhana yang bisa membaca file PHP, mencocokkan pola kode dengan aturan keamanan, dan menampilkan lokasi temuan.

5. Memahami Cara Kerja Scanner

Sebelum menjalankan aplikasi, ada baiknya kita memahami beberapa bagian penting dari kode tersebut.

A. Menentukan direktori target

$targetDir = __DIR__ . '/src';

Variabel tersebut menentukan lokasi source code yang akan diperiksa.

Dengan menggunakan __DIR__, PHP akan mengambil lokasi direktori tempat file scanner berada. Jadi, kita tidak perlu menuliskan alamat direktori komputer secara manual.

B. Membuat daftar aturan keamanan

Bagian $rules merupakan inti dari sistem deteksi.

Setiap aturan memiliki beberapa komponen:

  • name: Nama kategori pemeriksaan.
  • severity: Tingkat prioritas temuan.
  • pattern: Regular expression untuk mencari pola tertentu.
  • message: Penjelasan masalah yang ditemukan.
  • recommendation: Rekomendasi perbaikan.

Contohnya:

[
    'name' => 'Dangerous Function',
    'severity' => 'HIGH',
    'pattern' => '/\beval\s*\(/i',
    'message' => 'Ditemukan penggunaan eval().'
]

Artinya, scanner akan mencari pola pemanggilan fungsi eval() pada setiap baris file PHP yang diperiksa.

C. Membaca seluruh file PHP

$lines = @file($filename);

Fungsi file() digunakan untuk membaca isi file dan memisahkannya menjadi baris-baris teks.

Dengan pendekatan ini, scanner dapat mengetahui nomor baris tempat sebuah pola ditemukan.

D. Mencocokkan pola menggunakan preg_match()

if (preg_match($rule['pattern'], $line)) {
    // Simpan hasil temuan
}

Fungsi preg_match() digunakan untuk mencocokkan teks menggunakan regular expression.

Inilah alasan scanner dapat mencari berbagai pola kode tanpa harus menjalankan aplikasi yang sedang diperiksa.

6. Menambahkan Contoh Source Code yang Akan Diperiksa

Sekarang kita perlu mengisi direktori src dengan beberapa file PHP sebagai bahan pengujian.

Buat file bernama login.php dan masukkan kode berikut:

<?php

$password = "admin12345";

$username = $_GET['username'];

$query = "SELECT * FROM users
          WHERE username = '$username'";

echo $_GET['username'];

$result = mysqli_query($conn, $query);

eval($someCode);

?>

Kode tersebut sengaja dibuat dengan beberapa pola yang berpotensi bermasalah agar kita bisa melihat kemampuan scanner.

Selanjutnya, buat file config.php:

<?php

$db_host = "localhost";
$db_user = "root";
$db_pass = "password123";

ini_set('display_errors', '1');

?>

File tersebut juga memiliki beberapa pola yang akan diperiksa oleh scanner.

7. Menjalankan PHP Security Scanner

Setelah semua file tersedia, sekarang kita akan menjalankan proses pemindaian.

Buka Command Prompt atau terminal Visual Studio Code, kemudian masuk ke direktori proyek.

Contohnya jika proyek berada di:

C:\xampp\htdocs\php-security-scanner

Jalankan perintah berikut:

cd C:\xampp\htdocs\php-security-scanner

php scanner.php

Jika PHP sudah terdaftar dalam PATH Windows, perintah tersebut bisa langsung dijalankan.

Jika belum, gunakan lokasi executable PHP dari XAMPP:

C:\xampp\php\php.exe scanner.php

Scanner akan membaca seluruh file PHP di dalam direktori src dan menampilkan hasil pemeriksaan melalui terminal.

Contoh hasil pemindaian

====================================
       PHP SECURITY SCANNER
====================================

File diperiksa : 2
Temuan         : 6

------------------------------------

Temuan #1
File     : login.php
Baris    : 3
Kategori : Hardcoded Credentials
Risiko   : HIGH
Masalah  : Ditemukan indikasi kredensial hardcoded.
Solusi   : Simpan secret di environment variable.

------------------------------------

Temuan #2
File     : login.php
Baris    : 5
Kategori : Potential SQL Injection
Risiko   : HIGH
Masalah  : Input pengguna terindikasi digunakan
           langsung dalam query.
Solusi   : Gunakan prepared statement.

------------------------------------

Temuan #3
File     : login.php
Baris    : 8
Kategori : Potential XSS
Risiko   : MEDIUM
Masalah  : Input pengguna terindikasi ditampilkan
           langsung.
Solusi   : Periksa penggunaan output encoding.

------------------------------------

Pemindaian selesai.

Hasil di atas merupakan ilustrasi format laporan. Jumlah temuan sebenarnya bergantung pada isi file dan pola yang cocok dengan aturan scanner.

Sampai tahap ini, kita sudah memiliki sebuah tools sederhana untuk melakukan pemeriksaan source code PHP secara otomatis.

8. Mengembangkan Scanner Menjadi Lebih Akurat

Scanner versi pertama memang sudah bisa digunakan, tetapi masih memiliki keterbatasan.

Misalnya, penggunaan eval() belum tentu selalu berarti terdapat kerentanan yang dapat dieksploitasi. Begitu juga dengan echo $_GET, karena konteks penggunaannya perlu diperiksa lebih lanjut.

Scanner berbasis regular expression juga belum mampu mengikuti alur data dari sumber input sampai ke bagian kode yang menggunakannya.

Karena itu, ada beberapa pengembangan yang bisa kita lakukan.

A. Menggunakan PHP Tokenizer

PHP menyediakan fungsi token_get_all() yang dapat digunakan untuk mengubah source code PHP menjadi kumpulan token.

Dengan tokenizer, kita bisa melakukan analisis berdasarkan struktur bahasa PHP, bukan hanya mencocokkan teks menggunakan regular expression.

Contohnya:

<?php

$code = file_get_contents('src/login.php');

$tokens = token_get_all($code);

foreach ($tokens as $token) {

    if (!is_array($token)) {
        continue;
    }

    [$type, $value, $line] = $token;

    if ($type === T_EVAL) {

        echo "Ditemukan eval() pada baris "
            . $line . PHP_EOL;

    }

}

?>

Contoh ini menunjukkan bagaimana PHP dapat mengenali token T_EVAL secara langsung. Pendekatan tokenizer juga membantu mengurangi beberapa kesalahan pencocokan teks biasa.

B. Menambahkan Sistem Skor Risiko

Scanner bisa dikembangkan dengan sistem penilaian agar developer lebih mudah menentukan prioritas perbaikan.

Contoh pembobotan sederhana:

Severity Skor Prioritas
Critical 10 Sangat tinggi
High 7 Tinggi
Medium 4 Sedang
Low 2 Rendah
Info 1 Informasi

Skor tersebut hanya contoh pembobotan internal, bukan standar penilaian risiko resmi OWASP. Untuk penilaian yang lebih objektif, kamu dapat menggunakan pendekatan CVSS apabila temuan memang dapat dipetakan ke kerentanan yang relevan.

C. Menyimpan Hasil Scan ke JSON

Selain menampilkan hasil melalui terminal, scanner juga bisa dikembangkan agar menyimpan laporan dalam format JSON.

Contoh implementasinya:

<?php

$json = json_encode(
    $findings,
    JSON_PRETTY_PRINT |
    JSON_UNESCAPED_SLASHES
);

file_put_contents(
    __DIR__ . '/scan-report.json',
    $json
);

echo "Laporan berhasil disimpan.";

?>

Dengan format JSON, hasil pemindaian bisa diintegrasikan ke aplikasi dashboard, sistem monitoring, atau pipeline pengembangan perangkat lunak.

D. Membuat Dashboard Scanner

Jika ingin membuatnya lebih menarik, kita bisa mengembangkan scanner menjadi aplikasi berbasis web dengan tampilan dashboard.

Beberapa komponen yang bisa ditambahkan:

  • Dashboard jumlah file yang diperiksa.
  • Total temuan berdasarkan tingkat risiko.
  • Tabel hasil pemindaian.
  • Informasi nama file dan nomor baris.
  • Filter berdasarkan kategori kerentanan.
  • Fitur ekspor laporan PDF, CSV, atau JSON.
  • Riwayat hasil pemindaian sebelumnya.

Dengan demikian, PHP Security Scanner tidak hanya menjadi script pemeriksaan sederhana, tetapi dapat berkembang menjadi aplikasi internal untuk membantu tim developer melakukan evaluasi keamanan kode.

9. Apa Hubungan PHP Security Scanner dengan OWASP?

Setelah membuat scanner sederhana ini, mungkin muncul pertanyaan: apakah aplikasi yang kita buat sudah termasuk tools pengujian OWASP?

Jawabannya, aplikasi ini menggunakan pendekatan yang sejalan dengan konsep pemeriksaan keamanan source code yang dibahas dalam ekosistem OWASP, khususnya Static Application Security Testing.

Namun, perlu dibedakan bahwa scanner yang kita buat bukan produk resmi OWASP dan belum bisa dianggap sebagai tools SAST yang lengkap.

Hubungannya dapat dijelaskan sebagai berikut:

Referensi Hubungan dengan Scanner
OWASP Top 10 Menjadi referensi kategori risiko keamanan aplikasi.
OWASP ASVS Menjadi acuan persyaratan keamanan yang perlu dipenuhi aplikasi.
OWASP WSTG Menjadi panduan metodologi pengujian keamanan aplikasi.
OWASP SAST Menjadi pendekatan untuk menganalisis source code tanpa menjalankan aplikasi.

Untuk memahami lebih jauh mengenai konsep SAST, kamu juga bisa mempelajari dokumentasi OWASP Developer Guide dan OWASP Static Code Analysis.

10. Keterbatasan PHP Security Scanner Sederhana

Ada satu hal yang tidak boleh dilupakan. Scanner yang kita buat tidak bisa menjamin bahwa seluruh kode aplikasi sudah aman.

Berikut beberapa keterbatasannya:

  1. False Positive: Scanner melaporkan pola mencurigakan, padahal kode tersebut sebenarnya sudah memiliki perlindungan yang benar.
  2. False Negative: Scanner tidak menemukan pola tertentu meskipun terdapat kerentanan yang sebenarnya.
  3. Tidak memahami Business Logic: Scanner tidak mengetahui apakah alur bisnis aplikasi dapat dimanipulasi.
  4. Tidak menguji runtime: Scanner tidak menjalankan aplikasi sehingga tidak dapat menguji seluruh perilaku sistem saat digunakan.
  5. Tidak memeriksa seluruh konfigurasi server: Beberapa kelemahan berada di luar source code PHP.
  6. Tidak melakukan analisis dependensi secara mendalam: Kerentanan pada library pihak ketiga memerlukan pemeriksaan tersendiri.

Oleh sebab itu, scanner sederhana sebaiknya digunakan sebagai alat bantu pemeriksaan awal. Untuk mendapatkan hasil yang lebih baik, kombinasikan dengan pengujian manual, pemeriksaan dependensi, dan pengujian dinamis menggunakan tools seperti OWASP ZAP.

11. Kesimpulan: Membuat Tools Keamanan Tidak Harus Dimulai dari Sesuatu yang Rumit

Dari tutorial ini, kita sudah belajar bahwa PHP Native dapat digunakan untuk membangun sebuah aplikasi scanner keamanan sederhana. Dengan memanfaatkan fungsi seperti RecursiveDirectoryIterator(), preg_match(), dan file(), kita bisa membuat sistem yang mampu membaca banyak file PHP dan menemukan pola kode yang perlu diperiksa lebih lanjut.

Walaupun belum memiliki kemampuan analisis seperti scanner profesional, aplikasi ini sudah memberikan gambaran mengenai bagaimana konsep Static Application Security Testing bekerja.

Menariknya, proyek ini juga masih bisa dikembangkan menjadi aplikasi yang lebih serius. Kita bisa menambahkan dashboard, sistem laporan, analisis menggunakan PHP Tokenizer, pemeriksaan dependensi, hingga integrasi dengan tools keamanan lainnya.

Bagi developer yang terbiasa menggunakan PHP Native dan MySQL, proyek seperti ini bisa menjadi latihan yang menarik untuk memahami keamanan aplikasi dari sudut pandang yang berbeda.

Membangun aplikasi adalah tentang menciptakan fungsi. Menguji keamanan adalah tentang memastikan fungsi tersebut tidak berubah menjadi celah yang merugikan penggunanya.

Referensi dan Dokumentasi Tambahan

Catatan: PHP Security Scanner dalam tutorial ini merupakan proyek edukasi dan contoh implementasi awal. Jangan menganggap hasil pemindaian sebagai sertifikasi keamanan atau bukti bahwa sebuah aplikasi bebas dari kerentanan.

    Keyword utama:
  1. PHP Security Scanner
  2. Scanner keamanan PHP
  3. Tutorial PHP Native
  4. PHP Static Code Analysis
  5. OWASP SAST
  6. Aplikasi scanner keamanan

Diskusi & Komentar