Skip to content

Commit dc73fed

Browse files
authored
Merge pull request #290 from DevKor-github/feat/implement-issue-274
[codex] Remove sensitive request logging
2 parents a70ae7f + 90a185a commit dc73fed

48 files changed

Lines changed: 278 additions & 224 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Logging Redaction Policy
2+
3+
Production logs must only contain operational metadata needed to debug routing,
4+
ownership, status, and latency. Request payloads are not safe log data.
5+
6+
## Request Logs
7+
8+
Controller request logging is centralized in `LoggingAspect` and
9+
`RequestLogPolicy`. Request logs must include only:
10+
11+
- request ID
12+
- route
13+
- method
14+
- actor identifier
15+
- client IP
16+
- response status
17+
- timing in milliseconds
18+
19+
The request logger must not inspect or render `@RequestBody` arguments.
20+
21+
## Sensitive Fields
22+
23+
Never log values or raw key/value payloads for credentials, OAuth material,
24+
profile text, notes, or request bodies. Sensitive key names include:
25+
26+
- `authorization`
27+
- `firebaseToken`
28+
- `password`
29+
- `secret`
30+
- `token`
31+
32+
When field-level logging is genuinely needed, add the field to
33+
`RequestLogPolicy`'s allowlist and keep the log statement metadata-only.
34+
35+
## Regression Guard
36+
37+
`SensitiveLoggingPolicyTest` scans application source for sensitive key names in
38+
logger calls, request-body logging in `LoggingAspect`, and DTO-generated
39+
`toString` methods. Any future exception must update this policy and the
40+
central allowlist in the same change.

ontime-back/src/main/java/devkor/ontime_back/LoggingAspect.java

Lines changed: 27 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -2,130 +2,71 @@
22

33
import devkor.ontime_back.dto.RequestInfoDto;
44
import devkor.ontime_back.entity.ApiLog;
5-
import devkor.ontime_back.repository.ApiLogRepository;
5+
import devkor.ontime_back.logging.RequestLogPolicy;
66
import devkor.ontime_back.response.GeneralException;
77
import devkor.ontime_back.service.ApiLogService;
88
import jakarta.servlet.http.HttpServletRequest;
99
import lombok.RequiredArgsConstructor;
1010
import lombok.extern.slf4j.Slf4j;
11-
import org.aspectj.lang.JoinPoint;
1211
import org.aspectj.lang.ProceedingJoinPoint;
13-
import org.aspectj.lang.annotation.AfterThrowing;
1412
import org.aspectj.lang.annotation.Around;
1513
import org.aspectj.lang.annotation.Aspect;
1614
import org.aspectj.lang.annotation.Pointcut;
17-
import org.aspectj.lang.reflect.MethodSignature;
1815
import org.springframework.http.ResponseEntity;
19-
import org.springframework.security.access.AccessDeniedException;
2016
import org.springframework.security.core.Authentication;
2117
import org.springframework.security.core.context.SecurityContextHolder;
2218
import org.springframework.stereotype.Component;
23-
import org.springframework.web.bind.MethodArgumentNotValidException;
24-
import org.springframework.web.bind.annotation.PathVariable;
25-
import org.springframework.web.bind.annotation.RequestBody;
2619
import org.springframework.web.context.request.RequestContextHolder;
2720
import org.springframework.web.context.request.ServletRequestAttributes;
2821

