안녕하세요, Kernel360 FE 1기 크루 양하연 입니다. 오늘 포스팅에서는 외부 API호출하실 때에 주의하실 점에 대해서 제가 이번에 참여한 프로젝트를 예시로 들어 설명드리려고 합니다.

개발자로서 일상적인 작업 중, 예상치 못한 오류에 직면하는 순간들이 종종 있습니다. 최근 저는 고유한 ID에 대해 요청하는 사람별로 다른 Enctpyed Value가 반환되는 상황을 마주했습니다. 암호화 관련 개념을 안다면 이 상황은 전혀 문제가 될 것 없는 아주 당연한 상황이라고 볼 수 있겠지만, 암호화에 대한 지식이 없는 저로서는 상당한 혼란을 겪었습니다. 오늘은 과연 이 상황이 무엇인지에 대한 관련 개념 소개과 더불어, 비슷한 상황에서 어떤 부분을 고려해야 할지에 대해 이야기해보려 합니다.

📣자세한 상황설명

alt text

GET /riot/account/v1/accounts/by-riot-id/{gameName}/{tagLine}

위 API호출문을 잠시 설명하자면, 자신의 닉네임(gaemName)과 지역서버이름(tagLine)을 입력하면 그 유저에 대한 고유한 id값(puuid)을 암호화처리하여 리턴해주는 것이었습니다.

이 호출문을 통해 동료 개발자분은 본인 계정의 puuid를 알아내고, 그 puuid로 게임에 참여한 10경기 유저에 대한 정보를 담은 객체를 mock data로 만들어주셨고, 저는 그 파일을 넘겨받아 작업을 진행하려했습니다. 그런데 자꾸만 값을 찾을 수 없다는 에러가 뜨는 것이었습니다.

🤔에러의 원인을 찾기 위한 삽질..

깃헙 코드가 잘못됐나? -> 아무 문제 없음

github으로 협업을 하고 있는 프로젝트였기 때문에, 에러의 원인을 찾기까지 분명 ‘코드’에 문제가 있을 것이다 라는 추측하에 코드에서 다른 점을 찾아내고자 했습니다.

동일한 코드를 사용하는 환경을 만들기 위해, 최신 코드가 커밋된 github의 코드를 git clone도 시도해보고, yarn과 관련된 환경설정 파일인 .pnp.cjs파일도 지웠다가 다시 설치해보고, 다른 노트북으로 git clone을 다시한번 받아보기도 했습니다.

❗️원인 분석

동료의 계정으로 리턴받은 puuid와 내 계정으로 리턴받은 puuid가 다르기 때문이었다.

수많은 삽질 끝에 찾아낸 원인은…동료가 mock data를 받을 때 사용했던 puuid와 제가 직접 리턴받은 puuid가 달랐기 때문이었습니다.

뒤늦게 비교해본 puuid 값은 아래와 같았습니다.

(혹시모를 보안상의 이슈를 예방하여 만든 의미없는 값입니다.)
동료가 리턴받은 puuid 암호화값 : FDSKLFJSLEXSKWOSSO
내가 리턴받은 puuid 암호화값 : LQAOSJAMXBASLDKWJ

lol이라는 게임서버에 유일무이하게 존재하는 ‘id’에 대한 암호화된 값을 response body로 출력하는 API문이었기 떄문에, 호출하는 계정이 다른 것과 상관없이 동일한 값을 리턴받을 것이라 기대했습니다.

암호화에 ‘해시 알고리즘’이 쓰이는 것이 당연한 것은 아니다.

호출하는 계정이 다른 것과 상관없이 동일한 값을 리턴받을 것이라 기대했던 까닭은, key에 해당하는 value값이 해시값을 사용할 것이라는 전제 때문이었습니다.

