Skip to content

fix: reactive 검증기 6종이 행종결자로 끝나는 입력을 통과시키는 문제 수정 - #293

Open
z3rotig4r wants to merge 1 commit into
eGovFramework:mainfrom
z3rotig4r:fix/reactive-validation-anchor-bypass
Open

fix: reactive 검증기 6종이 행종결자로 끝나는 입력을 통과시키는 문제 수정#293
z3rotig4r wants to merge 1 commit into
eGovFramework:mainfrom
z3rotig4r:fix/reactive-validation-anchor-bypass

Conversation

@z3rotig4r

Copy link
Copy Markdown
Contributor

문제

Presentation/org.egovframe.rte.ptl.reactive의 검증기 6종은 ^…$로 양끝을 고정한 패턴을 컴파일해 두고 matcher.find()로 검사합니다. Java 정규식의 $는 입력 끝뿐 아니라 마지막 행종결자 바로 앞에서도 매치되므로, 개행으로 끝나는 입력이 패턴 검사를 통과합니다. 통과한 뒤의 동작은 두 갈래로 갈립니다.

법인등록번호(EgovCnCheckValidation)와 주민등록번호(EgovRrnCheckValidation)는 검사숫자를 Integer.parseInt(mValue.substring(12))로 파싱하는데 이때 개행이 함께 넘어가 NumberFormatException이 발생합니다. ConstraintValidator.isValid에서 던진 예외는 false로 수렴하지 않고 ValidationException으로 올라가므로 400이 아닌 500 응답이 나갑니다. 입력 예는 "1101110000002\n", "9913321123459\n"입니다.

휴대전화번호·일반전화번호·영문·한국어 검증기(EgovMobilePhoneCheckValidation·EgovPhoneCheckValidation·EgovEnglishCheckValidation·EgovKoreanCheckValidation)는 개행이 붙은 입력을 true로 판정합니다. "01012345678\n", "0212345678\n", "abc\n", "\n"이 모두 검증을 통과합니다.

수정

여섯 파일의 matcher.find()matcher.matches()로 바꿨습니다. 파일별 변경은 각 1줄이고 정규식 패턴 문자열은 손대지 않았습니다(Pattern.compile 라인 diff 0).

find()를 쓰는 곳은 upstream/main 기준으로 이 패키지 전체에서 7곳입니다. 비밀번호·이메일·IP 검증기는 이미 matches()를 쓰고 있고 EgovNullCheckValidation은 정규식을 쓰지 않아 변경 대상이 아닙니다. 남은 한 곳인 사업자등록번호 검증기는 아래 범위 항목에 적었습니다.

원인과 조치가 동일하고 파일별 변경이 각 1줄이라 6종을 한 PR로 묶었습니다. 나누면 같은 내용의 PR이 여섯 개가 되어 검토 부담만 늘어납니다.

하위 호환성

수정 전후 구현을 각각 컴파일해 동일 입력 132,609건을 검증기 7종에 통과시켜 판정을 대조했습니다. 입력은 정상값·하이픈 표기·앞뒤 공백·길이 오류·빈 문자열·행종결자 변형과 고정 시드 무작위 문자열로 구성했습니다.

판정 928,263건 중 달라진 것은 849건(0.09%)이고 전부 하이픈을 제거한 뒤 말미가 행종결자 하나로 끝나는 입력입니다.

판정 변화 건수
truefalse (검증 우회 차단) 557
NumberFormatException 전파 → false 292
falsetrue 0

하이픈 표기(110111-0000002·010-1234-5678)는 기존과 같이 통과하고 앞뒤 공백·길이 오류·빈 문자열 판정도 변화가 없습니다. 영문·한국어 검증기가 빈 문자열을 true로 판정하는 기존 계약도 그대로입니다. 기존에 통과하던 값의 집합은 줄지 않고 행종결자로 끝나는 입력만 걸러집니다.

범위

사업자등록번호 검증기(EgovCrnCheckValidation)도 같은 결함을 갖지만 가중치 배열 오류를 함께 고치는 별도 PR(#292)에서 matches()로 바꿉니다. 두 PR은 서로 다른 파일과 테스트를 건드려 머지 순서에 제약이 없고 양방향 병합 시뮬레이션에서 충돌이 없음을 확인했습니다.

검증

행종결자로 끝나는 입력을 예외 없이 false로 반환하는지 확인하는 ReactiveValidatorsLineTerminatorTest를 추가했습니다(4건 통과). find()로 되돌리면 3건이 실패하며 6종의 결함이 모두 재현됩니다. 개행 \n·\r\n과 유니코드 행종결자 U+2028을 함께 다룹니다. 정상값·하이픈 표기·체크숫자 오류 판정이 그대로인지도 같은 테스트에서 확인합니다.

모듈 전체 mvn clean test는 16건 통과합니다. 위 사업자등록번호 브랜치를 임시로 병합한 상태에서는 24건이 통과합니다.

reactive validation 패키지의 검증기 6종이 정규식 검사에 Matcher.find()를
사용한다. 패턴은 "^...$" 형태인데 Java 정규식의 $는 입력 끝 또는 최종
행종결자 앞에서 매치되므로, 행종결자(\n, \r\n, U+0085, U+2028, U+2029)로
끝나는 입력이 패턴 게이트를 통과한다.

- EgovCnCheckValidation: "1101110000002\n" -> NumberFormatException 전파
- EgovRrnCheckValidation: "9913321123459\n" -> NumberFormatException 전파
- EgovMobilePhoneCheckValidation: "01012345678\n" -> true
- EgovPhoneCheckValidation: "0212345678\n" -> true
- EgovEnglishCheckValidation: "abc\n" -> true
- EgovKoreanCheckValidation: "가나다\n" -> true

Cn/Rrn은 패턴을 통과한 뒤 마지막 자리를 Integer.parseInt로 파싱하므로
NumberFormatException이 발생한다. ConstraintValidator가 예외를 던지면
false 반환이 아니라 ValidationException으로 전파되어 400이 아닌 500 응답이
된다. 입력은 사용자 제어값이다.

find()를 전체 일치를 요구하는 matches()로 바꿔 행종결자로 끝나는 입력을
거부한다. 패턴 문자열은 변경하지 않았다. matches()에서는 ^/$ 앵커가 남아
있어도 무해하다.

수정 전후 판정을 실측 비교한 결과 정상값·하이픈 포함 값·앞뒤 공백·탭 종료·
길이 초과/미달·빈 문자열 판정은 모두 동일하고, 행종결자로 끝나는 입력만
false로 바뀐다. ReactiveValidatorsLineTerminatorTest로 6종의 행종결자
거부와 정상값 무회귀를 단언한다.
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.

1 participant