Di era digital di mana cyber threats semakin sophisticated, web application security menjadi prioritas utama untuk developer dan bisnis. Data breaches dapat menyebabkan kerugian finansial yang signifikan, kehilangan kepercayaan customer, dan konsekuensi legal yang serius. Artikel ini akan memberikan comprehensive guide tentang web application security best practices di tahun 2025.
Understanding Modern Cybersecurity Landscape
1. Current Threat Landscape di 2025
• Ransomware Evolution: More sophisticated encryption methods dan targeted attacks
• AI-Powered Attacks: Machine learning untuk automated vulnerability scanning
• Supply Chain Attacks: Compromise melalui third-party dependencies
• API Security Threats: Increased API attack vectors dan data exposure
• Cloud Security Challenges: Multi-cloud environment complexities
2. Indonesia Cybersecurity Context
• Regulatory Compliance: PDPA (Personal Data Protection Act) implementation
• Financial Sector: BI (Bank Indonesia) security regulations
• Government Sector: SINAP dan national cybersecurity frameworks
• Startup Ecosystem: Growing security awareness dalam tech community
3. Attack Vectors Evolution
• Traditional Web Vulnerabilities: SQL injection, XSS, CSRF masih relevan
• Modern Attack Patterns: API abuse, business logic flaws, zero-day exploits
• Social Engineering: Phishing, vishing, dan social media attacks
• Infrastructure Attacks: DDoS, DNS attacks, network intrusion
OWASP Top 10 2021 Updated Implementation
1. A01: Broken Access Control
“`javascript
// Broken Access Control Examples dan Solutions
// VULNERABLE: No authorization check
app.get(‘/api/users/:id’, async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user); // Any user can access any user data!
});
// SECURE: Proper authorization check
app.get(‘/api/users/:id’, async (req, res) => {
// Check if user has permission to access resource
if (req.user.id !== req.params.id && !req.user.isAdmin) {
return res.status(403).json({ error: ‘Access denied’ });
}
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json({ error: ‘User not found’ });
}
res.json(user);
});
// Role-based access control (RBAC) implementation
class AuthorizationMiddleware {
constructor() {
this.roles = {
admin: [‘read’, ‘write’, ‘delete’, ‘manage_users’],
editor: [‘read’, ‘write’],
viewer: [‘read’]
};
}
requirePermission(permission) {
return (req, res, next) => {
const userRole = req.user.role;
const userPermissions = this.roles[userRole] || [];
if (!userPermissions.includes(permission)) {
return res.status(403).json({
error: ‘Insufficient permissions’,
required: permission,
current: userRole
});
}
next();
};
}
requireOwnership(resourceType) {
return async (req, res, next) => {
try {
const resourceId = req.params.id;
const userId = req.user.id;
let resource;
switch (resourceType) {
case ‘post’:
resource = await Post.findById(resourceId);
break;
case ‘comment’:
resource = await Comment.findById(resourceId);
break;
default:
return res.status(400).json({ error: ‘Invalid resource type’ });
}
if (!resource) {
return res.status(404).json({ error: ‘Resource not found’ });
}
// Admin can access all resources
if (req.user.role === ‘admin’) {
req.resource = resource;
return next();
}
// Check ownership
if (resource.author.toString() !== userId) {
return res.status(403).json({ error: ‘Access denied’ });
}
req.resource = resource;
next();
} catch (error) {
res.status(500).json({ error: ‘Authorization check failed’ });
}
};
}
}
// Usage
const authz = new AuthorizationMiddleware();
app.get(‘/api/posts/:id’, authenticateToken, authz.requireOwnership(‘post’), getPost);
app.delete(‘/api/posts/:id’, authenticateToken, authz.requirePermission(‘delete’), authz.requireOwnership(‘post’), deletePost);
“`
2. A02: Cryptographic Failures
“`javascript
// Secure Cryptographic Implementation
import crypto from ‘crypto’;
import bcrypt from ‘bcrypt’;
import jsonwebtoken from ‘jsonwebtoken’;
class SecurityService {
constructor() {
this.algorithm = ‘aes-256-gcm’;
this.keyLength = 32;
this.ivLength = 16;
this.tagLength = 16;
this.saltRounds = 12;
}
// Generate secure random key
generateKey() {
return crypto.randomBytes(this.keyLength);
}
// Encrypt sensitive data
encrypt(text, key) {
const iv = crypto.randomBytes(this.ivLength);
const cipher = crypto.createCipher(this.algorithm, key, iv);
let encrypted = cipher.update(text, ‘utf8’, ‘hex’);
encrypted += cipher.final(‘hex’);
const tag = cipher.getAuthTag();
return {
iv: iv.toString(‘hex’),
encryptedData: encrypted,
tag: tag.toString(‘hex’)
};
}
// Decrypt sensitive data
decrypt(encryptedData, key, iv, tag) {
const decipher = crypto.createDecipher(this.algorithm, key, Buffer.from(iv, ‘hex’));
decipher.setAuthTag(Buffer.from(tag, ‘hex’));
let decrypted = decipher.update(encryptedData, ‘hex’, ‘utf8’);
decrypted += decipher.final(‘utf8′);
return decrypted;
}
// Secure password hashing
async hashPassword(password) {
const salt = await bcrypt.genSalt(this.saltRounds);
return bcrypt.hash(password, salt);
}
// Verify password
async verifyPassword(password, hash) {
return bcrypt.compare(password, hash);
}
// Generate JWT token with security best practices
generateToken(payload, expiresIn = ’15m’) {
const jwtPayload = {
sub: payload.userId,
email: payload.email,
role: payload.role,
iat: Math.floor(Date.now() / 1000),
// Add custom claims
permissions: payload.permissions || []
};
const options = {
expiresIn,
issuer: process.env.JWT_ISSUER,
audience: process.env.JWT_AUDIENCE,
algorithm: ‘HS256’
};
return jsonwebtoken.sign(jwtPayload, process.env.JWT_SECRET, options);
}
// Verify JWT token
verifyToken(token) {
try {
const decoded = jsonwebtoken.verify(token, process.env.JWT_SECRET, {
issuer: process.env.JWT_ISSUER,
audience: process.env.JWT_AUDIENCE,
algorithms: [‘HS256’]
});
// Check token expiration buffer (5 minutes)
const now = Math.floor(Date.now() / 1000);
if (decoded.exp – now < 300) {
// Token will expire soon, consider refresh
return { valid: true, decoded, needsRefresh: true };
}
return { valid: true, decoded };
} catch (error) {
return { valid: false, error: error.message };
}
}
// Generate secure random session ID
generateSessionId() {
return crypto.randomBytes(32).toString('hex');
}
// Generate API key
generateApiKey() {
const prefix = 'ak_';
const key = crypto.randomBytes(32).toString('hex');
return prefix + key;
}
}
// Secure password policy implementation
class PasswordPolicy {
constructor() {
this.minLength = 12;
this.requireUppercase = true;
this.requireLowercase = true;
this.requireNumbers = true;
this.requireSpecialChars = true;
this.maxConsecutiveChars = 2;
this.forbiddenPatterns = ['password', '123456', 'qwerty'];
}
validate(password) {
const errors = [];
// Length check
if (password.length < this.minLength) {
errors.push(`Password must be at least ${this.minLength} characters long`);
}
// Character requirements
if (this.requireUppercase && !/[A-Z]/.test(password)) {
errors.push('Password must contain at least one uppercase letter');
}
if (this.requireLowercase && !/[a-z]/.test(password)) {
errors.push('Password must contain at least one lowercase letter');
}
if (this.requireNumbers && !/\d/.test(password)) {
errors.push('Password must contain at least one number');
}
if (this.requireSpecialChars && !/[!@#$%^&*(),.?":{}|]/.test(password)) {
errors.push(‘Password must contain at least one special character’);
}
// Consecutive characters check
if (this.hasConsecutiveChars(password)) {
errors.push(‘Password cannot contain consecutive characters’);
}
// Forbidden patterns check
const lowerPassword = password.toLowerCase();
for (const pattern of this.forbiddenPatterns) {
if (lowerPassword.includes(pattern)) {
errors.push(`Password cannot contain common patterns like “${pattern}”`);
}
}
return {
isValid: errors.length === 0,
errors
};
}
hasConsecutiveChars(password) {
for (let i = 0; i < password.length – this.maxConsecutiveChars; i++) {
let isConsecutive = true;
for (let j = 1; j <= this.maxConsecutiveChars; j++) {
if (password.charCodeAt(i + j) !== password.charCodeAt(i) + j) {
isConsecutive = false;
break;
}
}
if (isConsecutive) return true;
}
return false;
}
}
“`
3. A03: Injection
“`javascript
// SQL Injection Prevention
import { Pool } from 'pg';
import mongoose from 'mongoose';
// Secure Database Query Implementation
class DatabaseService {
constructor() {
this.pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === 'production'
});
}
// VULNERABLE: SQL Injection prone
async getUserUnsafe(email) {
const query = `SELECT * FROM users WHERE email = '${email}'`;
const result = await this.pool.query(query);
return result.rows[0];
}
// SECURE: Parameterized queries
async getUser(email) {
const query = 'SELECT * FROM users WHERE email = $1';
const result = await this.pool.query(query, [email]);
return result.rows[0];
}
// Secure ORM usage dengan Mongoose
async findUserByEmail(email) {
try {
const user = await User.findOne({ email: email.trim().toLowerCase() });
return user;
} catch (error) {
throw new Error('Database query failed');
}
}
// Advanced query dengan input validation
async searchUsers(filters, pagination) {
// Validate inputs
const validatedFilters = this.validateFilters(filters);
const validatedPagination = this.validatePagination(pagination);
// Build query dynamically but safely
const query = {};
const options = {
limit: validatedPagination.limit,
skip: validatedPagination.offset,
sort: validatedPagination.sort
};
if (validatedFilters.name) {
query.name = { $regex: validatedFilters.name, $options: 'i' };
}
if (validatedFilters.role) {
query.role = validatedFilters.role;
}
if (validatedFilters.minAge) {
query.age = { $gte: parseInt(validatedFilters.minAge) };
}
const users = await User.find(query, null, options);
return users;
}
validateFilters(filters) {
const validated = {};
const allowedFields = ['name', 'role', 'minAge', 'maxAge'];
for (const [key, value] of Object.entries(filters)) {
if (allowedFields.includes(key)) {
// Sanitize input
if (typeof value === 'string') {
validated[key] = value.trim().replace(/[]/g, ”);
} else if (typeof value === ‘number’) {
validated[key] = Math.max(0, value); // Ensure positive numbers
}
}
}
return validated;
}
validatePagination(pagination) {
const limit = Math.min(Math.max(1, parseInt(pagination.limit) || 10), 100);
const page = Math.max(1, parseInt(pagination.page) || 1);
const offset = (page – 1) * limit;
const allowedSortFields = [‘name’, ‘createdAt’, ‘updatedAt’];
const sortField = allowedSortFields.includes(pagination.sortBy)
? pagination.sortBy
: ‘createdAt’;
const sortOrder = pagination.sortOrder === ‘desc’ ? -1 : 1;
return {
limit,
offset,
sort: { [sortField]: sortOrder }
};
}
}
// NoSQL Injection Prevention
class NoSQLSecurity {
// VULNERABLE: Direct query injection
async findUsersUnsafe(filters) {
return User.find(JSON.parse(filters));
}
// SECURE: Proper query building
async findUsers(filters) {
const query = {};
// Validate each filter key
if (filters.email && this.isValidEmail(filters.email)) {
query.email = filters.email.toLowerCase().trim();
}
if (filters.role && this.isValidRole(filters.role)) {
query.role = filters.role;
}
if (filters.age) {
const age = parseInt(filters.age);
if (!isNaN(age) && age >= 0 && age {
const apiKey = req.headers[‘x-api-key’];
if (!apiKey) {
return res.status(401).json({
error: ‘API key required’,
code: ‘MISSING_API_KEY’
});
}
try {
const keyRecord = await APIKey.findOne({
key: apiKey,
isActive: true
}).populate(‘user’);
if (!keyRecord) {
return res.status(401).json({
error: ‘Invalid API key’,
code: ‘INVALID_API_KEY’
});
}
// Check rate limit for this API key
const rateLimitResult = this.checkAPIKeyRateLimit(keyRecord);
if (!rateLimitResult.allowed) {
return res.status(429).json({
error: ‘Rate limit exceeded’,
code: ‘RATE_LIMIT_EXCEEDED’,
resetTime: rateLimitResult.resetTime
});
}
// Attach key info to request
req.apiKey = keyRecord;
req.user = keyRecord.user;
next();
} catch (error) {
res.status(500).json({
error: ‘Authentication failed’,
code: ‘AUTH_ERROR’
});
}
};
}
// JWT authentication
authenticateJWT() {
return (req, res, next) => {
const authHeader = req.headers.authorization;
const token = authHeader && authHeader.split(‘ ‘)[1];
if (!token) {
return res.status(401).json({
error: ‘Access token required’,
code: ‘MISSING_TOKEN’
});
}
const security = new SecurityService();
const result = security.verifyToken(token);
if (!result.valid) {
return res.status(401).json({
error: ‘Invalid token’,
code: ‘INVALID_TOKEN’,
details: result.error
});
}
req.user = result.decoded;
req.needsTokenRefresh = result.needsRefresh;
next();
};
}
// IP-based rate limiting
rateLimit(options = {}) {
const {
windowMs = 15 * 60 * 1000, // 15 minutes
max = 100, // requests per window
message = ‘Too many requests’
} = options;
return (req, res, next) => {
const clientId = req.ip || req.connection.remoteAddress;
const now = Date.now();
// Check if IP is blocked
if (this.blockedIPs.has(clientId)) {
return res.status(429).json({
error: ‘IP address blocked’,
code: ‘IP_BLOCKED’
});
}
// Initialize or get client data
if (!this.rateLimiter.has(clientId)) {
this.rateLimiter.set(clientId, {
count: 0,
resetTime: now + windowMs,
firstRequest: now
});
}
const clientData = this.rateLimiter.get(clientId);
// Reset window if expired
if (now > clientData.resetTime) {
clientData.count = 0;
clientData.resetTime = now + windowMs;
}
// Increment count
clientData.count++;
// Check if limit exceeded
if (clientData.count > max) {
// Block IP if severely exceeded
if (clientData.count > max * 5) {
this.blockedIPs.add(clientId);
setTimeout(() => {
this.blockedIPs.delete(clientId);
}, windowMs * 2);
}
return res.status(429).json({
error: message,
code: ‘RATE_LIMIT_EXCEEDED’,
limit: max,
remaining: 0,
resetTime: clientData.resetTime
});
}
// Add rate limit headers
res.setHeader(‘X-RateLimit-Limit’, max);
res.setHeader(‘X-RateLimit-Remaining’, Math.max(0, max – clientData.count));
res.setHeader(‘X-RateLimit-Reset’, clientData.resetTime);
next();
};
}
// CORS configuration
configureCORS() {
return (req, res, next) => {
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(‘,’) || [‘http://localhost:3000’];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader(‘Access-Control-Allow-Origin’, origin);
}
res.setHeader(‘Access-Control-Allow-Methods’, ‘GET, POST, PUT, DELETE, OPTIONS’);
res.setHeader(‘Access-Control-Allow-Headers’, ‘Content-Type, Authorization, X-API-Key’);
res.setHeader(‘Access-Control-Allow-Credentials’, ‘true’);
res.setHeader(‘Access-Control-Max-Age’, ‘86400’); // 24 hours
if (req.method === ‘OPTIONS’) {
return res.status(204).end();
}
next();
};
}
// Request validation middleware
validateRequest(schema) {
return (req, res, next) => {
const { error } = schema.validate(req.body);
if (error) {
return res.status(400).json({
error: ‘Validation failed’,
details: error.details.map(detail => ({
field: detail.path.join(‘.’),
message: detail.message
}))
});
}
next();
};
}
// API response security headers
setSecurityHeaders() {
return (req, res, next) => {
res.setHeader(‘X-Content-Type-Options’, ‘nosniff’);
res.setHeader(‘X-Frame-Options’, ‘DENY’);
res.setHeader(‘X-XSS-Protection’, ‘1; mode=block’);
res.setHeader(‘Referrer-Policy’, ‘strict-origin-when-cross-origin’);
res.setHeader(‘Permissions-Policy’, ‘camera=(), microphone=(), geolocation=()’);
next();
};
}
}
// Usage
const apiSecurity = new APISecurity();
app.use(apiSecurity.configureCORS());
app.use(apiSecurity.setSecurityHeaders());
// Public endpoints with rate limiting
app.get(‘/api/public’, apiSecurity.rateLimit({ max: 10 }), (req, res) => {
res.json({ message: ‘Public endpoint’ });
});
// Protected endpoints with authentication
app.get(‘/api/users’,
apiSecurity.authenticateJWT(),
apiSecurity.rateLimit({ max: 100 }),
(req, res) => {
res.json({ users: [] });
}
);
// API endpoints with API key authentication
app.post(‘/api/webhook’,
apiSecurity.authenticateAPIKey(),
(req, res) => {
res.json({ received: true });
}
);
“`
Security Monitoring dan Logging
1. Security Event Monitoring
“`javascript
// Security Monitoring System
class SecurityMonitor {
constructor() {
this.events = [];
this.alertThresholds = {
failedLogins: 5,
suspiciousActivity: 10,
dataAccess: 50
};
this.blockedIPs = new Set();
}
// Log security events
logSecurityEvent(event) {
const securityEvent = {
timestamp: new Date().toISOString(),
type: event.type,
severity: event.severity || ‘medium’,
ip: event.ip,
userId: event.userId,
userAgent: event.userAgent,
details: event.details,
metadata: event.metadata || {}
};
this.events.push(securityEvent);
// Check for alerts
this.checkAlerts(securityEvent);
// Store in persistent storage
this.persistEvent(securityEvent);
console.log(`Security Event [${event.severity}]: ${event.type}`, securityEvent);
}
// Failed login attempt
logFailedLogin(ip, email, userAgent) {
this.logSecurityEvent({
type: ‘FAILED_LOGIN’,
severity: ‘high’,
ip,
userId: email,
userAgent,
details: ‘Failed login attempt’
});
}
// Successful login
logSuccessfulLogin(userId, ip, userAgent) {
this.logSecurityEvent({
type: ‘SUCCESSFUL_LOGIN’,
severity: ‘info’,
ip,
userId,
userAgent,
details: ‘User logged in successfully’
});
}
// Suspicious activity detection
logSuspiciousActivity(ip, userId, activity) {
this.logSecurityEvent({
type: ‘SUSPICIOUS_ACTIVITY’,
severity: ‘high’,
ip,
userId,
details: activity
});
}
// Data access logging
logDataAccess(userId, resource, action, ip) {
this.logSecurityEvent({
type: ‘DATA_ACCESS’,
severity: ‘medium’,
ip,
userId,
details: `${action} on ${resource}`,
metadata: { resource, action }
});
}
// Check for security alerts
checkAlerts(event) {
const recentEvents = this.getRecentEvents(15 * 60 * 1000); // Last 15 minutes
// Check for multiple failed logins
const failedLogins = recentEvents.filter(e =>
e.type === ‘FAILED_LOGIN’ && e.ip === event.ip
);
if (failedLogins.length >= this.alertThresholds.failedLogins) {
this.triggerAlert({
type: ‘BRUTE_FORCE_DETECTED’,
severity: ‘critical’,
ip: event.ip,
details: `${failedLogins.length} failed login attempts detected`
});
// Block IP temporarily
this.blockIP(event.ip, 60 * 60 * 1000); // 1 hour
}
// Check for suspicious activity patterns
const suspiciousActivity = recentEvents.filter(e =>
e.type === ‘SUSPICIOUS_ACTIVITY’ && e.ip === event.ip
);
if (suspiciousActivity.length >= this.alertThresholds.suspiciousActivity) {
this.triggerAlert({
type: ‘SUSPICIOUS_PATTERN_DETECTED’,
severity: ‘critical’,
ip: event.ip,
details: `${suspiciousActivity.length} suspicious activities detected`
});
}
// Check for unusual data access
const dataAccess = recentEvents.filter(e =>
e.type === ‘DATA_ACCESS’ && e.userId === event.userId
);
if (dataAccess.length >= this.alertThresholds.dataAccess) {
this.triggerAlert({
type: ‘UNUSUAL_DATA_ACCESS’,
severity: ‘high’,
userId: event.userId,
details: `${dataAccess.length} data access operations detected`
});
}
}
// Trigger security alert
async triggerAlert(alert) {
console.error(‘SECURITY ALERT:’, alert);
// Send notification
await this.sendNotification(alert);
// Store alert
await this.persistAlert(alert);
}
// Send notification (email, Slack, etc.)
async sendNotification(alert) {
// Implementation depends on notification service
const notification = {
title: `Security Alert: ${alert.type}`,
message: alert.details,
severity: alert.severity,
timestamp: new Date().toISOString()
};
// Send to security team
if (alert.severity === ‘critical’) {
// Immediate notification for critical alerts
await this.sendCriticalNotification(notification);
}
}
// Block IP temporarily
blockIP(ip, duration) {
this.blockedIPs.add(ip);
setTimeout(() => {
this.blockedIPs.delete(ip);
}, duration);
this.logSecurityEvent({
type: ‘IP_BLOCKED’,
severity: ‘high’,
ip,
details: `IP blocked for ${duration}ms`
});
}
// Get recent events
getRecentEvents(timeWindow) {
const now = Date.now();
return this.events.filter(event =>
new Date(event.timestamp).getTime() > (now – timeWindow)
);
}
// Persist event to database
async persistEvent(event) {
try {
// Store in database for long-term analysis
await SecurityEvent.create(event);
} catch (error) {
console.error(‘Failed to persist security event:’, error);
}
}
// Persist alert to database
async persistAlert(alert) {
try {
await SecurityAlert.create({
…alert,
timestamp: new Date(),
status: ‘active’
});
} catch (error) {
console.error(‘Failed to persist security alert:’, error);
}
}
}
// Security monitoring middleware
class SecurityMiddleware {
constructor(securityMonitor) {
this.monitor = securityMonitor;
}
// Monitor login attempts
monitorLogin() {
return (req, res, next) => {
const originalSend = res.send;
res.send = function(data) {
// Log failed login attempts
if (res.statusCode === 401 || res.statusCode === 403) {
this.monitor.logFailedLogin(
req.ip,
req.body.email,
req.get(‘User-Agent’)
);
}
// Log successful login
if (res.statusCode === 200 && req.path === ‘/api/login’) {
this.monitor.logSuccessfulLogin(
req.user?.id,
req.ip,
req.get(‘User-Agent’)
);
}
originalSend.call(this, data);
}.bind(this);
next();
};
}
// Monitor API access
monitorAPIAccess() {
return (req, res, next) => {
const originalSend = res.send;
res.send = function(data) {
// Log data access
if (req.user && req.method !== ‘GET’) {
this.monitor.logDataAccess(
req.user.id,
req.path,
req.method,
req.ip
);
}
originalSend.call(this, data);
}.bind(this);
next();
};
}
// Detect suspicious patterns
detectSuspiciousActivity() {
return (req, res, next) => {
const suspiciousPatterns = [
/\.\./, // Directory traversal
/ r.passed).length;
const total = results.length;
const critical = results.filter(r =>
!r.passed && r.details?.severity === ‘critical’
).length;
return {
summary: {
passed,
failed: total – passed,
total,
passRate: (passed / total) * 100,
criticalIssues: critical
},
results
};
}
}
// Common security tests
const securityTests = new SecurityTestSuite();
// Test for SQL injection vulnerabilities
securityTests.addTest(‘SQL Injection Test’, async () => {
const maliciousInputs = [
“‘ OR ‘1’=’1”,
“‘; DROP TABLE users; –“,
“‘ UNION SELECT * FROM users –”
];
for (const input of maliciousInputs) {
try {
const response = await fetch(‘/api/users’, {
method: ‘POST’,
headers: { ‘Content-Type’: ‘application/json’ },
body: JSON.stringify({ email: input, password: ‘test’ })
});
if (response.status === 200) {
return {
passed: false,
message: ‘Potential SQL injection vulnerability detected’,
details: { severity: ‘critical’, input }
};
}
} catch (error) {
// Network errors are expected for malformed requests
}
}
return { passed: true, message: ‘No SQL injection vulnerabilities detected’ };
}, ‘injection’);
// Test for XSS vulnerabilities
securityTests.addTest(‘XSS Protection Test’, async () => {
const xssPayloads = [
‘alert(“XSS”)’,
‘‘,
‘javascript:alert(“XSS”)’
];
for (const payload of xssPayloads) {
try {
const response = await fetch(‘/api/comments’, {
method: ‘POST’,
headers: { ‘Content-Type’: ‘application/json’ },
body: JSON.stringify({ content: payload })
});
if (response.status === 200) {
const data = await response.json();
// Check if XSS payload was not sanitized
if (data.content && data.content.includes(”)) {
return {
passed: false,
message: ‘XSS vulnerability detected’,
details: { severity: ‘high’, payload }
};
}
}
} catch (error) {
// Network errors are expected
}
}
return { passed: true, message: ‘No XSS vulnerabilities detected’ };
}, ‘xss’);
// Test for authentication bypass
securityTests.addTest(‘Authentication Bypass Test’, async () => {
try {
// Try to access protected endpoint without token
const response = await fetch(‘/api/users/profile’, {
headers: { ‘Content-Type’: ‘application/json’ }
});
if (response.status === 200) {
return {
passed: false,
message: ‘Authentication bypass vulnerability detected’,
details: { severity: ‘critical’ }
};
}
// Try with invalid token
const invalidResponse = await fetch(‘/api/users/profile’, {
headers: {
‘Content-Type’: ‘application/json’,
‘Authorization’: ‘Bearer invalid.token.here’
}
});
if (invalidResponse.status === 200) {
return {
passed: false,
message: ‘Invalid token accepted’,
details: { severity: ‘critical’ }
};
}
return { passed: true, message: ‘Authentication is properly secured’ };
} catch (error) {
return {
passed: false,
message: ‘Authentication test failed’,
details: { error: error.message }
};
}
}, ‘auth’);
// Test for rate limiting
securityTests.addTest(‘Rate Limiting Test’, async () => {
const requests = [];
// Send multiple requests quickly
for (let i = 0; i r.status === 200).length;
if (successCount >= 15) {
return {
passed: false,
message: ‘Rate limiting not working or too permissive’,
details: { severity: ‘medium’, successCount }
};
}
return { passed: true, message: ‘Rate limiting is working properly’ };
}, ‘infrastructure’);
// Test for HTTPS enforcement
securityTests.addTest(‘HTTPS Enforcement Test’, async () => {
if (process.env.NODE_ENV === ‘production’) {
try {
// Try HTTP request (should fail or redirect)
const response = await fetch(`http://${process.env.DOMAIN}/api/health`);
if (response.status === 200 || response.url.startsWith(‘https://’)) {
return { passed: true, message: ‘HTTPS properly enforced’ };
} else {
return {
passed: false,
message: ‘HTTPS not properly enforced’,
details: { severity: ‘high’ }
};
}
} catch (error) {
// Expected if HTTP is not available
return { passed: true, message: ‘HTTPS properly configured’ };
}
} else {
return { passed: true, message: ‘HTTPS test skipped in development’ };
}
}, ‘infrastructure’);
// Run security tests in CI/CD
async function runSecurityTests() {
console.log(‘Running security tests…’);
const results = await securityTests.runTests();
const report = securityTests.generateReport(results);
console.log(‘Security Test Results:’, report.summary);
// Fail build if critical issues found
if (report.summary.criticalIssues > 0) {
console.error(‘Critical security issues detected!’);
process.exit(1);
}
return report;
}
// Export untuk use in CI/CD
module.exports = { runSecurityTests, securityTests };
“`
Compliance dan Regulatory Requirements
1. Data Protection Compliance
“`javascript
// Personal Data Protection Act (PDPA) Compliance
class PDPACompliance {
constructor() {
this.sensitiveDataTypes = [
‘nik’, // National ID
‘passport’,
‘bankAccount’,
‘creditCard’,
‘medical’,
‘biometric’
];
}
// Consent management
async recordConsent(userId, consentType, consentData) {
const consent = {
userId,
type: consentType,
granted: consentData.granted,
purpose: consentData.purpose,
timestamp: new Date(),
ipAddress: consentData.ipAddress,
userAgent: consentData.userAgent,
documentVersion: consentData.documentVersion
};
await Consent.create(consent);
return consent;
}
// Data anonymization
anonymizePersonalData(data, fieldsToAnonymize) {
const anonymized = { …data };
fieldsToAnonymize.forEach(field => {
if (anonymized[field]) {
anonymized[field] = this.anonymizeValue(anonymized[field]);
}
});
return anonymized;
}
anonymizeValue(value) {
if (typeof value !== ‘string’) return value;
// Keep first 2 dan last 2 characters
if (value.length <= 4) {
return '*'.repeat(value.length);
}
const start = value.substring(0, 2);
const end = value.substring(value.length – 2);
const middle = '*'.repeat(value.length – 4);
return start + middle + end;
}
// Data retention management
async applyRetentionPolicy() {
const retentionPeriods = {
'userActivity': 365, // 1 year
'transaction': 2555, // 7 years
'consent': 2555, // 7 years
'auditLog': 2555 // 7 years
};
for (const [dataType, days] of Object.entries(retentionPeriods)) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() – days);
await this.deleteExpiredData(dataType, cutoffDate);
}
}
async deleteExpiredData(dataType, cutoffDate) {
switch (dataType) {
case 'userActivity':
await UserActivity.deleteMany({
timestamp: { $lt: cutoffDate }
});
break;
case 'transaction':
await Transaction.deleteMany({
createdAt: { $lt: cutoffDate }
});
break;
// Implement other data types
}
}
// Data access logging
async logDataAccess(userId, dataType, action, purpose) {
const logEntry = {
userId,
dataType,
action, // 'read', 'write', 'delete'
purpose,
timestamp: new Date(),
ipAddress: this.getCurrentIP(),
userAgent: this.getCurrentUserAgent()
};
await DataAccessLog.create(logEntry);
}
// Data breach notification
async handleDataBreach(breachData) {
// Log breach
await SecurityBreach.create({
…breachData,
reportedAt: new Date(),
status: 'investigating'
});
// Notify affected users
const affectedUsers = await this.identifyAffectedUsers(breachData);
await this.notifyAffectedUsers(affectedUsers, breachData);
// Notify authorities if required
if (breachData.severity === 'high') {
await this.notifyAuthorities(breachData);
}
}
async identifyAffectedUsers(breachData) {
// Logic to identify users whose data was compromised
return User.find({
$or: [
{ email: { $in: breachData.compromisedEmails } },
{ phone: { $in: breachData.compromisedPhones } }
]
});
}
async notifyAffectedUsers(users, breachData) {
for (const user of users) {
await EmailService.sendDataBreachNotification(user.email, {
breachType: breachData.type,
compromisedData: breachData.compromisedDataTypes,
recommendedActions: [
'Change password immediately',
'Monitor account activity',
'Report suspicious activity'
]
});
}
}
}
// Data Classification dan Handling
class DataClassification {
constructor() {
this.classifications = {
'public': { impact: 'none', encryption: false, accessLevel: 'all' },
'internal': { impact: 'low', encryption: false, accessLevel: 'employee' },
'confidential': { impact: 'medium', encryption: true, accessLevel: 'authorized' },
'restricted': { impact: 'high', encryption: true, accessLevel: 'need-to-know' }
};
}
classifyData(dataType, content) {
// Auto-classification logic
const patterns = {
'nik': /\b\d{16}\b/,
'email': /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/,
'phone': /\b\d{10,13}\b/,
'creditCard': /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/
};
for (const [type, pattern] of Object.entries(patterns)) {
if (pattern.test(content)) {
if (['nik', 'creditCard'].includes(type)) {
return 'restricted';
}
return 'confidential';
}
}
// Default classification
return 'internal';
}
applyProtection(data, classification) {
const rules = this.classifications[classification];
if (!rules) {
throw new Error(`Unknown classification: ${classification}`);
}
return {
…data,
classification,
accessLevel: rules.accessLevel,
encrypted: rules.encryption,
lastAccessed: new Date()
};
}
}
“`
Kesimpulan
Web application security adalah ongoing process yang membutuhkan continuous attention dan improvement. Dengan implementasi comprehensive security measures dari development phase hingga production monitoring, kita dapat protect applications dari modern cyber threats.
Key security priorities untuk 2025:
• Zero Trust Architecture: Never trust, always verify
• AI-Powered Security: Leverage machine learning untuk threat detection
• DevSecOps Integration: Security embedded dalam development lifecycle
• Compliance Management: Adherence to evolving regulations
• User Education: Security awareness training untuk semua stakeholders
Investasi dalam security bukan optional melainkan necessity di digital age. Cost prevention jauh lebih rendah dari cost recovery setelah security breach terjadi.
Remember: Security is everyone's responsibility. Build security culture dalam organization, stay updated dengan latest threats, dan continuously improve security practices.






























