Mata Kuliah: Keamanan Siber - Fokus Ekosistem Website
Pertemuan: 1 dari 14
Pengajar: ahmadasroni38
Tanggal: 2025-07-17 06:25:44
Durasi: 3 jam (180 menit)
Level: Pemula
π Daftar Isi
- Tujuan Pembelajaran
- Pengantar Keamanan Web
- Arsitektur Aplikasi Web
- Keamanan Frontend
- Keamanan Backend
- Keamanan Database
- Keamanan API
- Studi Kasus
- Praktikum
- Evaluasi
π― Tujuan Pembelajaran
Setelah menyelesaikan pertemuan ini, mahasiswa diharapkan dapat:
- Memahami konsep dasar keamanan aplikasi web
- Mengidentifikasi komponen-komponen arsitektur web modern
- Menjelaskan ancaman keamanan pada setiap layer aplikasi
- Mengimplementasikan praktik keamanan dasar
- Menganalisis kerentanan keamanan melalui studi kasus
π Pengantar Keamanan Web
Apa itu Keamanan Web?
Keamanan web adalah praktik melindungi aplikasi web dan data dari ancaman cyber. Ini meliputi:
- Perlindungan data: Menjaga kerahasiaan informasi pengguna
- Integritas sistem: Memastikan data tidak diubah tanpa izin
- Ketersediaan layanan: Menjamin sistem dapat diakses kapan saja
- Otentikasi: Memverifikasi identitas pengguna
- Otorisasi: Mengontrol akses ke resource
Mengapa Keamanan Web Penting?
- Melindungi data pribadi pengguna
- Menjaga reputasi perusahaan
- Mencegah kerugian finansial
- Mematuhi regulasi yang berlaku
- Mempertahankan kepercayaan pengguna
Ancaman Keamanan Web Umum
- Cross-Site Scripting (XSS)
- SQL Injection
- Cross-Site Request Forgery (CSRF)
- Session Hijacking
- Broken Authentication
- Insecure Direct Object References
ποΈ Arsitektur Aplikasi Web
Komponen Utama Aplikasi Web
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT/BROWSER β
β (HTML, CSS, JavaScript) β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β HTTP/HTTPS Request
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WEB SERVER β
β (Apache, Nginx, IIS) β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β Process Request
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β APPLICATION SERVER β
β (Node.js, PHP, Python, Java) β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β Database Query
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DATABASE β
β (MySQL, PostgreSQL, MongoDB) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Penjelasan Setiap Layer
1. Client/Browser Layer
- Fungsi: Menampilkan interface ke pengguna
- Komponen: HTML, CSS, JavaScript
- Kerentanan: XSS, CSRF, Client-side injection
2. Web Server Layer
- Fungsi: Menerima dan memproses HTTP request
- Komponen: Apache, Nginx, IIS
- Kerentanan: Server misconfiguration, DDoS
3. Application Server Layer
- Fungsi: Menjalankan business logic aplikasi
- Komponen: Node.js, PHP, Python, Java
- Kerentanan: Authentication bypass, Logic flaws
4. Database Layer
- Fungsi: Menyimpan dan mengelola data
- Komponen: MySQL, PostgreSQL, MongoDB
- Kerentanan: SQL injection, Data exposure
π₯οΈ Keamanan Frontend
Konsep Dasar Frontend Security
Frontend adalah bagian aplikasi yang berinteraksi langsung dengan pengguna. Keamanan frontend fokus pada:
- Input Validation: Memvalidasi data yang dimasukkan pengguna
- Output Encoding: Mengamankan data yang ditampilkan
- Client-side Authentication: Mengelola status login
- Secure Communication: Komunikasi aman dengan server
Ancaman Keamanan Frontend
1. Cross-Site Scripting (XSS)
Definisi: Serangan yang menyisipkan script berbahaya ke dalam halaman web.
Contoh Vulnerable Code:
<!DOCTYPE html>
<html>
<head>
<title>XSS Vulnerability Demo</title>
</head>
<body>
<h1>Welcome Message</h1>
<div id="message"></div>
<script>
// β VULNERABLE CODE
function displayMessage() {
// Mengambil parameter dari URL
const urlParams = new URLSearchParams(window.location.search);
const message = urlParams.get('msg');
// Langsung memasukkan ke DOM tanpa sanitization
document.getElementById('message').innerHTML = message;
}
displayMessage();
</script>
</body>
</html>
Serangan:
http://example.com/page.html?msg=<script>alert('XSS Attack!')</script>
Solusi:
<!DOCTYPE html>
<html>
<head>
<title>XSS Protection Demo</title>
</head>
<body>
<h1>Welcome Message</h1>
<div id="message"></div>
<script>
// β
SECURE CODE
function displayMessage() {
const urlParams = new URLSearchParams(window.location.search);
const message = urlParams.get('msg');
if (message) {
// Sanitize input dengan escape HTML characters
const sanitizedMessage = escapeHtml(message);
document.getElementById('message').innerHTML = sanitizedMessage;
}
}
// Function untuk escape HTML characters
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
displayMessage();
</script>
</body>
</html>
2. Input Validation
Pentingnya Input Validation:
- Mencegah data berbahaya masuk ke sistem
- Memastikan data sesuai format yang diharapkan
- Mengurangi beban server processing
Contoh Implementasi:
<!DOCTYPE html>
<html>
<head>
<title>Input Validation Demo</title>
<style>
.error { color: red; font-size: 12px; }
.success { color: green; font-size: 12px; }
.form-group { margin: 10px 0; }
input, textarea { padding: 8px; width: 300px; }
button { padding: 10px 20px; background: #007bff; color: white; border: none; }
</style>
</head>
<body>
<h1>Secure Contact Form</h1>
<form id="contactForm">
<div class="form-group">
<label for="name">Nama:</label><br>
<input type="text" id="name" name="name" maxlength="50" required>
<div class="error" id="nameError"></div>
</div>
<div class="form-group">
<label for="email">Email:</label><br>
<input type="email" id="email" name="email" maxlength="100" required>
<div class="error" id="emailError"></div>
</div>
<div class="form-group">
<label for="phone">Telepon:</label><br>
<input type="tel" id="phone" name="phone" maxlength="15">
<div class="error" id="phoneError"></div>
</div>
<div class="form-group">
<label for="message">Pesan:</label><br>
<textarea id="message" name="message" rows="4" maxlength="500" required></textarea>
<div class="error" id="messageError"></div>
</div>
<button type="submit">Kirim Pesan</button>
</form>
<div id="result"></div>
<script>
// Validation functions
function validateName(name) {
const nameRegex = /^[a-zA-Z\s]+$/;
if (!name.trim()) {
return 'Nama harus diisi';
}
if (name.length < 2) {
return 'Nama minimal 2 karakter';
}
if (name.length > 50) {
return 'Nama maksimal 50 karakter';
}
if (!nameRegex.test(name)) {
return 'Nama hanya boleh mengandung huruf dan spasi';
}
return '';
}
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email.trim()) {
return 'Email harus diisi';
}
if (!emailRegex.test(email)) {
return 'Format email tidak valid';
}
return '';
}
function validatePhone(phone) {
const phoneRegex = /^[0-9+\-\s()]+$/;
if (phone && !phoneRegex.test(phone)) {
return 'Nomor telepon tidak valid';
}
return '';
}
function validateMessage(message) {
if (!message.trim()) {
return 'Pesan harus diisi';
}
if (message.length < 10) {
return 'Pesan minimal 10 karakter';
}
if (message.length > 500) {
return 'Pesan maksimal 500 karakter';
}
return '';
}
// Real-time validation
document.getElementById('name').addEventListener('input', function() {
const error = validateName(this.value);
document.getElementById('nameError').textContent = error;
});
document.getElementById('email').addEventListener('input', function() {
const error = validateEmail(this.value);
document.getElementById('emailError').textContent = error;
});
document.getElementById('phone').addEventListener('input', function() {
const error = validatePhone(this.value);
document.getElementById('phoneError').textContent = error;
});
document.getElementById('message').addEventListener('input', function() {
const error = validateMessage(this.value);
document.getElementById('messageError').textContent = error;
});
// Form submission
document.getElementById('contactForm').addEventListener('submit', function(e) {
e.preventDefault();
// Get form data
const formData = {
name: document.getElementById('name').value,
email: document.getElementById('email').value,
phone: document.getElementById('phone').value,
message: document.getElementById('message').value
};
// Validate all fields
const errors = {
name: validateName(formData.name),
email: validateEmail(formData.email),
phone: validatePhone(formData.phone),
message: validateMessage(formData.message)
};
// Display errors
document.getElementById('nameError').textContent = errors.name;
document.getElementById('emailError').textContent = errors.email;
document.getElementById('phoneError').textContent = errors.phone;
document.getElementById('messageError').textContent = errors.message;
// Check if there are any errors
const hasErrors = Object.values(errors).some(error => error !== '');
if (!hasErrors) {
// Sanitize data before sending
const sanitizedData = {
name: sanitizeInput(formData.name),
email: sanitizeInput(formData.email),
phone: sanitizeInput(formData.phone),
message: sanitizeInput(formData.message)
};
// Simulate form submission
document.getElementById('result').innerHTML = `
<h2>Form Submitted Successfully!</h2>
<p><strong>Nama:</strong> ${sanitizedData.name}</p>
<p><strong>Email:</strong> ${sanitizedData.email}</p>
<p><strong>Telepon:</strong> ${sanitizedData.phone}</p>
<p><strong>Pesan:</strong> ${sanitizedData.message}</p>
<p><strong>Processed by:</strong> ahmadasroni38</p>
<p><strong>Time:</strong> ${new Date().toLocaleString()}</p>
`;
// Reset form
this.reset();
}
});
// Sanitization function
function sanitizeInput(input) {
return input.trim()
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/');
}
console.log('Contact Form loaded by ahmadasroni38 at', new Date());
</script>
</body>
</html>
π Keamanan Backend
Konsep Dasar Backend Security
Backend adalah server yang memproses request dari client. Keamanan backend meliputi:
- Authentication: Memverifikasi identitas pengguna
- Authorization: Mengontrol akses ke resource
- Input Validation: Validasi data dari client
- Session Management: Mengelola sesi pengguna
- Error Handling: Menangani error dengan aman
Implementasi Authentication
Contoh Simple Authentication System:
// simple-auth.js
// Simple authentication system
// Author: ahmadasroni38
// Date: 2025-07-17 06:25:44
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const app = express();
// Middleware
app.use(express.json());
app.use(express.static('public'));
// Simple user database (in production, use real database)
const users = [
{
id: 1,
username: 'admin',
email: 'admin@example.com',
password: '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // "password"
role: 'admin'
},
{
id: 2,
username: 'user',
email: 'user@example.com',
password: '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // "password"
role: 'user'
}
];
const JWT_SECRET = 'your-secret-key-here';
// Login endpoint
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body;
// Input validation
if (!username || !password) {
return res.status(400).json({
success: false,
message: 'Username dan password harus diisi'
});
}
// Find user
const user = users.find(u => u.username === username);
if (!user) {
return res.status(401).json({
success: false,
message: 'Username atau password salah'
});
}
// Verify password
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
return res.status(401).json({
success: false,
message: 'Username atau password salah'
});
}
// Generate JWT token
const token = jwt.sign(
{
id: user.id,
username: user.username,
role: user.role
},
JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({
success: true,
message: 'Login berhasil',
token: token,
user: {
id: user.id,
username: user.username,
email: user.email,
role: user.role
}
});
console.log(`Login successful for user: ${username} by ahmadasroni38`);
} catch (error) {
console.error('Login error:', error);
res.status(500).json({
success: false,
message: 'Server error'
});
}
});
// Authentication middleware
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({
success: false,
message: 'Access token required'
});
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({
success: false,
message: 'Invalid or expired token'
});
}
req.user = user;
next();
});
};
// Protected endpoint
app.get('/api/profile', authenticateToken, (req, res) => {
const user = users.find(u => u.id === req.user.id);
if (!user) {
return res.status(404).json({
success: false,
message: 'User not found'
});
}
res.json({
success: true,
user: {
id: user.id,
username: user.username,
email: user.email,
role: user.role
}
});
});
// Input validation and sanitization
app.post('/api/contact', (req, res) => {
const { name, email, message } = req.body;
// Input validation
const errors = [];
if (!name || name.length < 2) {
errors.push('Nama minimal 2 karakter');
}
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
errors.push('Email tidak valid');
}
if (!message || message.length < 10) {
errors.push('Pesan minimal 10 karakter');
}
if (errors.length > 0) {
return res.status(400).json({
success: false,
message: 'Validation error',
errors: errors
});
}
// Sanitize input
const sanitizedData = {
name: name.trim().replace(/[<>]/g, ''),
email: email.trim().toLowerCase(),
message: message.trim().replace(/[<>]/g, '')
};
// Log contact submission
console.log('Contact form submitted:', sanitizedData);
console.log('Processed by: ahmadasroni38');
console.log('Time:', new Date().toISOString());
res.json({
success: true,
message: 'Pesan berhasil dikirim',
data: sanitizedData
});
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
console.log(`Started by: ahmadasroni38`);
console.log(`Time: ${new Date().toISOString()}`);
});
ποΈ Keamanan Database
Konsep Dasar Database Security
Database security meliputi:
- Access Control: Mengontrol akses ke database
- Data Encryption: Mengenkripsi data sensitif
- SQL Injection Prevention: Mencegah serangan SQL injection
- Backup Security: Backup data dengan aman
- Audit Logging: Mencatat aktivitas database
SQL Injection Prevention
Contoh Vulnerable Code:
// β VULNERABLE - SQL Injection
app.get('/api/user/:id', (req, res) => {
const userId = req.params.id;
// Dangerous - String concatenation
const query = `SELECT * FROM users WHERE id = ${userId}`;
db.query(query, (err, results) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.json(results);
});
});
// Attack: /api/user/1 OR 1=1
// Query becomes: SELECT * FROM users WHERE id = 1 OR 1=1
// Returns all users!
Secure Code:
// β
SECURE - Prepared Statements
app.get('/api/user/:id', (req, res) => {
const userId = req.params.id;
// Input validation
if (!userId || isNaN(userId)) {
return res.status(400).json({
success: false,
message: 'Invalid user ID'
});
}
// Use prepared statement
const query = 'SELECT id, username, email FROM users WHERE id = ?';
db.query(query, [userId], (err, results) => {
if (err) {
console.error('Database error:', err);
return res.status(500).json({
success: false,
message: 'Database error'
});
}
if (results.length === 0) {
return res.status(404).json({
success: false,
message: 'User not found'
});
}
res.json({
success: true,
user: results[0]
});
});
});
Database Setup dengan Security
-- Secure database setup
-- Author: ahmadasroni38
-- Date: 2025-07-17 06:25:44
-- Create database
CREATE DATABASE secure_app;
USE secure_app;
-- Create users table
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role ENUM('admin', 'user') DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Create sessions table
CREATE TABLE sessions (
id VARCHAR(255) PRIMARY KEY,
user_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Create audit log table
CREATE TABLE audit_log (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
action VARCHAR(100) NOT NULL,
table_name VARCHAR(50),
record_id INT,
ip_address VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
);
-- Create application user dengan limited privileges
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'secure_password_123';
GRANT SELECT, INSERT, UPDATE, DELETE ON secure_app.* TO 'app_user'@'localhost';
-- Sample data
INSERT INTO users (username, email, password, role) VALUES
('admin', 'admin@example.com', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'admin'),
('user', 'user@example.com', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'user');
π Keamanan API
API Security Best Practices
- Authentication: Verifikasi identitas pengguna
- Authorization: Kontrol akses ke resource
- Rate Limiting: Mencegah abuse
- Input Validation: Validasi semua input
- HTTPS: Enkripsi data in transit
- API Versioning: Versioning untuk backward compatibility
Implementasi API Security
// api-security.js
// API Security implementation
// Author: ahmadasroni38
// Date: 2025-07-17 06:25:44
const express = require('express');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const cors = require('cors');
const app = express();
// Security middleware
app.use(helmet()); // Set security headers
app.use(cors({
origin: ['http://localhost:3000', 'https://yourdomain.com'],
credentials: true
}));
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: {
success: false,
message: 'Too many requests, please try again later.',
timestamp: new Date().toISOString()
},
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/', limiter);
// Special rate limiting for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: {
success: false,
message: 'Too many authentication attempts, please try again later.',
timestamp: new Date().toISOString()
}
});
app.use('/api/auth/', authLimiter);
// Body parsing dengan size limit
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Request logging middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.path} - ${req.ip} - ${new Date().toISOString()}`);
next();
});
// Input validation middleware
const validateInput = (req, res, next) => {
const { body, query, params } = req;
// Check for potential XSS
const checkXSS = (value) => {
if (typeof value === 'string') {
const xssPattern = /<script|javascript:|onload=|onerror=/i;
return xssPattern.test(value);
}
return false;
};
// Check all inputs
const allInputs = { ...body, ...query, ...params };
for (const key in allInputs) {
if (checkXSS(allInputs[key])) {
return res.status(400).json({
success: false,
message: 'Potentially malicious input detected',
timestamp: new Date().toISOString()
});
}
}
next();
};
app.use('/api/', validateInput);
// Error handling middleware
app.use((err, req, res, next) => {
console.error(`Error: ${err.message}`);
console.error(`Stack: ${err.stack}`);
console.error(`User: ahmadasroni38`);
console.error(`Time: ${new Date().toISOString()}`);
res.status(500).json({
success: false,
message: 'Internal server error',
timestamp: new Date().toISOString()
});
});
// API endpoints
app.get('/api/status', (req, res) => {
res.json({
success: true,
message: 'API is running',
timestamp: new Date().toISOString(),
author: 'ahmadasroni38'
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`API Server running on port ${PORT}`);
console.log(`Started by: ahmadasroni38`);
console.log(`Time: ${new Date().toISOString()}`);
});
π Studi Kasus
Kasus 1: Toko Online Sederhana
Skenario: Sebuah toko online kecil mengalami serangan yang mencuri data pelanggan.
Analisis Kerentanan:
- XSS di Search Function
<!-- Vulnerable search function -->
<script>
function searchProducts() {
const query = document.getElementById('searchInput').value;
// Langsung memasukkan ke DOM tanpa sanitasi
document.getElementById('searchResults').innerHTML =
'<h3>Hasil pencarian untuk: ' + query + '</h3>';
}
</script>
Serangan:
<script>
// Attacker input di search box
alert('XSS Attack! Cookie: ' + document.cookie);
</script>
Solusi:
<script>
function searchProducts() {
const query = document.getElementById('searchInput').value;
// Sanitasi input
const sanitizedQuery = query.replace(/[<>]/g, '');
// Gunakan textContent untuk mencegah XSS
document.getElementById('searchResults').textContent =
'Hasil pencarian untuk: ' + sanitizedQuery;
}
</script>
- SQL Injection di Login
// Vulnerable login code
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($conn, $query);
Serangan:
Username: admin' OR '1'='1
Password: anything
Solusi:
// Secure login code
$username = $_POST['username'];
$password = $_POST['password'];
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();
Kasus 2: Blog Pribadi
Skenario: Blog pribadi yang memiliki sistem komentar mengalami spam dan konten berbahaya.
Problem: Komentar tidak divalidasi dan disanitasi dengan baik.
Vulnerable Code:
<!DOCTYPE html>
<html>
<head>
<title>Blog Comment System</title>
</head>
<body>
<h1>Komentar</h1>
<form id="commentForm">
<textarea id="comment" placeholder="Tulis komentar..."></textarea>
<button type="submit">Kirim</button>
</form>
<div id="comments"></div>
<script>
let comments = [];
document.getElementById('commentForm').addEventListener('submit', function(e) {
e.preventDefault();
const comment = document.getElementById('comment').value;
// β VULNERABLE - No validation/sanitization
comments.push(comment);
displayComments();
});
function displayComments() {
const commentsDiv = document.getElementById('comments');
commentsDiv.innerHTML = '';
comments.forEach(comment => {
// β VULNERABLE - Direct HTML insertion
commentsDiv.innerHTML += '<div>' + comment + '</div>';
});
}
</script>
</body>
</html>
Secure Solution:
<!DOCTYPE html>
<html>
<head>
<title>Secure Blog Comment System</title>
<style>
.error { color: red; }
.comment { border: 1px solid #ccc; padding: 10px; margin: 10px 0; }
.comment-meta { font-size: 12px; color: #666; }
</style>
</head>
<body>
<h1>Komentar</h1>
<form id="commentForm">
<div>
<label for="name">Nama:</label>
<input type="text" id="name" maxlength="50" required>
<div class="error" id="nameError"></div>
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" maxlength="100" required>
<div class="error" id="emailError"></div>
</div>
<div>
<label for="comment">Komentar:</label>
<textarea id="comment" maxlength="1000" required></textarea>
<div class="error" id="commentError"></div>
</div>
<button type="submit">Kirim</button>
</form>
<div id="comments"></div>
<script>
let comments = [];
// Validation functions
function validateName(name) {
if (!name.trim()) return 'Nama harus diisi';
if (name.length < 2) return 'Nama minimal 2 karakter';
if (!/^[a-zA-Z\s]+$/.test(name)) return 'Nama hanya boleh huruf dan spasi';
return '';
}
function validateEmail(email) {
if (!email.trim()) return 'Email harus diisi';
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return 'Format email tidak valid';
return '';
}
function validateComment(comment) {
if (!comment.trim()) return 'Komentar harus diisi';
if (comment.length < 10) return 'Komentar minimal 10 karakter';
return '';
}
// Sanitization function
function sanitizeInput(input) {
return input.trim()
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// Form submission
document.getElementById('commentForm').addEventListener('submit', function(e) {
e.preventDefault();
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const comment = document.getElementById('comment').value;
// Validate inputs
const nameError = validateName(name);
const emailError = validateEmail(email);
const commentError = validateComment(comment);
document.getElementById('nameError').textContent = nameError;
document.getElementById('emailError').textContent = emailError;
document.getElementById('commentError').textContent = commentError;
// If no errors, add comment
if (!nameError && !emailError && !commentError) {
const sanitizedComment = {
name: sanitizeInput(name),
email: sanitizeInput(email),
comment: sanitizeInput(comment),
timestamp: new Date().toLocaleString(),
author: 'ahmadasroni38'
};
comments.push(sanitizedComment);
displayComments();
// Reset form
this.reset();
}
});
function displayComments() {
const commentsDiv = document.getElementById('comments');
commentsDiv.innerHTML = '';
comments.forEach(comment => {
const commentDiv = document.createElement('div');
commentDiv.className = 'comment';
// β
SECURE - Use textContent instead of innerHTML
commentDiv.innerHTML = `
<strong>${comment.name}</strong>
<div class="comment-meta">
${comment.timestamp} | Processed by: ${comment.author}
</div>
<p>${comment.comment}</p>
`;
commentsDiv.appendChild(commentDiv);
});
}
console.log('Secure comment system loaded by ahmadasroni38');
</script>
</body>
</html>
π§ͺ Praktikum
Praktikum 1: Setup Environment (20 menit)
1.1 Install Prerequisites
# Install Node.js
# Download dari https://nodejs.org/
# Verify installation
node --version
npm --version
# Create project directory
mkdir security-lab
cd security-lab
# Initialize npm project
npm init -y
# Install dependencies
npm install express bcryptjs jsonwebtoken cors helmet express-rate-limit
1.2 Create Project Structure
security-lab/
βββ package.json
βββ server.js
βββ public/
β βββ index.html
β βββ login.html
β βββ dashboard.html
βββ README.md
Praktikum 2: Implementasi Basic Security (40 menit)
2.1 Create Server dengan Security
// server.js
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const app = express();
// Security middleware
app.use(helmet());
app.use(cors());
app.use(express.json());
app.use(express.static('public'));
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use(limiter);
// Simple user database
const users = [
{
id: 1,
username: 'admin',
password: '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // "password"
role: 'admin'
}
];
const JWT_SECRET = 'your-secret-key-here';
// Login endpoint
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body;
// Find user
const user = users.find(u => u.username === username);
if (!user || !await bcrypt.compare(password, user.password)) {
return res.status(401).json({
success: false,
message: 'Invalid credentials'
});
}
// Generate token
const token = jwt.sign(
{ id: user.id, username: user.username, role: user.role },
JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({
success: true,
token: token,
user: { id: user.id, username: user.username, role: user.role }
});
} catch (error) {
res.status(500).json({ success: false, message: 'Server error' });
}
});
// Protected endpoint
app.get('/api/dashboard', (req, res) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ success: false, message: 'Token required' });
}
try {
const decoded = jwt.verify(token, JWT_SECRET);
res.json({
success: true,
message: 'Welcome to dashboard',
user: decoded,
timestamp: new Date().toISOString(),
processedBy: 'ahmadasroni38'
});
} catch (error) {
res.status(403).json({ success: false, message: 'Invalid token' });
}
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
console.log(`Started by: ahmadasroni38`);
});
2.2 Create Frontend Pages
public/index.html:
<!DOCTYPE html>
<html>
<head>
<title>Security Lab - Home</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.card { border: 1px solid #ddd; padding: 20px; margin: 20px 0; }
button { padding: 10px 20px; margin: 10px; }
.error { color: red; }
.success { color: green; }
</style>
</head>
<body>
<h1>π‘οΈ Security Lab</h1>
<p>Author: ahmadasroni38 | Date: 2025-07-17 06:25:44</p>
<div class="card">
<h2>Available Pages</h2>
<button onclick="location.href='/login.html'">Login Page</button>
<button onclick="location.href='/dashboard.html'">Dashboard</button>
</div>
<div class="card">
<h2>Test XSS Protection</h2>
<input type="text" id="testInput" placeholder="Try entering <script>alert('XSS')</script>">
<button onclick="testXSS()">Test Input</button>
<div id="output"></div>
</div>
<script>
function testXSS() {
const input = document.getElementById('testInput').value;
const output = document.getElementById('output');
// Safe way to display user input
output.textContent = 'You entered: ' + input;
console.log('XSS test performed by ahmadasroni38');
}
</script>
</body>
</html>
public/login.html:
<!DOCTYPE html>
<html>
<head>
<title>Security Lab - Login</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.form-group { margin: 15px 0; }
input { padding: 8px; width: 200px; }
button { padding: 10px 20px; }
.error { color: red; }
.success { color: green; }
</style>
</head>
<body>
<h1>π Login</h1>
<p>Author: ahmadasroni38</p>
<form id="loginForm">
<div class="form-group">
<label>Username:</label><br>
<input type="text" id="username" required>
</div>
<div class="form-group">
<label>Password:</label><br>
<input type="password" id="password" required>
</div>
<button type="submit">Login</button>
</form>
<div id="message"></div>
<p>Test credentials: admin / password</p>
<script>
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const result = await response.json();
const messageDiv = document.getElementById('message');
if (result.success) {
localStorage.setItem('token', result.token);
messageDiv.innerHTML = '<p class="success">Login successful! Redirecting...</p>';
setTimeout(() => {
window.location.href = '/dashboard.html';
}, 1000);
} else {
messageDiv.innerHTML = '<p class="error">' + result.message + '</p>';
}
} catch (error) {
document.getElementById('message').innerHTML = '<p class="error">Network error</p>';
}
});
</script>
</body>
</html>
public/dashboard.html:
<!DOCTYPE html>
<html>
<head>
<title>Security Lab - Dashboard</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.card { border: 1px solid #ddd; padding: 20px; margin: 20px 0; }
button { padding: 10px 20px; margin: 10px; }
.error { color: red; }
.success { color: green; }
</style>
</head>
<body>
<h1>π Dashboard</h1>
<p>Author: ahmadasroni38</p>
<div class="card">
<h2>User Information</h2>
<div id="userInfo">Loading...</div>
</div>
<div class="card">
<h2>Actions</h2>
<button onclick="loadDashboard()">Refresh Data</button>
<button onclick="logout()">Logout</button>
</div>
<script>
// Check if user is logged in
const token = localStorage.getItem('token');
if (!token) {
window.location.href = '/login.html';
}
async function loadDashboard() {
try {
const response = await fetch('/api/dashboard', {
headers: { 'Authorization': 'Bearer ' + token }
});
const result = await response.json();
if (result.success) {
document.getElementById('userInfo').innerHTML = `
<p><strong>Username:</strong> ${result.user.username}</p>
<p><strong>Role:</strong> ${result.user.role}</p>
<p><strong>Timestamp:</strong> ${result.timestamp}</p>
<p><strong>Processed by:</strong> ${result.processedBy}</p>
`;
} else {
document.getElementById('userInfo').innerHTML = '<p class="error">' + result.message + '</p>';
if (response.status === 401 || response.status === 403) {
setTimeout(() => {
window.location.href = '/login.html';
}, 2000);
}
}
} catch (error) {
document.getElementById('userInfo').innerHTML = '<p class="error">Network error</p>';
}
}
function logout() {
localStorage.removeItem('token');
window.location.href = '/login.html';
}
// Load dashboard on page load
loadDashboard();
</script>
</body>
</html>
Praktikum 3: Testing Security (40 menit)
3.1 Test XSS Protection
Test Cases:
- Input
<script>alert('XSS')</script>
di form - Input
<img src=x onerror=alert('XSS')>
- Input
javascript:alert('XSS')
3.2 Test Authentication
# Start server
node server.js
# Test login dengan curl
curl -X POST http://localhost:3000/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"password"}'
# Test protected endpoint tanpa token
curl http://localhost:3000/api/dashboard
# Test dengan token
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" \
http://localhost:3000/api/dashboard
3.3 Test Rate Limiting
// rate-limit-test.js
async function testRateLimit() {
console.log('Testing rate limit...');
for (let i = 1; i <= 110; i++) {
try {
const response = await fetch('http://localhost:3000/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'test', password: 'test' })
});
console.log(`Request ${i}: Status ${response.status}`);
if (response.status === 429) {
console.log('Rate limit reached!');
break;
}
} catch (error) {
console.log(`Request ${i}: Error - ${error.message}`);
}
// Small delay between requests
await new Promise(resolve => setTimeout(resolve, 100));
}
console.log('Rate limit test completed by ahmadasroni38');
}
testRateLimit();
Praktikum 4: Implementasi Logging (20 menit)
4.1 Add Logging to Server
// Add to server.js
const fs = require('fs');
const path = require('path');
// Create logs directory
const logsDir = path.join(__dirname, 'logs');
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir);
}
// Logging function
function logActivity(activity, details = {}) {
const logEntry = {
timestamp: new Date().toISOString(),
activity: activity,
details: details,
author: 'ahmadasroni38'
};
const logFile = path.join(logsDir, `security-${new Date().toISOString().split('T')[0]}.log`);
fs.appendFileSync(logFile, JSON.stringify(logEntry) + '\n');
}
// Add logging to login endpoint
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body;
// Log login attempt
logActivity('LOGIN_ATTEMPT', { username, ip: req.ip });
const user = users.find(u => u.username === username);
if (!user || !await bcrypt.compare(password, user.password)) {
logActivity('LOGIN_FAILED', { username, ip: req.ip });
return res.status(401).json({
success: false,
message: 'Invalid credentials'
});
}
// Log successful login
logActivity('LOGIN_SUCCESS', { username, ip: req.ip });
const token = jwt.sign(
{ id: user.id, username: user.username, role: user.role },
JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({
success: true,
token: token,
user: { id: user.id, username: user.username, role: user.role }
});
} catch (error) {
logActivity('LOGIN_ERROR', { error: error.message, ip: req.ip });
res.status(500).json({ success: false, message: 'Server error' });
}
});
π Evaluasi
Pertanyaan Teori (40 menit)
-
Jelaskan perbedaan antara authentication dan authorization! (10 poin)
- Authentication: Verifikasi identitas pengguna
- Authorization: Kontrol akses ke resource
-
Mengapa input validation harus dilakukan di frontend DAN backend? (10 poin)
- Frontend: UX yang lebih baik, feedback langsung
- Backend: Tidak bisa dibypass, security sesungguhnya
-
Apa itu XSS dan bagaimana cara mencegahnya? (10 poin)
- XSS: Penyisipan script berbahaya
- Pencegahan: Input validation, output encoding, CSP
-
Jelaskan pentingnya rate limiting dalam API security! (10 poin)
- Mencegah brute force attacks
- Melindungi dari DDoS
- Mengontrol penggunaan resource
Tugas Praktik (60 menit)
-
Implementasi CSRF Protection (20 poin)
- Tambahkan CSRF token pada form
- Validasi token di server
- Test dengan dan tanpa token
-
Password Strength Checker (20 poin)
- Minimum 8 karakter
- Harus ada huruf besar, kecil, angka, simbol
- Real-time feedback
-
Session Management (20 poin)
- Implementasi session timeout
- Logout otomatis setelah inactive
- Multiple session handling
Rubrik Penilaian
Aspek | Excellent (90-100) | Good (80-89) | Fair (70-79) | Poor (<70) |
---|---|---|---|---|
Pemahaman Konsep | Memahami semua konsep dengan sempurna | Memahami sebagian besar konsep | Memahami konsep dasar | Pemahaman terbatas |
Implementasi | Code berfungsi sempurna dan secure | Code berfungsi dengan minor issues | Code berfungsi tapi ada kerentanan | Code tidak berfungsi |
Testing | Comprehensive testing dengan berbagai skenario | Good testing coverage | Basic testing | Minimal testing |
Documentation | Dokumentasi lengkap dan jelas | Dokumentasi good | Dokumentasi minimal | Tidak ada dokumentasi |
π Referensi
Dokumentasi Resmi
Tutorial dan Artikel
Tools
- OWASP ZAP - Web vulnerability scanner
- Burp Suite - Web security testing
- Postman - API testing
π― Kesimpulan
Pada pertemuan ini, kita telah mempelajari:
- Konsep dasar keamanan aplikasi web
- Arsitektur aplikasi web modern dan titik kerentanannya
- Implementasi keamanan di frontend, backend, dan database
- Praktik terbaik untuk mengamankan aplikasi web
- Testing dan validasi keamanan aplikasi
Key Takeaways:
- Security by Design: Keamanan harus dipikirkan dari awal pengembangan
- Defense in Depth: Implementasi keamanan di multiple layers
- Never Trust Input: Selalu validasi dan sanitasi input
- Principle of Least Privilege: Berikan akses minimal yang diperlukan
- Keep It Simple: Solusi sederhana lebih mudah di-secure
Next Steps:
- Pertemuan 2: OWASP Top 10 - Injection Attacks
- Praktik lebih lanjut dengan real-world applications
- Implementasi advanced security measures
π Bantuan dan Dukungan
Pengajar: ahmadasroni38
Email: ahmad.asroni@motaacademy.id
Office Hours: Senin-Jumat, 09:00-16:00
Lab Repository: GitHub Security Lab
Emergency Contact:
- Slack: @ahmadasroni38
- WhatsApp: +62-xxx-xxx-xxxx (Lab hours only)
Dibuat dengan β€οΈ untuk pembelajaran keamanan web
Author: ahmadasroni38
Last Updated: 2025-07-17 06:25:44
Version: 1.0
Top comments (0)