Skip to content

Task 1-3: Mask Sensitive Data trong AdminRequestAspect Logs

Phase: 1 - Security Priority: Medium Module: admin Depends on: Không có Reference: SPEC.md — xem mục 4 (ADM-F-01 Admin auth riêng) và Code references (admin/aop/AdminRequestAspect.java)

Background

AdminRequestAspect log request body. Nếu có sensitive fields (password, token, coin amount, user personal data), chúng bị log plain text.

Tasks

File: admin/aop/AdminRequestAspect.java

  • [ ] Xác nhận hiện tại log những gì (request body? headers?)
  • [ ] Tạo SensitiveDataMasker utility:

    public class SensitiveDataMasker {
        private static final Set<String> SENSITIVE_KEYS = Set.of(
            "password", "token", "secret", "coin_amount",
            "wallet_address", "private_key", "receipt_data"
        );
    
        public static String mask(String json) {
            // Replace values for sensitive keys with "***"
            for (String key : SENSITIVE_KEYS) {
                json = json.replaceAll(
                    "(?i)(\"" + key + "\"\\s*:\\s*\")[^\"]*\"",
                    "\"" + key + "\": \"***\""
                );
            }
            return json;
        }
    }
    

    Note về package: Đặt SensitiveDataMasker trong package admin/utils/ (ví dụ: com.figpop.admin.utils.SensitiveDataMasker). Đây là plain utility class (không cần @Component), chỉ cần import tĩnh vào AdminRequestAspect.

    Edge cases: Regex trên chỉ mask JSON string values ("key": "value"). Nếu value là number ("coin_amount": 100), cần thêm regex pattern riêng:

    json = json.replaceAll(
        "(?i)(\"" + key + "\"\\s*:\\s*)\\d+",
        "\"" + key + "\": \"***\""
    );
    

  • [ ] Apply trong aspect:

    String maskedBody = SensitiveDataMasker.mask(requestBody);
    LOGGER.info("[ADMIN_AUDIT] admin={} endpoint={} body={}", adminId, endpoint, maskedBody);
    

    Note: adminIdendpoint cần được lấy từ JoinPoint hoặc HttpServletRequest. Kiểm tra cách AdminRequestAspect hiện tại trích xuất thông tin này, đảm bảo không thay đổi logic existing — chỉ thay requestBody bằng maskedBody trong câu lệnh log.

Verification / Acceptance Criteria

  • [ ] Code compiles: AdminRequestAspect.javaSensitiveDataMasker.java build thành công; import SensitiveDataMasker resolve đúng package
  • [ ] Password bị mask: Gọi POST /api/admin/auth/login với body {"email":"...", "password":"mySecret"} → log hiển thị "password": "***", không hiển thị "mySecret"
  • [ ] Token bị mask: Request body chứa "token": "abc123" → log hiển thị "token": "***"
  • [ ] coin_amount bị mask: Request chứa "coin_amount": 500 → log hiển thị "coin_amount": "***"
  • [ ] Non-sensitive fields không bị mask: Các field như "userId", "email" (không trong SENSITIVE_KEYS) vẫn hiển thị nguyên giá trị
  • [ ] Test class tham khảo: SensitiveDataMaskerTest (unit test cho utility class) — kiểm tra từng key trong SENSITIVE_KEYS và các edge cases (numeric values, nested JSON)

Files to Modify

  • admin/aop/AdminRequestAspect.java
  • admin/utils/SensitiveDataMasker.java (new)