Skip to content

[REFACTOR]: 문서 CRUD 트레이스 로깅 및 requestId 연계 예외 로깅 수정#153

Open
masonkimseoul wants to merge 1 commit intodevelopfrom
refactor/151
Open

[REFACTOR]: 문서 CRUD 트레이스 로깅 및 requestId 연계 예외 로깅 수정#153
masonkimseoul wants to merge 1 commit intodevelopfrom
refactor/151

Conversation

@masonkimseoul
Copy link
Collaborator

변경 사항

  • 문서 도메인 CRUD 트레이스 AOP 로깅 추가
    • 대상: com.wooteco.wiki.document.service., com.wooteco.wiki.organizationdocument.service.
    • 로그 항목: status(SUCCESS/FAILED), action(CREATE/READ/UPDATE/DELETE), class, method, requestId, httpMethod, uri, durationMs, args
    • 실패 시 추가 항목: errorType, errorMessage
  • 예외 로그에 요청 연계 정보 추가
    • WikiExceptionHandler 로그에 requestId, httpMethod, uri, errorCode, message 포함
  • link()를 CREATE로 분류되도록 추가 (post/create/add/link)

@masonkimseoul masonkimseoul requested a review from Minjoo522 March 8, 2026 12:56
Copy link
Collaborator

@Starlight258 Starlight258 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로깅 개선해주셔서 감사합니당 !

return value.substring(0, maxLength) + "...";
}

private enum CrudAction {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@Minjoo522
Copy link
Collaborator

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 PR은 문서 도메인의 CRUD 오퍼레이션에 대한 트레이스 로깅을 추가하고, 예외 로깅 시 요청 관련 정보를 포함하도록 개선합니다. AOP를 활용한 로깅 구현과 구조화된 로그 메시지 포맷은 좋은 접근 방식입니다. 그러나 여러 로깅 구문이 비정제된 사용자 제어 데이터(예: 요청 URI, 예외 메시지, 메서드 인자)를 포함하여 로그 주입에 취약합니다. 이는 공격자가 CRLF 문자를 사용하여 가짜 로그 항목을 주입할 수 있게 합니다. BusinessLogicLogger에는 이미 개행 문자를 대체하는 sanitize 메서드가 있지만, 모든 로깅 필드에 일관되게 적용되지 않고 있습니다. 코드의 일관성과 안정성을 높이고 이러한 취약점을 해결하기 위해 모든 사용자 제공 데이터를 로깅하기 전에 반드시 정제하는 것을 권장합니다.

Comment on lines +52 to +60
log.error(
"api_exception requestId={} httpMethod={} uri={} errorCode={} message={}",
requestInfo.requestId,
requestInfo.httpMethod,
requestInfo.uri,
errorCode,
message,
exception
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

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.

Comment on lines +52 to +62
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
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

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.

Comment on lines +68 to +79
"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
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

Similar to the success log, the error log in traceCrud also logs the unsanitized uri and arguments. This allows for log injection if the query string or method arguments contain CRLF characters. Ensure all user-controlled data is sanitized before being passed to the logger.

Comment on lines +67 to +68
String requestId = request.getAttribute("requestId") instanceof String id ? id : "N/A";
return new RequestInfo(requestId, request.getMethod(), request.getRequestURI());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

예외 로그에 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);

Comment on lines +108 to +115
private boolean startsWithAny(String value, String... prefixes) {
for (String prefix : prefixes) {
if (value.startsWith(prefix)) {
return true;
}
}
return false;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

startsWithAny 메서드는 Java Stream API를 사용하면 더 간결하고 표현력 있게 작성할 수 있습니다. 가독성 향상을 위해 스트림을 사용하는 것을 제안합니다. java.util.Arrays를 import해야 합니다.

    private boolean startsWithAny(String value, String... prefixes) {
        return java.util.Arrays.stream(prefixes).anyMatch(value::startsWith);
    }

if (value != null) {
joiner.add(label + "=" + sanitize(value.toString()));
}
} catch (Exception ignored) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

appendIdentifier 메서드에서 모든 Exception을 잡아 무시하고 있습니다. 이는 예기치 않은 다른 오류까지 숨길 수 있어 좋지 않은 패턴입니다. 리플렉션 관련 예외만 명시적으로 처리하도록 ReflectiveOperationException을 사용하는 것이 더 안전합니다.

        } catch (ReflectiveOperationException ignored) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants