"로그, 남기는 건 필수지만 반복하는 건 싫으니까요."
PII 마스킹, 암호화, 감사 로깅, 위변조 방지 등 보안 로깅에 필요한 기능들을 직접 구현하다 보면 로깅 코드가 비즈니스 코드보다 복잡해집니다.
🚛 KBS (blacK-Box logging SDK)는 이 모든 걸 어노테이션과 설정으로 해결합니다.
| 기능 | 설명 |
|---|---|
| PII 자동 마스킹 | 주민번호, 카드번호 등 민감정보 자동 탐지 및 마스킹 |
| @Mask 어노테이션 마스킹 | DTO 필드에 선언적 마스킹 (9가지 타입 지원) |
| 비상용 복호화 로깅 | 비상시 복호화 가능한 암호화 저장 (emergency = true) |
| AOP 기반 감사 문맥 | 누가/누구를/왜 조회했는지 AOP로 자동 기록 |
| 위변조 방지 | 블록체인 방식 위변조 탐지 |
| 암호화 | 로그 전체 암호화 |
| Crypto-Shredding | 암호화 키 파기로 데이터 영구 삭제 |
비즈니스 로직에만 집중하세요. 전화번호, 이메일, 주민등록번호 같은 민감 정보(PII)가 감지되면 SDK가 자동으로
******마스킹 처리하여 저장합니다.
DTO 필드에 어노테이션만 붙이면 리플렉션 프로세서가 자동으로 마스킹을 수행합니다. 9가지 MaskType(RRN, PHONE, EMAIL, CREDIT_CARD, PASSWORD, SSN, NAME, ADDRESS, ACCOUNT_NUMBER)을 지원합니다.
public class EmployeeDto {
@Mask(MaskType.RRN)
private String residentNumber; // 123456-1234567 → 123456-*******
@Mask(MaskType.PHONE)
private String phoneNumber; // 010-1234-5678 → 010-****-5678
@Mask(MaskType.EMAIL)
private String email; // user@example.com → u***@example.com
}"마스킹된 데이터, 사고 분석 시 원본이 필요하면?" 이런 고민을 해결합니다.
@Mask(emergency = true)설정 시 마스킹 대신 RSA-OAEP로 암호화된 원본을 저장합니다. 평문 노출 없이 추후 인가된 관리자만 복호화할 수 있습니다.
@Mask(value = MaskType.RRN, emergency = true)
private String residentNumber; // 결과: {"display":"123456-*******", "encrypted":"Base64..."}누가(Who), 누구의(Whom) 정보를 왜(Why) 봤는지 자동으로 기록합니다. 비즈니스 로직에 침투하지 않고도 감사 정보를 추출합니다.
@AuditContext(
why = "급여 정보 조회",
whomParam = "employeeId",
action = AuditAction.READ
)
public EmployeeSalaryDto getSalary(String employeeId) {
// who: Spring Security에서 자동 추출
// whom: employeeId 파라미터 값
// why: "급여 정보 조회"
return repository.findSalary(employeeId);
}로그의 신뢰성을 위해 블록체인의 Hash Chain 기술을 적용했습니다. 이전 로그와 현재 로그가 체인처럼 연결되어 있어, 중간에 데이터가 1바이트라도 변조되면 즉시 탐지 가능합니다.
저장되는 순간 AES-256-GCM 암호화 방식이 적용됩니다. 암호화 키 없이는 그 누구도 내용을 확인할 수 없습니다.
수많은 로그 속에서 특정 개인정보를 찾아 지우는 것은 비효율적입니다. 우리는 해당 데이터의 '암호화 키'를 파기하는 방식으로, 데이터를 영구적으로 복구 불가능하게 만듭니다.
| 기능 | ⭐️ KBS SDK ⭐ | Logback+SLF4J |
|---|---|---|
| PII 자동 마스킹 | ✅ 내장 | ❌ |
| @Mask 어노테이션 | ✅ | ❌ |
| 비상용 복호화 | ✅ | ❌ |
| 감사 문맥 (@AuditContext) | ✅ | ❌ |
| 위변조 방지 (Hash Chain) | ✅ | ❌ |
| 암호화 | ✅ | ❌ |
| Crypto-Shredding | ✅ | ❌ |
| 분류 | 기술 스택 |
|---|---|
|
BackEnd |
|
|
Infra |
|
build.gradle파일에 의존성을 추가합니다.
dependencies {
implementation 'io.github.hongjungwan:blackbox-logging-sdk:3.0.0-RELEASE'
}application.yml에서 로깅 정책을 설정합니다. 비동기 처리(ASYNC)를 통해 애플리케이션 성능 영향을 최소화하며, 보안 옵션을 손쉽게 활성화할 수 있습니다.
secure-hr:
logging:
enabled: true
mode: ASYNC # 로깅 성능 최적화 (ASYNC / SYNC)
pii-masking:
enabled: true
patterns: [rrn, credit_card, password, ssn] # 자동 마스킹할 필드명 패턴
security:
encryption-enabled: true # 로그 데이터 암호화 활성화
integrity-enabled: true # 로그 위변조 방지를 위한 무결성 검증 활성화
fallback-directory: "logs/fallback" # 로깅 실패 시 저장할 로컬 경로일반 Logger와 동일하게 사용하되, 설정된 민감 정보 패턴(예: rrn)이 감지되면 자동으로 마스킹 처리됩니다.
@Service
public class EmployeeService {
private final SecureLogger secureLogger;
public void process(String employeeId) {
// 민감 정보(rrn)는 자동으로 마스킹되어 기록됩니다.
secureLogger.info("급여 조회", Map.of(
"employeeId", employeeId,
"rrn", "901234-1234567" // Log Output: 901234-*******
));
}
}DTO 필드에 @Mask 어노테이션을 적용하여 명시적으로 마스킹 정책을 적용할 수 있습니다.
public class EmployeeDto {
@Mask(MaskType.RRN) // 주민등록번호 마스킹
private String residentNumber;
@Mask(MaskType.PHONE) // 전화번호 마스킹
private String phoneNumber;
}누가(Who), 왜(Why), 어떤 행위(Action)를 했는지 명확한 감사 로그를 남깁니다. Controller 메서드에 적용 시 반복적인 감사 로깅 코드를 줄일 수 있습니다.
@AuditContext(
why = "급여 정보 조회", // 조회 목적
whomParam = "employeeId", // 조회 대상 (파라미터명 매핑)
action = AuditAction.READ // 수행 행위
)
@GetMapping("/{employeeId}/salary")
public SalaryDto getSalary(@PathVariable String employeeId) {
// 비즈니스 로직 실행 시, Audit Context 정보가 자동으로 로깅됩니다.
return service.getSalary(employeeId);
}읽기 쉬운 JSON 포맷으로 출력되며, 보안 메타데이터가 포함됩니다.
{
"timestamp": 1734448200000,
"level": "INFO",
"traceId": "abc123",
"message": "급여 조회",
"payload": {
"employeeId": "EMP001",
"rrn": "901234-*******"
},
"integrity": "sha256:a1b2c3d4...",
"encryptedDek": "eyJhbGciOi..."
}- payload: 마스킹 처리된 안전한 데이터
- integrity: 로그 위변조 검증을 위한 해시 값
- encryptedDek: 데이터 복호화를 위한 암호화된 키