제가 일반적인 암호화기법으로 알고있던 “해시값”은 특정 입력 값에 대해 항상 동일한 출력을 제공하는 1대1 대응의 값을 의미합니다. 자료구조를 배울 때도 Dictonary는 해시 알고리즘을 사용한다고 배웠기때문에 너무나도 당연하게 해시값을 기대하였습니다. 그렇지만 이 ‘해시 알고리즘’이 대중적으로 쓰이지 않는 다는 것을 뒤늦게 알았습니다.

‘해시 알고리즘’을 안 쓰는 이유

그렇다면 해시 알고리즘이 잘 쓰이지 않는 것일까요? 해시 알고리즘 자체도 encrypted vlaue를 복호화하여 원래 값을 찾아내는 것은 거의 불가능하다고 생각할지 모릅니다. 그렇지만, 전문적인 해커의 시점에서 특정 유저에 대해 모든 사람이 동일한 값을 받아볼 수 있다면 마음만 먹으면 ‘레인보우 테이블’ 등을 통한 연산으로 쉽게 알아낼 수 있는 보안상의 취약점이 존재합니다.

그렇다면 어떤 식으로 해시 알고리즘이 보완되는가?

  • 계정별 고유한 ‘salt’ 적용

보안상의 이유로, 많은 API에서는 동일한 입력 값에 대해 다른 해시값을 반환하기 위해 ‘salt’를 적용합니다. 이 ‘salt’는 계정별로 다를 수 있기 때문에, 같은 ID라도 각 계정에 맞춰 다른 해시값이 생성됩니다.

  • 사용자 컨텍스트 기반의 해시

또 다른 가능성은 API가 계정 정보나 특정 파라미터를 포함한 상태에서 해시를 생성하는 경우입니다. 예를 들어, 해시 계산 시 사용자 ID나 타임스탬프가 포함되었다면 동일한 ID라도 계정마다 해시값이 다르게 나올 수 있습니다.

이 밖에도 암호화 기법에는 여러 방법이 존재하지만, 핵심은 일반적으로 동일한 대상에 대한 API를 호출하더라도 요청하는 주체에 따라 리턴받는 암호화값이 다른 경우가 일반적이라는 사실입니다.


이와 같이 다양한 암호화 기법에 대해 공부를 한 후 혹시나 LOL 공식문서에서 “계정별로 다른 salt가 적용됨”과 같은 암호화 알고리즘에 대한 안내가 나와있는지 살펴보았지만 아쉽게도 정보를 얻을 수는 없었습니다. 추측건대 사용자별로 다른 값을 리턴하는 것이 아주 보편적인 방법이라 따로 설명할 필요성을 못 느꼈거나, 혹은 보안상의 이유로 공개를 하지 않은 것이라 생각합니다.

이유가 어찌되었건 중요한 것은 이런 암호화 알고리즘에 대해 인지하고 있는 것이겠죠?

🔥결론

이번 경험을 통해, 동일한 ID에 대해 항상 같은 암호화된 값이 반환되지 않을 수 있다는 사실을 배웠습니다. 특히 보안상의 이유로 계정마다 다른 암호화된 값을 반환할 수 있다는 점을 이해하게 되었죠.

기본적인 암호화 기법을 이해하고 있다면 헤매지 않을 것이지만, 저같이 관련 지식이 없는 사람이라면 이렇게 해맬 수도 있다는 것을 보여드렸습니다. 만약 여러분도 동일한 ID에 대한 리턴값에서 원하는 걸 찾기 어렵다는 문제에 직면한다면, 동일한 값일지라도 호출하는 주체에 따라 리턴되는 암호화된 값은 달라진다는 사실을 고려하시고 에러 핸들링 해나가시면 좋을 것 같습니다.

참고

https://auth0.com/blog/adding-salt-to-hashing-a-better-way-to-store-passwords/ </br> https://st-lab.tistory.com/100
https://velog.io/@yoosj97/%EC%95%94%ED%98%B8%ED%99%94-%EA%B8%B0%EB%B2%95-%ED%95%B4%EC%8B%9CHash-%EC%86%94%ED%8A%B8Salt