[REFACTOR]: 문서 CRUD 트레이스 로깅 및 requestId 연계 예외 로깅 수정#153
[REFACTOR]: 문서 CRUD 트레이스 로깅 및 requestId 연계 예외 로깅 수정#153masonkimseoul wants to merge 1 commit intodevelopfrom
Conversation
| return value.substring(0, maxLength) + "..."; | ||
| } | ||
|
|
||
| private enum CrudAction { |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This PR introduces trace logging for document CRUD operations and enhances logging to include request information upon exceptions, aiming to improve observability and overall code quality. However, the current implementation is vulnerable to Log Injection, as untrusted data from the request URI, query string, and exception messages are logged without proper sanitization for newline characters, potentially allowing attackers to inject fake log entries. To mitigate this, the sanitize method in BusinessLogicLogger needs consistent application across all logged fields, and similar sanitization should be implemented in WikiExceptionHandler. Furthermore, for general code quality, WikiExceptionHandler should log the full URI including query parameters, and BusinessLogicLogger could use clearer pointcuts and improved reflection exception handling to enhance stability and performance.
src/main/java/com/wooteco/wiki/global/exception/WikiExceptionHandler.java
Show resolved
Hide resolved
src/main/java/com/wooteco/wiki/global/exception/WikiExceptionHandler.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Code Review
이 PR은 문서 도메인의 CRUD 오퍼레이션에 대한 트레이스 로깅을 추가하고, 예외 로깅 시 요청 관련 정보를 포함하도록 개선합니다. AOP를 활용한 로깅 구현과 구조화된 로그 메시지 포맷은 좋은 접근 방식입니다. 그러나 여러 로깅 구문이 비정제된 사용자 제어 데이터(예: 요청 URI, 예외 메시지, 메서드 인자)를 포함하여 로그 주입에 취약합니다. 이는 공격자가 CRLF 문자를 사용하여 가짜 로그 항목을 주입할 수 있게 합니다. BusinessLogicLogger에는 이미 개행 문자를 대체하는 sanitize 메서드가 있지만, 모든 로깅 필드에 일관되게 적용되지 않고 있습니다. 코드의 일관성과 안정성을 높이고 이러한 취약점을 해결하기 위해 모든 사용자 제공 데이터를 로깅하기 전에 반드시 정제하는 것을 권장합니다.
| log.error( | ||
| "api_exception requestId={} httpMethod={} uri={} errorCode={} message={}", | ||
| requestInfo.requestId, | ||
| requestInfo.httpMethod, | ||
| requestInfo.uri, | ||
| errorCode, | ||
| message, | ||
| exception | ||
| ); |
There was a problem hiding this comment.
The logError method logs the uri and message directly without sanitization. While uri (from request.getRequestURI()) is generally safe in most environments, the message parameter can contain unsanitized user input, especially when handling MethodArgumentNotValidException. If the input contains CRLF characters, an attacker can perform log injection, potentially misleading administrators or bypassing log monitoring systems. It is recommended to sanitize these values by replacing or removing CRLF characters before logging.
| log.info( | ||
| "crud_trace status=SUCCESS action={} class={} method={} requestId={} httpMethod={} uri={} durationMs={} args={}", | ||
| crudAction, | ||
| className, | ||
| methodName, | ||
| requestInfo.requestId(), | ||
| requestInfo.httpMethod(), | ||
| requestInfo.uri(), | ||
| durationMs, | ||
| arguments | ||
| ); |
There was a problem hiding this comment.
The traceCrud method logs the request URI and method arguments, which may contain unsanitized user input. The uri includes the query string (see lines 185-189), and the arguments string includes CharSequence arguments that are not sanitized (see lines 136-138). If these contain CRLF characters, an attacker can inject fake log entries. Although a sanitize method exists in this class, it is not applied to the uri or to CharSequence arguments. It is recommended to apply sanitization to all user-controlled data before logging.
| "crud_trace status=FAILED action={} class={} method={} requestId={} httpMethod={} uri={} durationMs={} errorType={} errorMessage={} args={}", | ||
| crudAction, | ||
| className, | ||
| methodName, | ||
| requestInfo.requestId(), | ||
| requestInfo.httpMethod(), | ||
| requestInfo.uri(), | ||
| durationMs, | ||
| throwable.getClass().getSimpleName(), | ||
| sanitize(throwable.getMessage()), | ||
| arguments | ||
| ); |
There was a problem hiding this comment.
| String requestId = request.getAttribute("requestId") instanceof String id ? id : "N/A"; | ||
| return new RequestInfo(requestId, request.getMethod(), request.getRequestURI()); |
There was a problem hiding this comment.
예외 로그에 URI를 기록할 때 쿼리 문자열(query string)이 누락되어 있습니다. 디버깅 시 전체 요청 URI 정보가 중요할 수 있으므로, BusinessLogicLogger에서와 같이 쿼리 문자열을 포함하는 것이 좋습니다. 이렇게 하면 로깅 정보의 일관성도 개선됩니다.
String requestId = request.getAttribute("requestId") instanceof String id ? id : "N/A";
String uri = request.getRequestURI();
String queryString = request.getQueryString();
if (queryString != null && !queryString.isBlank()) {
uri += "?" + queryString;
}
return new RequestInfo(requestId, request.getMethod(), uri);| private boolean startsWithAny(String value, String... prefixes) { | ||
| for (String prefix : prefixes) { | ||
| if (value.startsWith(prefix)) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } |
There was a problem hiding this comment.
| if (value != null) { | ||
| joiner.add(label + "=" + sanitize(value.toString())); | ||
| } | ||
| } catch (Exception ignored) { |
변경 사항