Nxdom Framework

Nusantara eXtreme Development Object Model — dokumentasi resmi

Keamanan form

Fitur dan praktik untuk mengamankan form dari CSRF, XSS, SQL injection, dan ancaman umum lainnya.

NexaForm · CSRF XSS

Introduction

Form security is a critical aspect of web application development. NexaUI provides several built-in features and best practices to help secure your forms against common vulnerabilities such as Cross-Site Request Forgery (CSRF), Cross-Site Scripting (XSS), SQL Injection, and more.

CSRF Protection dengan NexaSession

Cross-Site Request Forgery (CSRF) adalah serangan yang memaksa user yang sudah login untuk mengirim request yang tidak diinginkan. Nexa menyediakan CSRF protection melalui NexaSession:

Generate dan Validasi CSRF Token

// Di controller, generate CSRF token public function showForm(): void { // Generate token (otomatis disimpan di session) $csrfToken = $this->session->getCsrfToken(); // Assign ke template $this->assignVar('csrf_token', $csrfToken); $this->render('form'); } // Di template HTML <form method="post" action="/submit"> <!-- Hidden field untuk CSRF token --> <input type="hidden" name="csrf_token" value="{{csrf_token}}"> <!-- Form fields --> <input type="text" name="username"> <input type="password" name="password"> <button type="submit">Submit</button> </form>

Validasi CSRF Token di Controller