29-
import java.lang.annotation.Annotation;
30-
import java.util.Map;
31-
32-
3322
@Slf4j
3423
@Aspect
3524
@Component
3625
@RequiredArgsConstructor
3726
public class LoggingAspect {
3827

3928
private final ApiLogService apiLogService;
40-
private static final String NO_PARAMS = "No Params";
41-
private static final String NO_BODY = "No Body";
4229

4330
@Pointcut("bean(*Controller)")
4431
private void allRequest() {}
4532

4633
@Around("allRequest()")
4734
public Object logRequest(ProceedingJoinPoint joinPoint) throws Throwable {
48-
RequestInfoDto requestInfoDto = extractRequestInfo();
49-
50-
// requestTime
35+
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
36+
HttpServletRequest request = attributes.getRequest();
37+
String requestId = RequestLogPolicy.resolveRequestId(request);
38+
RequestLogPolicy.exposeRequestId(attributes, requestId);
39+
RequestInfoDto requestInfoDto = extractRequestInfo(request);
5140
long beforeRequest = System.currentTimeMillis();
5241

53-
// pathVariable, requestBody
54-
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
55-
Object[] args = joinPoint.getArgs();
56-
Annotation[][] parameterAnnotations = signature.getMethod().getParameterAnnotations();
57-
58-
String pathVariable = null;
59-
String requestBody = null;
60-
61-
for (int i = 0; i < parameterAnnotations.length; i++) {
62-
Annotation[] annotations = parameterAnnotations[i];
63-
for (Annotation annotation : annotations) {
64-
if (annotation instanceof PathVariable) {
65-
pathVariable = args[i].toString(); // @PathVariable 값 저장
66-
} else if (annotation instanceof RequestBody) {
67-
requestBody = args[i].toString(); // @RequestBody 값 저장
68-
}
69-
}
70-
}
71-
72-
// responseStatus
73-
int responseStatus = 200;
74-
Object result;
7542
try {
76-
// 실제 메서드 실행
77-
result = joinPoint.proceed();
43+
Object result = joinPoint.proceed();
44+
int responseStatus = 200;
7845
if (result instanceof ResponseEntity) {
7946
ResponseEntity<?> responseEntity = (ResponseEntity<?>) result;
80-
responseStatus = responseEntity.getStatusCodeValue(); // 상태 코드 추출
47+
responseStatus = responseEntity.getStatusCode().value();
8148
}
8249

83-
// 정상 요청 로그 저장
8450
long timeTaken = System.currentTimeMillis() - beforeRequest;
85-
86-
ApiLog apiLog = buildApiLog(requestInfoDto, responseStatus, timeTaken);
87-
apiLogService.saveLog(apiLog);
88-
89-
log.info("[Request Log] requestUrl: {}, requestMethod: {}, userId: {}, clientIp: {}, pathVariable: {}, requestBody: {}, responseStatus: {}, timeTaken: {}",
90-
requestInfoDto.getRequestUrl(), requestInfoDto.getRequestMethod(), requestInfoDto.getUserId(), requestInfoDto.getClientIp(),
91-
pathVariable != null ? pathVariable : NO_PARAMS,
92-
requestBody != null ? requestBody : NO_BODY,
93-
responseStatus, timeTaken);
51+
saveApiLog(requestInfoDto, responseStatus, timeTaken);
52+
log.info("[Request Log] requestId: {}, route: {}, method: {}, actor: {}, clientIp: {}, responseStatus: {}, timeTakenMs: {}",
53+
requestId, requestInfoDto.getRequestUrl(), requestInfoDto.getRequestMethod(), requestInfoDto.getUserId(),
54+
requestInfoDto.getClientIp(), responseStatus, timeTaken);
9455

9556
return result;
96-
97-
} catch (Exception ex) {
57+
} catch (Throwable ex) {
58+
int responseStatus = mapExceptionToStatusCode(ex);
59+
long timeTaken = System.currentTimeMillis() - beforeRequest;
60+
saveApiLog(requestInfoDto, responseStatus, timeTaken);
61+
log.error("[Error Log] requestId: {}, route: {}, method: {}, actor: {}, clientIp: {}, exception: {}, responseStatus: {}, timeTakenMs: {}",
62+
requestId, requestInfoDto.getRequestUrl(), requestInfoDto.getRequestMethod(), requestInfoDto.getUserId(),
63+
requestInfoDto.getClientIp(), ex.getClass().getSimpleName(), responseStatus, timeTaken);
9864
throw ex;
9965
}
10066
}
10167

102-
@AfterThrowing(pointcut = "allRequest()", throwing = "ex")
103-
public void logException(JoinPoint joinPoint, Exception ex) {
104-
RequestInfoDto requestInfoDto = extractRequestInfo();
105-
106-
// exceptionName
107-
String exceptionName;
108-
if (ex instanceof GeneralException) {
109-
exceptionName = ((GeneralException) ex).getErrorCode().name();
110-
} else {
111-
exceptionName = ex.getClass().getSimpleName();
112-
};
113-
// exceptionMessage
114-
String exceptionMessage = ex.getMessage();
115-
// responseStatus
116-
int responseStatus = mapExceptionToStatusCode(ex);
117-
118-
log.error("[Error Log] requestUrl: {}, requestMethod: {}, userId: {}, clientIp: {}, exception: {}, message: {}, responseStatus: {}",
119-
requestInfoDto.getRequestUrl(), requestInfoDto.getRequestMethod(), requestInfoDto.getUserId(), requestInfoDto.getClientIp(), exceptionName, exceptionMessage, responseStatus);
120-
121-
ApiLog errorLog = buildApiLog(requestInfoDto, responseStatus, 0);
122-
apiLogService.saveLog(errorLog);
123-
}
124-
12568
// requestinfo 추출
126-
private RequestInfoDto extractRequestInfo() {
127-
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
128-
69+
private RequestInfoDto extractRequestInfo(HttpServletRequest request) {
12970
String requestUrl = request.getRequestURI();
13071
String requestMethod = request.getMethod();
13172
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
@@ -137,6 +78,11 @@ private RequestInfoDto extractRequestInfo() {
13778
return new RequestInfoDto(requestUrl, requestMethod, userId, clientIp);
13879
}
13980

81+
private void saveApiLog(RequestInfoDto requestInfoDto, int responseStatus, long timeTaken) {
82+
ApiLog apiLog = buildApiLog(requestInfoDto, responseStatus, timeTaken);
83+
apiLogService.saveLog(apiLog);
84+
}
85+
14086
// apilog 생성
14187
private ApiLog buildApiLog(RequestInfoDto info, int responseStatus, long timeTaken) {
14288
return ApiLog.builder()
@@ -149,7 +95,7 @@ private ApiLog buildApiLog(RequestInfoDto info, int responseStatus, long timeTak
14995
.build();
15096
}
15197

152-
private int mapExceptionToStatusCode(Exception e) {
98+
private int mapExceptionToStatusCode(Throwable e) {
15399
if (e instanceof GeneralException ge) {
154100
return ge.getErrorCode().getCode();
155101
}

ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentRequestDto.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
import lombok.Builder;
55
import lombok.Getter;
66
import lombok.NoArgsConstructor;
7-
import lombok.ToString;
87

98
@Getter
109
@Builder
11-
@ToString
1210
@NoArgsConstructor
1311
@AllArgsConstructor
1412
public class AlarmDeviceCurrentRequestDto {

ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceCurrentResponseDto.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import lombok.AllArgsConstructor;
44
import lombok.Builder;
55
import lombok.Getter;
6-
76
import java.time.Instant;
87

98
@Getter

ontime-back/src/main/java/devkor/ontime_back/dto/AlarmDeviceUnregisterRequestDto.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
import lombok.Builder;
55
import lombok.Getter;
66
import lombok.NoArgsConstructor;
7-
import lombok.ToString;
87

98
@Getter
109
@Builder
11-
@ToString
1210
@NoArgsConstructor
1311
@AllArgsConstructor
1412
public class AlarmDeviceUnregisterRequestDto {

ontime-back/src/main/java/devkor/ontime_back/dto/AlarmSettingsResponseDto.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import lombok.AllArgsConstructor;
44
import lombok.Builder;
55
import lombok.Getter;
6-
76
import java.time.Instant;
87

98
@Getter

ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusCurrentResponseDto.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import lombok.AllArgsConstructor;
44
import lombok.Builder;
55
import lombok.Getter;
6-
76
import java.time.Instant;
87
import java.time.LocalDateTime;
98
import java.util.List;

ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusFailureDto.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
import lombok.Builder;
55
import lombok.Getter;
66
import lombok.NoArgsConstructor;
7-
import lombok.ToString;
87

98
@Getter
109
@Builder
11-
@ToString
1210
@NoArgsConstructor
1311
@AllArgsConstructor
1412
public class AlarmStatusFailureDto {

ontime-back/src/main/java/devkor/ontime_back/dto/AlarmStatusReportRequestDto.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@
44
import lombok.Builder;
55
import lombok.Getter;
66
import lombok.NoArgsConstructor;
7-
import lombok.ToString;
8-
97
import java.time.LocalDateTime;
108
import java.time.OffsetDateTime;
119
import java.util.List;
1210

1311
@Getter
1412
@Builder
15-
@ToString
1613
@NoArgsConstructor
1714
@AllArgsConstructor
1815
public class AlarmStatusReportRequestDto {

ontime-back/src/main/java/devkor/ontime_back/dto/AlarmWindowScheduleDto.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import lombok.AllArgsConstructor;
55
import lombok.Builder;
66
import lombok.Getter;
7-
87
import java.time.LocalDateTime;
98
import java.util.List;
109
import java.util.UUID;

0 commit comments

Comments
 (0)