# Panduan Keamanan Website Sekolah

## Analisis Keamanan Aplikasi

### ⚠️ Potensi Kerentanan Saat Ini

#### 1. **Database SQLite - CRITICAL**
**Risiko**: Database file bisa didownload jika tidak diproteksi
**Status**: ✅ Sudah diproteksi via `.htaccess`
**Rekomendasi Tambahan**:
- Pindahkan folder `data/` ke luar `public_html/` (outside web root)
- Set permission 600 untuk `school.db`

#### 2. **Session Management - MEDIUM**
**Risiko**: Session hijacking, fixation
**Status**: ⚠️ Perlu perbaikan
**Solusi**: Implementasi session security

#### 3. **SQL Injection - LOW**
**Risiko**: Manipulasi database via input
**Status**: ✅ Menggunakan PDO prepared statements
**Catatan**: Sudah aman, tapi perlu validasi input tambahan

#### 4. **XSS (Cross-Site Scripting) - MEDIUM**
**Risiko**: Injeksi JavaScript berbahaya
**Status**: ⚠️ Perlu sanitasi output
**Solusi**: Escape semua output dengan `htmlspecialchars()`

#### 5. **File Upload - HIGH**
**Risiko**: Upload file berbahaya (PHP shell)
**Status**: ⚠️ Perlu validasi ketat
**Solusi**: Validasi MIME type, rename file, scan malware

#### 6. **Authentication - MEDIUM**
**Risiko**: Brute force attack, weak password
**Status**: ⚠️ Perlu rate limiting
**Solusi**: Implementasi login throttling

#### 7. **CSRF (Cross-Site Request Forgery) - MEDIUM**
**Risiko**: Aksi tidak sah atas nama user
**Status**: ❌ Tidak ada proteksi
**Solusi**: Implementasi CSRF token

#### 8. **Directory Traversal - LOW**
**Risiko**: Akses file di luar folder yang diizinkan
**Status**: ✅ Sudah diproteksi via `.htaccess`

---

## 🛡️ Implementasi Keamanan

### 1. Session Security

Buat file `includes/session.php`:

```php
<?php
// includes/session.php - Secure Session Management

// Prevent session fixation
if (session_status() === PHP_SESSION_NONE) {
    // Secure session configuration
    ini_set('session.cookie_httponly', 1);
    ini_set('session.cookie_secure', 1); // HTTPS only
    ini_set('session.cookie_samesite', 'Strict');
    ini_set('session.use_strict_mode', 1);
    ini_set('session.use_only_cookies', 1);
    
    session_start();
    
    // Regenerate session ID periodically
    if (!isset($_SESSION['created'])) {
        $_SESSION['created'] = time();
    } else if (time() - $_SESSION['created'] > 1800) {
        session_regenerate_id(true);
        $_SESSION['created'] = time();
    }
    
    // Validate session
    if (isset($_SESSION['user_id'])) {
        // Check user agent (basic fingerprinting)
        $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
        if (!isset($_SESSION['user_agent'])) {
            $_SESSION['user_agent'] = $user_agent;
        } else if ($_SESSION['user_agent'] !== $user_agent) {
            // Session hijacking detected
            session_destroy();
            header('Location: /admin/login.php?error=session_invalid');
            exit;
        }
    }
}
?>
```

### 2. CSRF Protection

Buat file `includes/csrf.php`:

```php
<?php
// includes/csrf.php - CSRF Token Management

function csrf_token() {
    if (!isset($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function csrf_field() {
    return '<input type="hidden" name="csrf_token" value="' . csrf_token() . '">';
}

function csrf_verify() {
    if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token'])) {
        return false;
    }
    return hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']);
}

function csrf_protect() {
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && !csrf_verify()) {
        die('CSRF token validation failed');
    }
}
?>
```

### 3. Input Validation & Sanitization

Buat file `includes/security.php`:

```php
<?php
// includes/security.php - Security Functions

// XSS Protection
function e($string) {
    return htmlspecialchars($string ?? '', ENT_QUOTES, 'UTF-8');
}

// Validate email
function validate_email($email) {
    return filter_var($email, FILTER_VALIDATE_EMAIL);
}

// Sanitize filename
function sanitize_filename($filename) {
    $filename = preg_replace('/[^a-zA-Z0-9._-]/', '', $filename);
    return substr($filename, 0, 255);
}

// Validate image upload
function validate_image_upload($file) {
    $allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    $max_size = 5 * 1024 * 1024; // 5MB
    
    if ($file['error'] !== UPLOAD_ERR_OK) {
        return ['success' => false, 'error' => 'Upload error'];
    }
    
    if ($file['size'] > $max_size) {
        return ['success' => false, 'error' => 'File too large (max 5MB)'];
    }
    
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime = finfo_file($finfo, $file['tmp_name']);
    finfo_close($finfo);
    
    if (!in_array($mime, $allowed_types)) {
        return ['success' => false, 'error' => 'Invalid file type'];
    }
    
    // Check if it's really an image
    if (!getimagesize($file['tmp_name'])) {
        return ['success' => false, 'error' => 'Not a valid image'];
    }
    
    return ['success' => true];
}

// Rate limiting for login
function check_rate_limit($identifier, $max_attempts = 5, $window = 900) {
    $cache_file = __DIR__ . '/../data/rate_limit_' . md5($identifier) . '.txt';
    
    if (file_exists($cache_file)) {
        $data = json_decode(file_get_contents($cache_file), true);
        if (time() - $data['time'] < $window) {
            if ($data['attempts'] >= $max_attempts) {
                return false;
            }
            $data['attempts']++;
        } else {
            $data = ['time' => time(), 'attempts' => 1];
        }
    } else {
        $data = ['time' => time(), 'attempts' => 1];
    }
    
    file_put_contents($cache_file, json_encode($data));
    return true;
}

// Password strength validation
function validate_password_strength($password) {
    if (strlen($password) < 8) {
        return ['valid' => false, 'error' => 'Password minimal 8 karakter'];
    }
    if (!preg_match('/[A-Z]/', $password)) {
        return ['valid' => false, 'error' => 'Password harus mengandung huruf besar'];
    }
    if (!preg_match('/[a-z]/', $password)) {
        return ['valid' => false, 'error' => 'Password harus mengandung huruf kecil'];
    }
    if (!preg_match('/[0-9]/', $password)) {
        return ['valid' => false, 'error' => 'Password harus mengandung angka'];
    }
    return ['valid' => true];
}
?>
```