public function processForm(): void { // Get token dari POST $token = $_POST['csrf_token'] ?? ''; // Validasi token if (!$this->session->validateCsrfToken($token)) { // CSRF token invalid $this->setFlash('error', 'Invalid form submission'); $this->redirect('/form'); return; } // Process form data // ... }

CSRF Protection untuk AJAX dengan NexaAjax

use App\System\Helpers\NexaAjax; // Generate token untuk AJAX public function getToken(): void { $token = NexaAjax::getCsrfToken(); NexaAjax::success('Token generated', ['token' => $token]); } // Validasi token di AJAX endpoint public function apiEndpoint(): void { $token = NexaAjax::input('csrf_token'); if (!NexaAjax::validateCsrfToken($token)) { NexaAjax::forbidden('Invalid CSRF token'); return; } // Process request // ... }
// Di frontend, include CSRF token const csrfToken = document.querySelector('[name="csrf_token"]').value; // Kirim dengan fetch fetch('/api/endpoint', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, body: JSON.stringify({ csrf_token: csrfToken, // ... data lainnya }) });

Sanitasi Input Otomatis

NexaForm otomatis melakukan sanitasi input untuk mencegah XSS dan injection attacks:

// NexaForm otomatis sanitize semua POST data $form = $this->createForm(); $form->fields([ 'name' => 'required|min:3', 'email' => 'required|email', 'comment' => 'required|min:10' ]); $result = $form->process(); // Data di $result['data'] sudah di-sanitize dengan htmlspecialchars() // Input: <script>alert('xss')</script> // Output: &lt;script&gt;alert('xss')&lt;/script&gt; if ($result['success']) { $safeData = $result['data']; // ← Sudah aman dari XSS // Simpan ke database $this->useModels('Comment', 'save', $safeData); }

Sanitasi dilakukan oleh trait NexaValidation melalui method sanitizePostData():

  • Trim whitespace di awal dan akhir
  • Convert special characters ke HTML entities dengan htmlspecialchars()
  • Recursive untuk array nested

XSS Prevention

Cross-Site Scripting (XSS) attacks occur when untrusted data is included in a web page. NexaUI provides methods to prevent XSS:

Output Escaping

<!-- Unsafe (vulnerable to XSS) --> <div><?= $userInput ?></div> <!-- Safe (escaped) --> <div><?= $this->escape($userInput) ?></div>

HTML Purification

For cases where you need to allow some HTML (like in rich text editors), use HTML purification:

// In your controller public function saveArticle(): void { $content = $this->getPostData('content'); // Purify HTML content to remove malicious code $purifiedContent = $this->purifyHtml($content); // Save purified content $this->articleRepository->save([ 'title' => $this->getPostData('title'), 'content' => $purifiedContent ]); } // HTML purification method private function purifyHtml(string $html): string { // NexaUI uses HTMLPurifier internally return $this->htmlPurifier->purify($html); }

Input Validation

Proper input validation is essential for security. NexaUI provides a comprehensive validation system:

public function register(): void { $input = $this->getPostData(); // Create validator with rules $validator = new NexaValidator(); // Add validation rules $validator->rule('required', ['username', 'email', 'password']); $validator->rule('email', 'email'); $validator->rule('lengthMin', 'password', 8); $validator->rule('regex', 'username', '/^[a-zA-Z0-9_]+$/'); // Alphanumeric + underscore only // Check if validation passes if (!$validator->validate($input)) { $this->validationErrorResponse($validator->errors()); return; } // Process valid data // ... }

Type-Specific Validation

// Integer validation $id = filter_var($this->getParam('id'), FILTER_VALIDATE_INT); if ($id === false) { $this->errorResponse('Invalid ID'); return; } // Email validation $email = filter_var($this->getPostData('email'), FILTER_VALIDATE_EMAIL); if ($email === false) { $this->errorResponse('Invalid email address'); return; } // URL validation $url = filter_var($this->getPostData('website'), FILTER_VALIDATE_URL); if ($url === false) { $this->errorResponse('Invalid URL'); return; }

SQL Injection Prevention

SQL Injection attacks occur when untrusted data is used to construct SQL queries. NexaUI uses prepared statements to prevent SQL injection:

// Unsafe (vulnerable to SQL injection) $username = $_POST['username']; $query = "SELECT * FROM users WHERE username = '$username'"; // Safe (using prepared statements in NexaUI) public function findUser(): void { $username = $this->getPostData('username'); // Using the repository pattern with prepared statements $user = $this->userRepository->findByUsername($username); // ... } // In the repository class public function findByUsername(string $username) { // NexaUI uses PDO with prepared statements internally $stmt = $this->db->prepare("SELECT * FROM users WHERE username = ?"); $stmt->execute([$username]); return $stmt->fetch(); }

Rate Limiting

Rate limiting helps prevent brute force attacks and form spam. NexaUI provides rate limiting capabilities:

public function login(): void { $ip = $this->getClientIp(); $email = $this->getPostData('email'); // Check rate limiting if ($this->isRateLimited('login', $ip)) { $this->errorResponse('Too many login attempts. Please try again later.', 429); return; } // Increment attempt counter $this->incrementRateLimit('login', $ip); // Process login $success = $this->authenticateUser($email, $this->getPostData('password')); if ($success) { // Reset rate limit on successful login $this->resetRateLimit('login', $ip); $this->successResponse(null, 'Login successful'); } else { $this->errorResponse('Invalid email or password'); } } // Rate limiting methods private function isRateLimited(string $action, string $identifier, int $maxAttempts = 5, int $timeWindow = 300): bool { $key = "rate_limit:{$action}:{$identifier}"; $attempts = $_SESSION[$key]['attempts'] ?? 0; $timestamp = $_SESSION[$key]['timestamp'] ?? 0; // Reset if time window has passed if (time() - $timestamp > $timeWindow) { $this->resetRateLimit($action, $identifier); return false; } return $attempts >= $maxAttempts; } private function incrementRateLimit(string $action, string $identifier): void { $key = "rate_limit:{$action}:{$identifier}"; if (!isset($_SESSION[$key])) { $_SESSION[$key] = [ 'attempts' => 0, 'timestamp' => time() ]; } $_SESSION[$key]['attempts']++; } private function resetRateLimit(string $action, string $identifier): void { $key = "rate_limit:{$action}:{$identifier}"; unset($_SESSION[$key]); }

File Upload Security

File uploads present unique security challenges. NexaUI provides secure file upload handling:

public function uploadFile(): void { // Configure secure file upload settings $uploadConfig = [ 'allowed_types' => ['image/jpeg', 'image/png', 'application/pdf'], 'max_size' => 2 * 1024 * 1024, // 2MB 'upload_dir' => $this->getUploadPath(), 'rename' => true // Generate secure random filenames ]; // Process the upload $result = $this->processFileUpload('file', $uploadConfig); if ($result['success']) { $this->successResponse($result['data'], 'File uploaded successfully'); } else { $this->errorResponse($result['message']); } } private function processFileUpload(string $fileKey, array $config): array { // Check if file exists if (!isset($_FILES[$fileKey]) || $_FILES[$fileKey]['error'] !== UPLOAD_ERR_OK) { return [ 'success' => false, 'message' => 'No file uploaded or upload error' ]; } $file = $_FILES[$fileKey]; // Validate file type if (!in_array($file['type'], $config['allowed_types'])) { return [ 'success' => false, 'message' => 'File type not allowed' ]; } // Validate file size if ($file['size'] > $config['max_size']) { return [ 'success' => false, 'message' => 'File size exceeds limit' ]; } // Generate secure filename if requested $filename = $file['name']; if ($config['rename']) { $extension = pathinfo($filename, PATHINFO_EXTENSION); $filename = bin2hex(random_bytes(16)) . '.' . $extension; } // Ensure upload directory exists and is writable if (!is_dir($config['upload_dir']) || !is_writable($config['upload_dir'])) { return [ 'success' => false, 'message' => 'Upload directory not writable' ]; } $destination = $config['upload_dir'] . '/' . $filename; // Move uploaded file if (move_uploaded_file($file['tmp_name'], $destination)) { return [ 'success' => true, 'data' => [ 'filename' => $filename, 'path' => $destination, 'type' => $file['type'], 'size' => $file['size'] ] ]; } else { return [ 'success' => false, 'message' => 'Failed to move uploaded file' ]; } }

Session Security

Secure session management is important for maintaining user state. NexaUI implements several session security measures:

// In your application bootstrap or initialization public function initializeSecureSessions(): void { // Set secure session parameters ini_set('session.cookie_httponly', 1); // Prevent JavaScript access to session cookie ini_set('session.use_only_cookies', 1); // Force use of cookies for session // Use secure cookies in production if ($this->isProduction()) { ini_set('session.cookie_secure', 1); // Only send cookie over HTTPS } // Set SameSite attribute for cookies session_set_cookie_params([ 'samesite' => 'Lax' ]); // Start session session_start(); // Regenerate session ID periodically to prevent session fixation if (!isset($_SESSION['last_regeneration']) || time() - $_SESSION['last_regeneration'] > 1800) { // 30 minutes session_regenerate_id(true); $_SESSION['last_regeneration'] = time(); } }

Content Security Policy

Content Security Policy (CSP) helps prevent XSS and other code injection attacks. NexaUI supports setting CSP headers:

// In your controller or middleware public function setSecurityHeaders(): void { // Content Security Policy header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://trusted-cdn.com; style-src 'self' 'unsafe-inline' https://trusted-cdn.com; img-src 'self' data: https://trusted-cdn.com; connect-src 'self' https://api.example.com"); // Other security headers header("X-Content-Type-Options: nosniff"); header("X-Frame-Options: SAMEORIGIN"); header("X-XSS-Protection: 1; mode=block"); header("Referrer-Policy: strict-origin-when-cross-origin"); // HSTS (HTTP Strict Transport Security) - only in production if ($this->isProduction()) { header("Strict-Transport-Security: max-age=31536000; includeSubDomains; preload"); } }

Security Best Practices

  • Always validate and sanitize all user input, both on client and server sides
  • Use CSRF protection for all forms that change state
  • Escape all output to prevent XSS attacks
  • Use prepared statements for database queries
  • Implement proper file upload validation
  • Use HTTPS for all form submissions
  • Implement rate limiting for sensitive operations
  • Use secure session management
  • Set appropriate security headers
  • Keep all dependencies updated to patch security vulnerabilities
  • Follow the principle of least privilege for user permissions
  • Implement proper error handling without leaking sensitive information

Security Checklist

Use this checklist to ensure your forms are secure:

Security Measure Implementation
CSRF Protection Add $this->csrfField() to all forms
Input Validation Use NexaValidator with appropriate rules
Output Escaping Use $this->escape() for all user-generated content
SQL Injection Prevention Use prepared statements via repository methods
File Upload Security Validate file types, sizes, and use secure file names
Rate Limiting Implement rate limiting for sensitive operations
Session Security Use secure session settings and regenerate IDs
HTTPS Force HTTPS for all form submissions
Security Headers Set appropriate security headers including CSP
Error Handling Implement proper error handling without leaking information

Lanjut

Sebelumnya: Upload berkas. Berikutnya: Respons & error. Indeks Forms · Dokumentasi · Beranda.