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: <script>alert('xss')</script>
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.