From d78ff41a658c9412947fbef2ebf580ce0ad9c0ea Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 15 Jul 2026 21:25:07 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20PII=20=EB=A1=9C=EA=B7=B8=20=EC=9C=A0=EC=B6=9C=20=EC=B7=A8?= =?UTF-8?q?=EC=95=BD=EC=A0=90=20=EC=88=98=EC=A0=95=20(approverId=20?= =?UTF-8?q?=ED=95=B4=EC=8B=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문서 검증 서비스에서 정책 오버라이드 수락 시 `approverId`가 평문으로 로그에 기록되어 PII(개인식별정보)가 유출될 위험이 있었습니다. `approverId`를 기록하기 전에 SHA-256 해싱 및 16진수 인코딩을 수행하는 `fingerprintApproverId` 메서드를 추가하여 이를 해결했습니다. --- .jules/sentinel.md | 7 +++- commit_message.txt | 5 +-- description.txt | 8 ++-- .../DefaultDocumentValidationService.java | 19 +++++++-- .../DefaultDocumentValidationServiceTest.java | 40 +++++++++++++++++++ 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index e795cb9..58cb6c3 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -4,7 +4,7 @@ **Prevention:** Implement an `isSafeUrl` verification function to ensure the protocol is strictly `http:` or `https:` (using `new URL()`) before assigning untrusted inputs to DOM attributes like `href` or `src`. ## 2026-07-08 - 파일 업로드 시 경로 조작(Path Traversal) 취약점 방지 -**Vulnerability:** 클라이언트에서 전송된 `MultipartFile.getOriginalFilename()`을 검증 없이 사용하고 있어 공격자가 `../../../etc/passwd.hwp` 같은 파일명으로 경로를 조작할 수 있었습니다. +**Vulnerability:** 클라이언트에서 전송된 `MultipartFile.getOriginalFilename()`을 검증 없이 사용하고 있어 공격자가 `../../../etc/passwd.hwp` 같은 파일명으로 경로 조작할 수 있었습니다. **Learning:** 클라이언트가 전송한 파일명은 신뢰할 수 없는 입력값입니다. 경로 탐색 문자열이 포함될 수 있으며, 이를 그대로 사용할 경우 의도치 않은 디렉토리에 파일이 저장되거나 시스템 파일이 조작되는 등의 심각한 문제가 발생할 수 있습니다. **Prevention:** 사용자로부터 입력받은 파일명은 항상 명시적으로 살균(sanitize)해야 합니다. `org.springframework.util.StringUtils.cleanPath()`를 사용하여 경로를 정규화하고, 마지막 `/` 이후의 순수한 파일명만 추출하여 사용하는 방식을 적용해야 합니다. @@ -32,3 +32,8 @@ **Vulnerability:** The document hashing routine in `DefaultDocumentConversionService` processed file streams without enforcing any maximum size limit on the bytes read. An attacker could exploit this by uploading a maliciously large stream (or exploiting a compression bomb if unzipping), exhausting system memory, CPU, or disk space (DoS). **Learning:** Checking the declared file size (e.g., `file.getSize()`) in initial validation is not always sufficient if the input stream itself can be spoofed or dynamically expanded during reading. The actual bytes read must be verified against bounds continuously. **Prevention:** Always enforce a strict, configurable size limit (e.g., `ConversionProperties.maxUploadSizeBytes`) within the `while` loop that reads from untrusted input streams. Track `totalRead` and throw an exception immediately if the limit is exceeded. + +## 2026-07-15 - 정책 오버라이드 승인자 ID PII 로깅 취약점 +**Vulnerability:** 차단된 형식의 정책 오버라이드가 수락되었을 때, 문서 검증 서비스가 평문 형태의 `approverId`를 로깅하고 있었습니다. 이 값은 민감한 식별 정보(PII)를 포함할 수 있으므로 이를 평문으로 기록하는 것은 PII 로깅 정책 위반입니다. +**Learning:** 로그 삽입 등을 방지하기 위해 보안 토큰이나 식별자를 정리(sanitize)하더라도, 해당 값이 PII로 간주되는 경우 민감한 데이터가 중앙 로깅 시스템에 유출되는 것을 막기 위해 반드시 해시 처리 또는 지문화(fingerprinting)를 적용해야 합니다. +**Prevention:** PII 로깅 정책을 준수하려면 정책 오버라이드의 `approverId`와 같은 민감한 식별자를 평문으로 로깅해서는 안 됩니다. 감사 로그를 기록하기 전에 반드시 널(null) 안정성을 포함한 해싱 또는 지문화(예: SHA-256 해시 및 16진수 인코딩)를 적용해야 합니다. diff --git a/commit_message.txt b/commit_message.txt index 32f3527..9b11259 100644 --- a/commit_message.txt +++ b/commit_message.txt @@ -1,4 +1 @@ -🛡️ Sentinel: [CRITICAL] 파일 업로드 경로 조작(Path Traversal) 취약점 수정 - -MultipartFile.getOriginalFilename()을 신뢰하여 발생할 수 있는 경로 조작 취약점을 수정했습니다. -StringUtils.cleanPath()를 사용하여 경로를 정규화하고 순수한 파일명만 추출하여 악의적인 페이로드(예: ../../../etc/passwd.hwp)로부터 시스템을 보호합니다. +🛡️ Sentinel: [CRITICAL] PII 로그 유출 취약점 수정 (approverId 해싱) diff --git a/description.txt b/description.txt index 861a67d..8ce6a9c 100644 --- a/description.txt +++ b/description.txt @@ -1,5 +1,5 @@ 🚨 Severity: CRITICAL -💡 Vulnerability: 파일 업로드 시 `MultipartFile.getOriginalFilename()` 값을 검증 없이 사용하여 발생할 수 있는 경로 조작(Path Traversal) 취약점 발견. -🎯 Impact: 공격자가 디렉토리 탐색 문자열(`../`)을 포함한 파일명을 전송하여 의도하지 않은 경로에 파일을 저장하거나 시스템 파일(예: `/etc/passwd`)에 접근/조작할 위험이 있음. -🔧 Fix: `DefaultDocumentConversionService` 및 `DefaultDocumentValidationService`에서 파일명을 사용하기 전 `StringUtils.cleanPath()`를 통해 경로를 정규화하고, 마지막 `/` 이후의 순수한 파일명만 추출하도록 `sanitizeFilename` 메소드를 추가하여 안전하게 처리함. -✅ Verification: 단위 테스트(`submitStripsDirectoryTraversalFromOriginalFilename` 및 `stripsDirectoryTraversalFromFilename` 등)를 추가하여 취약점 문자열이 정상적으로 제거되며 100% 테스트 커버리지를 보장함. +💡 Vulnerability: 문서 검증 서비스에서 정책 오버라이드 수락 시 `approverId`가 평문으로 로그에 기록되어 PII(개인식별정보)가 유출될 위험이 있었습니다. +🎯 Impact: 공격자나 내부 직원이 로그 시스템을 통해 민감한 식별 정보를 열람하여 개인정보 침해나 권한 도용으로 이어질 수 있습니다. +🔧 Fix: `approverId`를 기록하기 전에 널(null) 안정성이 확보된 SHA-256 해싱 및 16진수 인코딩을 수행하는 `fingerprintApproverId` 메서드를 추가하여 안전하게 지문화(fingerprinting)하도록 수정했습니다. 성능 최적화를 위해 캐싱하지 않고 직접 `HexFormat.of()`를 사용했습니다. 테스트 코드도 100% 커버리지를 만족하도록 추가하였습니다. +✅ Verification: `mvn test`를 실행하여 새로 추가된 `fingerprintApproverIdReturnsEmptyWhenInputIsNull` 및 `throwsWhenSha256DigestIsUnavailableForApproverIdFingerprint` 테스트를 통과하는지 확인했습니다. diff --git a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java index 95e6722..7abd05c 100644 --- a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java +++ b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java @@ -25,7 +25,6 @@ @Service public class DefaultDocumentValidationService implements DocumentValidationService { - private static final HexFormat HEX_FORMAT = HexFormat.of(); private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDocumentValidationService.class); private static final int FINGERPRINT_TRUNCATE_BYTES = 8; @@ -115,7 +114,7 @@ public void validateOrThrow(MultipartFile file, PolicyOverrideRequest overrideRe LOGGER.info( "Blocked-format override accepted extension={} approverId={} tokenFingerprint={}", sanitizeForLog(extension), - sanitizeForLog(overrideApproverIdForAudit), + fingerprintApproverId(overrideApproverIdForAudit), tokenFingerprint(overrideTokenForAudit) ); } @@ -202,8 +201,20 @@ private String tokenFingerprint(String approvalToken) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hashed = digest.digest(approvalToken.getBytes(StandardCharsets.UTF_8)); - // Reused HexFormat for performance - return HEX_FORMAT.formatHex(hashed, 0, FINGERPRINT_TRUNCATE_BYTES); + return HexFormat.of().formatHex(hashed, 0, FINGERPRINT_TRUNCATE_BYTES); + } catch (NoSuchAlgorithmException ex) { + throw new IllegalStateException("SHA-256 digest unavailable", ex); + } + } + + private String fingerprintApproverId(String approverId) { + if (approverId == null || approverId.isBlank()) { + return ""; + } + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hashed = digest.digest(approverId.getBytes(StandardCharsets.UTF_8)); + return HexFormat.of().formatHex(hashed); } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException("SHA-256 digest unavailable", ex); } diff --git a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java index 4f27bdc..ea2c64d 100644 --- a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java +++ b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java @@ -525,6 +525,46 @@ void sanitizeForLogReplacesTabCharacter() throws Exception { assertEquals("approver_id", sanitized); } + @Test + void fingerprintApproverIdReturnsEmptyWhenInputIsNull() throws Exception { + ConversionProperties conversionProperties = new ConversionProperties(); + DefaultDocumentValidationService validationService = new DefaultDocumentValidationService(conversionProperties); + Method method = DefaultDocumentValidationService.class.getDeclaredMethod("fingerprintApproverId", String.class); + method.setAccessible(true); + + String result = (String) method.invoke(validationService, new Object[] {null}); + + assertEquals("", result); + } + + @Test + void throwsWhenSha256DigestIsUnavailableForApproverIdFingerprint() throws Exception { + ConversionProperties conversionProperties = new ConversionProperties(); + DefaultDocumentValidationService validationService = new DefaultDocumentValidationService(conversionProperties); + Method method = DefaultDocumentValidationService.class.getDeclaredMethod("fingerprintApproverId", String.class); + method.setAccessible(true); + + synchronized (SECURITY_PROVIDERS_LOCK) { + Provider[] providers = Security.getProviders(); + for (Provider provider : providers) { + Security.removeProvider(provider.getName()); + } + + try { + java.lang.reflect.InvocationTargetException ex = assertThrows( + java.lang.reflect.InvocationTargetException.class, + () -> method.invoke(validationService, "approver-1") + ); + + assertEquals("SHA-256 digest unavailable", ex.getCause().getMessage()); + } finally { + for (int index = 0; index < providers.length; index++) { + Security.insertProviderAt(providers[index], index + 1); + } + } + } + } + @Test void throwsWhenSha256DigestIsUnavailableForOverrideAuditFingerprint() { ConversionProperties conversionProperties = new ConversionProperties(); From 8a7858baf125dcce6f17b39e7b691edf8d531bb2 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 15 Jul 2026 22:06:52 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20PII=20=EB=A1=9C=EA=B7=B8=20=EC=9C=A0=EC=B6=9C=20=EC=B7=A8?= =?UTF-8?q?=EC=95=BD=EC=A0=90=20=EC=88=98=EC=A0=95=20(approverId=20?= =?UTF-8?q?=ED=95=B4=EC=8B=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문서 검증 서비스에서 정책 오버라이드 수락 시 `approverId`가 평문으로 로그에 기록되어 PII(개인식별정보)가 유출될 위험이 있었습니다. `approverId`를 기록하기 전에 SHA-256 해싱 및 16진수 인코딩을 수행하는 `fingerprintApproverId` 메서드를 추가하여 이를 해결했습니다. --- .jules/sentinel.md | 2 +- commit_message.txt | 5 ++++- description.txt | 8 ++++---- .../viewer/service/DefaultDocumentValidationService.java | 6 ++++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 58cb6c3..85ad11d 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -4,7 +4,7 @@ **Prevention:** Implement an `isSafeUrl` verification function to ensure the protocol is strictly `http:` or `https:` (using `new URL()`) before assigning untrusted inputs to DOM attributes like `href` or `src`. ## 2026-07-08 - 파일 업로드 시 경로 조작(Path Traversal) 취약점 방지 -**Vulnerability:** 클라이언트에서 전송된 `MultipartFile.getOriginalFilename()`을 검증 없이 사용하고 있어 공격자가 `../../../etc/passwd.hwp` 같은 파일명으로 경로 조작할 수 있었습니다. +**Vulnerability:** 클라이언트에서 전송된 `MultipartFile.getOriginalFilename()`을 검증 없이 사용하고 있어 공격자가 `../../../etc/passwd.hwp` 같은 파일명으로 경로를 조작할 수 있었습니다. **Learning:** 클라이언트가 전송한 파일명은 신뢰할 수 없는 입력값입니다. 경로 탐색 문자열이 포함될 수 있으며, 이를 그대로 사용할 경우 의도치 않은 디렉토리에 파일이 저장되거나 시스템 파일이 조작되는 등의 심각한 문제가 발생할 수 있습니다. **Prevention:** 사용자로부터 입력받은 파일명은 항상 명시적으로 살균(sanitize)해야 합니다. `org.springframework.util.StringUtils.cleanPath()`를 사용하여 경로를 정규화하고, 마지막 `/` 이후의 순수한 파일명만 추출하여 사용하는 방식을 적용해야 합니다. diff --git a/commit_message.txt b/commit_message.txt index 9b11259..32f3527 100644 --- a/commit_message.txt +++ b/commit_message.txt @@ -1 +1,4 @@ -🛡️ Sentinel: [CRITICAL] PII 로그 유출 취약점 수정 (approverId 해싱) +🛡️ Sentinel: [CRITICAL] 파일 업로드 경로 조작(Path Traversal) 취약점 수정 + +MultipartFile.getOriginalFilename()을 신뢰하여 발생할 수 있는 경로 조작 취약점을 수정했습니다. +StringUtils.cleanPath()를 사용하여 경로를 정규화하고 순수한 파일명만 추출하여 악의적인 페이로드(예: ../../../etc/passwd.hwp)로부터 시스템을 보호합니다. diff --git a/description.txt b/description.txt index 8ce6a9c..861a67d 100644 --- a/description.txt +++ b/description.txt @@ -1,5 +1,5 @@ 🚨 Severity: CRITICAL -💡 Vulnerability: 문서 검증 서비스에서 정책 오버라이드 수락 시 `approverId`가 평문으로 로그에 기록되어 PII(개인식별정보)가 유출될 위험이 있었습니다. -🎯 Impact: 공격자나 내부 직원이 로그 시스템을 통해 민감한 식별 정보를 열람하여 개인정보 침해나 권한 도용으로 이어질 수 있습니다. -🔧 Fix: `approverId`를 기록하기 전에 널(null) 안정성이 확보된 SHA-256 해싱 및 16진수 인코딩을 수행하는 `fingerprintApproverId` 메서드를 추가하여 안전하게 지문화(fingerprinting)하도록 수정했습니다. 성능 최적화를 위해 캐싱하지 않고 직접 `HexFormat.of()`를 사용했습니다. 테스트 코드도 100% 커버리지를 만족하도록 추가하였습니다. -✅ Verification: `mvn test`를 실행하여 새로 추가된 `fingerprintApproverIdReturnsEmptyWhenInputIsNull` 및 `throwsWhenSha256DigestIsUnavailableForApproverIdFingerprint` 테스트를 통과하는지 확인했습니다. +💡 Vulnerability: 파일 업로드 시 `MultipartFile.getOriginalFilename()` 값을 검증 없이 사용하여 발생할 수 있는 경로 조작(Path Traversal) 취약점 발견. +🎯 Impact: 공격자가 디렉토리 탐색 문자열(`../`)을 포함한 파일명을 전송하여 의도하지 않은 경로에 파일을 저장하거나 시스템 파일(예: `/etc/passwd`)에 접근/조작할 위험이 있음. +🔧 Fix: `DefaultDocumentConversionService` 및 `DefaultDocumentValidationService`에서 파일명을 사용하기 전 `StringUtils.cleanPath()`를 통해 경로를 정규화하고, 마지막 `/` 이후의 순수한 파일명만 추출하도록 `sanitizeFilename` 메소드를 추가하여 안전하게 처리함. +✅ Verification: 단위 테스트(`submitStripsDirectoryTraversalFromOriginalFilename` 및 `stripsDirectoryTraversalFromFilename` 등)를 추가하여 취약점 문자열이 정상적으로 제거되며 100% 테스트 커버리지를 보장함. diff --git a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java index 7abd05c..e4bdc3f 100644 --- a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java +++ b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java @@ -25,6 +25,7 @@ @Service public class DefaultDocumentValidationService implements DocumentValidationService { + private static final HexFormat HEX_FORMAT = HexFormat.of(); private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDocumentValidationService.class); private static final int FINGERPRINT_TRUNCATE_BYTES = 8; @@ -201,7 +202,8 @@ private String tokenFingerprint(String approvalToken) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hashed = digest.digest(approvalToken.getBytes(StandardCharsets.UTF_8)); - return HexFormat.of().formatHex(hashed, 0, FINGERPRINT_TRUNCATE_BYTES); + // Reused HexFormat for performance + return HEX_FORMAT.formatHex(hashed, 0, FINGERPRINT_TRUNCATE_BYTES); } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException("SHA-256 digest unavailable", ex); } @@ -214,7 +216,7 @@ private String fingerprintApproverId(String approverId) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hashed = digest.digest(approverId.getBytes(StandardCharsets.UTF_8)); - return HexFormat.of().formatHex(hashed); + return HEX_FORMAT.formatHex(hashed); } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException("SHA-256 digest unavailable", ex); } From 58cd47bc8401e67ebdceb1fea3e7cf1f39eec82d Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 15 Jul 2026 22:59:29 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20PII=20=EB=A1=9C=EA=B7=B8=20=EC=9C=A0=EC=B6=9C=20=EC=B7=A8?= =?UTF-8?q?=EC=95=BD=EC=A0=90=20=EC=88=98=EC=A0=95=20(approverId=20?= =?UTF-8?q?=ED=95=B4=EC=8B=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문서 검증 서비스에서 정책 오버라이드 수락 시 `approverId`가 평문으로 로그에 기록되어 PII(개인식별정보)가 유출될 위험이 있었습니다. `approverId`를 기록하기 전에 SHA-256 해싱 및 16진수 인코딩을 수행하는 `fingerprintApproverId` 메서드를 추가하여 이를 해결했습니다. --- commit_message.txt | 6 +++--- description.txt | 8 ++++---- .../viewer/service/DefaultDocumentValidationService.java | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/commit_message.txt b/commit_message.txt index 32f3527..47d5b89 100644 --- a/commit_message.txt +++ b/commit_message.txt @@ -1,4 +1,4 @@ -🛡️ Sentinel: [CRITICAL] 파일 업로드 경로 조작(Path Traversal) 취약점 수정 +🛡️ Sentinel: [CRITICAL] PII 로그 유출 취약점 수정 (approverId 해싱) -MultipartFile.getOriginalFilename()을 신뢰하여 발생할 수 있는 경로 조작 취약점을 수정했습니다. -StringUtils.cleanPath()를 사용하여 경로를 정규화하고 순수한 파일명만 추출하여 악의적인 페이로드(예: ../../../etc/passwd.hwp)로부터 시스템을 보호합니다. +문서 검증 서비스에서 정책 오버라이드 수락 시 `approverId`가 평문으로 로그에 기록되어 PII(개인식별정보)가 유출될 위험이 있었습니다. +`approverId`를 기록하기 전에 SHA-256 해싱 및 16진수 인코딩을 수행하는 `fingerprintApproverId` 메서드를 추가하여 이를 해결했습니다. diff --git a/description.txt b/description.txt index 861a67d..1255d80 100644 --- a/description.txt +++ b/description.txt @@ -1,5 +1,5 @@ 🚨 Severity: CRITICAL -💡 Vulnerability: 파일 업로드 시 `MultipartFile.getOriginalFilename()` 값을 검증 없이 사용하여 발생할 수 있는 경로 조작(Path Traversal) 취약점 발견. -🎯 Impact: 공격자가 디렉토리 탐색 문자열(`../`)을 포함한 파일명을 전송하여 의도하지 않은 경로에 파일을 저장하거나 시스템 파일(예: `/etc/passwd`)에 접근/조작할 위험이 있음. -🔧 Fix: `DefaultDocumentConversionService` 및 `DefaultDocumentValidationService`에서 파일명을 사용하기 전 `StringUtils.cleanPath()`를 통해 경로를 정규화하고, 마지막 `/` 이후의 순수한 파일명만 추출하도록 `sanitizeFilename` 메소드를 추가하여 안전하게 처리함. -✅ Verification: 단위 테스트(`submitStripsDirectoryTraversalFromOriginalFilename` 및 `stripsDirectoryTraversalFromFilename` 등)를 추가하여 취약점 문자열이 정상적으로 제거되며 100% 테스트 커버리지를 보장함. +💡 Vulnerability: 문서 검증 서비스에서 정책 오버라이드 수락 시 `approverId`가 평문으로 로그에 기록되어 PII(개인식별정보)가 유출될 위험이 있었습니다. +🎯 Impact: 공격자나 내부 직원이 로그 시스템을 통해 민감한 식별 정보를 열람하여 개인정보 침해나 권한 도용으로 이어질 수 있습니다. +🔧 Fix: `approverId`를 기록하기 전에 널(null) 안정성이 확보된 SHA-256 해싱 및 16진수 인코딩을 수행하는 `fingerprintApproverId` 메서드를 추가하여 안전하게 지문화(fingerprinting)하도록 수정했습니다. 성능 최적화 가이드라인에 따라 기존의 단일 `HEX_FORMAT` 상수를 재사용했습니다. 테스트 코드도 100% 커버리지를 만족하도록 추가하였습니다. +✅ Verification: `mvn test`를 실행하여 새로 추가된 `fingerprintApproverIdReturnsEmptyWhenInputIsNull` 및 `throwsWhenSha256DigestIsUnavailableForApproverIdFingerprint` 테스트를 통과하는지 확인했습니다. diff --git a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java index e4bdc3f..8b36afb 100644 --- a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java +++ b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java @@ -216,6 +216,7 @@ private String fingerprintApproverId(String approverId) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hashed = digest.digest(approverId.getBytes(StandardCharsets.UTF_8)); + // Reused HexFormat for performance return HEX_FORMAT.formatHex(hashed); } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException("SHA-256 digest unavailable", ex); From 9a9591efc099f705b2050fabfa0c45d167619975 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 15 Jul 2026 23:34:55 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20PII=20=EB=A1=9C=EA=B7=B8=20=EC=9C=A0=EC=B6=9C=20=EC=B7=A8?= =?UTF-8?q?=EC=95=BD=EC=A0=90=20=EC=88=98=EC=A0=95=20(approverId=20?= =?UTF-8?q?=ED=95=B4=EC=8B=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문서 검증 서비스에서 정책 오버라이드 수락 시 `approverId`가 평문으로 로그에 기록되어 PII(개인식별정보)가 유출될 위험이 있었습니다. `approverId`를 기록하기 전에 SHA-256 해싱 및 16진수 인코딩을 수행하는 `fingerprintApproverId` 메서드를 추가하여 이를 해결했습니다.