어느 화창한 아침, 나는 노트북 앞에 앉아 C# 코드를 재구성하고 있었다. 모든 게 잘 진행되고 있고, 생산적인 하루가 될 거예요. 그런데 상수 문자열 문자에 너무 많은 동호를 추가해서 상황이 폭발했습니다. 생산성은 사라졌습니다. 일요일의 차분한 재건은 지나갔습니다. 심지어 태양도 구름 뒤에 숨기로 결정했다.
30분에서 40분 정도 내가 뭘 잘못하고 있는지 고민한 끝에, 그게 내 잘못이 아니라는 걸 깨달았다. 마이크로소프트입니다. Base64 디코딩 함수에서 오래된 버그를 발견한 것 같습니다.변환.Base64String에서 변환이 버그는 2003년 .NET 1.1이 도입된 이후로 확실히 존재해 왔습니다. 와우! 그거 오래됐네. 그리고 재현하는 것도 그리 어렵지 않습니다. 잘 했어요:
기술적으로 이건 불법 베이스64야. 법적 표현은 "abc="입니다. 참고로 채우기 캐릭터는 단 한 명뿐입니다 =. Base64 인코딩은 ASCII 문자 하나로 6비트 이진 입력을 나타냅니다. 즉, Base64로 인코딩된 문자열의 4문자마다 3바이트를 의미합니다. 인코딩된 데이터가 3바이트의 배수가 아닐 경우, Base64 인코더는 4바이트의 배수로 만들기 위해 채우기 문자를 추가합니다. 이렇게 하면 "abc="로 제대로 채워진 Base64 문자열이 생성됩니다. 또 다른 =을 추가하면 무효화됩니다.
Base64 "abc="는 두 바이트로 디코딩됩니다 [105, 183]. 맞아요. 끝에 패딩 문자를 추가한다고 해서 인코딩된 값이 크게 바뀌지는 않을 것입니다. 문장 끝에 띄어쓰는 것과 같아요. 네, 그 말이 있긴 하지만, 문장의 의미를 바꾸지는 않습니다. 하지만 .NET은 그렇게 생각하지 않습니다. "abc=="는 바이트 [109]로 디코딩됩니다. 입력 거리를 길게 만들었는데, 이상했어요. 또한 달라졌다. 첫 바이트는 105에서 109로 이어집니다. 예외도 없었다. 또 다른 = 를 더하면 예외가 됩니다. 신기하다!
코드:
출력:
'abc=' -> [105, 183] 'abc==' -> [109] 수년간 아무도 이걸 눈치채지 못했다는 게 정말 놀랍습니다. 또는 발견되었지만 고치지 않은 경우도 있습니다. Base64는 네트워크 내 정보 교환에서 매우 중요합니다. 어디서나 볼 수 있습니다. 하지만 .NET은 결함이 있는 Base64 디코더를 수년간 없애지 않았습니다.
처음에는 믿기지 않아 조사하기 시작했습니다. 한동안 구글링을 했지만 별다른 정보를 찾지 못했습니다. 그 후 StackOverflow에 글을 올렸지만 별다른 성과를 얻지 못했습니다. 무슨 일이 일어나고 있는지 알게 된 후에는, 심지어 제 질문에 답해야 했습니다. GitHub에서 한참 검색하다가 2018년 7월에 .NET Core에서 만들어진 수정 패치를 우연히 발견했습니다. 최신 .NET Core 버전은 이 문제를 올바르게 처리하며 예외를 던집니다:
처리되지 않은 예외: System.FormatException: 입력이 유효한 Base-64 문자열이 아니며, 비기본 64 문자, 두 개 이상의 패딩 문자, 또는 그 중 불법 문자를 포함하고 있습니다. 캐릭터를 채우는 것. System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength) System.Convert.FromBase64String(String s) ./base64/Program.cs:13행의 Program.DecodeAndPrint(String base64) ./base64/Program.cs:8행의 Program.Main() 문제를 찾고 해결하는 데 약 15년이 걸렸습니다. 흥미롭게도, 아무도 특별히 고치려고 하지 않았습니다. 코드를 다시 작성해 더 빠르게 만들었을 때 이런 일이 일어났습니다:
Convert.FromBase64()에는 문자열 끝에 두 번째로 불법적으로 패딩된 문자가 끝에서 두 번째 문자를 제거하여 디코딩이 "성공"하는 미묘한 버그가 있습니다.
우리는 . 이 버그는 NetCore 2.1에서 API가 최적화되면서 의도치 않게 수정되었습니다. 오류를 기록하는 테스트를 추가하고 후퇴하지 않도록 하세요. 이 문제는 .NET Core 2.2에서 수정되었습니다. 하지만 최신 버전의 .NET Framework 4.7.2에서도 여전히 문제가 있습니다. 모노에서도 고장 난 것 같아요.
.NET 4.7.2의 우회 방법은 잘못 채워진 문자열을 다음과 같은 것으로 다시 채우는 것입니다:
원문 언어:하이퍼링크 로그인이 보입니다.
|