### 4. Secure File Upload

Update fungsi upload di `admin/slides.php`:

```php
// Secure file upload
if (!empty($_FILES['image']['name'])) {
    // Validate upload
    $validation = validate_image_upload($_FILES['image']);
    if (!$validation['success']) {
        flash_set('error', $validation['error']);
        header('Location: slides.php');
        exit;
    }
    
    $tmp = $_FILES['image']['tmp_name'];
    $ext = strtolower(pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION));
    
    // Generate random filename
    $filename = bin2hex(random_bytes(16)) . '.' . $ext;
    $dest = $uploadsDir . '/' . $filename;
    
    // Move and resize
    if (move_uploaded_file($tmp, $dest)) {
        resize_image_gd($dest, $dest, 1400, 900);
        // Set secure permissions
        chmod($dest, 0644);
    }
}
```

---

## 🔒 Checklist Keamanan Hosting

### Pre-Deployment

- [ ] **Ganti semua password default**
  - Admin user
  - Database (jika migrasi ke MySQL)
  - Backup secret key

- [ ] **Update konfigurasi**
  - Set `base_url()` ke domain production
  - Disable error display: `ini_set('display_errors', 0)`
  - Enable error logging: `ini_set('log_errors', 1)`

- [ ] **File permissions**
  ```bash
  # Folders
  chmod 755 admin/ public/ includes/
  chmod 755 data/
  chmod 755 data/uploads/
  
  # Files
  chmod 644 *.php
  chmod 644 data/school.db
  chmod 600 includes/db.php  # Config files
  ```

- [ ] **Proteksi file sensitif**
  - `.htaccess` di root
  - `.htaccess` di `data/`
  - Hapus file development (test.php, debug.php, dll)

### Post-Deployment

- [ ] **SSL Certificate**
  - Install SSL (Let's Encrypt gratis)
  - Force HTTPS redirect
  - Update session cookie secure flag

- [ ] **Security Headers**
  ```apache
  # Tambahkan di .htaccess
  Header set X-Content-Type-Options "nosniff"
  Header set X-Frame-Options "SAMEORIGIN"
  Header set X-XSS-Protection "1; mode=block"
  Header set Referrer-Policy "strict-origin-when-cross-origin"
  Header set Permissions-Policy "geolocation=(), microphone=(), camera=()"
  ```

- [ ] **Monitoring**
  - Setup error logging
  - Monitor failed login attempts
  - Check file integrity berkala

- [ ] **Backup**
  - Setup automated backup (cron job)
  - Test restore procedure
  - Store backup di multiple lokasi

### Maintenance Rutin

- [ ] **Mingguan**
  - Check error logs
  - Review failed login attempts
  - Verify backup berjalan

- [ ] **Bulanan**
  - Update PHP version (jika ada)
  - Review user accounts
  - Test restore backup
  - Scan malware

- [ ] **Per 3 Bulan**
  - Audit security
  - Update dependencies
  - Review access logs
  - Change admin password

---

## 🚨 Incident Response

### Jika Terjadi Hack/Breach:

1. **Immediate Action**
   - Matikan website (maintenance mode)
   - Backup semua file dan database
   - Disconnect dari internet jika perlu

2. **Investigation**
   - Check access logs
   - Identify entry point
   - List affected files

3. **Recovery**
   - Restore dari backup bersih
   - Patch vulnerability
   - Change semua password
   - Regenerate session keys

4. **Prevention**
   - Update security measures
   - Implement additional monitoring
   - Document incident

---

## 📊 Security Score

### Current Status: ⚠️ MEDIUM (60/100)

**Sudah Aman**:
- ✅ PDO Prepared Statements (SQL Injection)
- ✅ `.htaccess` proteksi database
- ✅ Directory listing disabled
- ✅ Session-based authentication

**Perlu Perbaikan**:
- ⚠️ CSRF protection
- ⚠️ Rate limiting
- ⚠️ XSS sanitization
- ⚠️ File upload validation
- ⚠️ Session security hardening

**Rekomendasi Priority**:
1. Implementasi CSRF token (HIGH)
2. Secure file upload validation (HIGH)
3. Rate limiting login (MEDIUM)
4. Session security (MEDIUM)
5. XSS output escaping (MEDIUM)

---

## 📚 Resources

- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [PHP Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/PHP_Configuration_Cheat_Sheet.html)
- [SQLite Security](https://www.sqlite.org/security.html)

---

**Last Updated**: <?php echo date('Y-m-d'); ?>  
**Version**: 1.0
