문자열과 해시에 대한 얘기

2021. 11. 7. 11:18c,c++ 기본/next step

320x100

안녕하세요 대학생 개발자입니다.

 

이번 글에서는 표준 c에서 제공하는 문자열과 위험한 부분 그리고 우리는 어떻게 써야되는지에 대한 내용과 해시에 대한 내용을 간단하게 정리하고 넘어가고자 합니다.

 

우선 windows의 visual studio환

경에서 문자열을 쓰다보면 scanf함수가 작동이 안되는 경우를 확인할 수 있습니다.

이렇게 말이죠... 이건 사실 다른 컴파일러에서는 에러사항은 아닙니다.

하지만 windows의 vs가 특별히 안전한 버전인 _s를 붙인 버전을 제공하고 있기 때문인데요...

사실... 문자열의 비교를 위한 strcmp, 그리고 문자열의 길이를 알아내는 strlen과 같은 함수들은 _s 버전이 존재하지 않습니다 그이유는 뭘까요?

우선...  _s가 존재하는 기준에 대해서 알아봐야되겠는데요.

strcpy, strcat, scanf 등과 같이 들어온 버퍼에 대해서 변경이라는 행위를 하는 함수들은 모두 _s가 붙습니다. 그럼 왜 붙냐고요? 이걸 통해서 공격을 할 수 있기 때문입니다. BOF인데요... BOF에 대해서는 굳이 크게 설명하고 넘어가진 않겠습니다. 그냥 버퍼 오버플로우입니다. strcpy를 통해서 30바이트 할당된 버퍼에 50바이트를 적을 수 있는 그런 일이 가능했기 때문에 제공하는겁니다.

 

그럼 우리 개발자들은 저런 함수를 쓰시면 안된다는 아니고요... 좀 위험하긴 하지만 메뉴얼대로만 쓰면 문제가 안되는 부분이라서 괜찮다고 생각 하시는 분도 계시는 반면에 저 처럼 저렇게 예상치 못한 행동을 유발할 수 있는 코드는 싹 제거하고 가는게 속편한 그런 사람도 분명히 있을겁니다.

 

사실 윈도우즈에서는 이런 부분을 생각하고 strcpy_s 함수를 제공하는 것입니다.

이걸 쓰게되면 할당된 버퍼보다 더 많은 문자열이 들어오게 되면 assert에서 터져버립니다.

음......? 그러면 결국 똑같은거 아닌가... 싶으실수도 있는데요...

strcpy를 통해 문제없는것 처럼 행동이 되다가 나중에 bof로 인해 다른 메모리가 침범되고 그 결과를 나중에 알게된다면.. 그러면 디버깅에 매우 곤란한 상황이 발생하겠죠... 그래서 이런 문제가 있다면 차라리 그때 터트리는 것이 좋습니다.

 

하지만 이건 언제까지나 서버개발자에게는 옳지 못한 방법입니다. 만약 서버가 죽어가는 과정에서 최후의 보루로 로그를 남기려고했는데 로그 사이즈가 내가 할당해놓은 버퍼보다 커서(물론 이런일이 있으면 안되겠지만) 거기서 또 터져버린다면..? 이건 어떻게 해야될까요...? 이런 문제가 있죠...

 

사실 윈도우에서는 또다른 함수들을 지원합니다. strsafe.h에 StringCch~이런식으로 시작하는 함수들이 존재합니다.

이런 함수들은 strcpy처럼 오버플로가 되어도 문제가 아닌것 처럼 작성해주지도 않으며, strcpy_s처럼 오버플로가 된다면 바로 예외를 던져버리는 그런 케이스도 생기지 않습니다. 정확히 우리가 원하는 안전한 함수입니다.

행동은 이렇습니다. 내가 가진 버퍼의 길이가 문자열보다 모자라다면 그냥 모자란 만큼만 적는겁니다. 이게 안전한 방법이지 않나요? 굳이 종이를 다썼는데... 적으려고하다가 그 누구도 알아보지 못하는것 보다는... 차라리 알아볼 수 있는데까지 적어놓으면 그다음 줄은 어느정도 예측가능하니까요.

 

다음으로 해시에 대해서 간단하게 설명하고 이번글은 짧게 끝내려고 합니다.

해시는 역시 key와 value로 이루어진 하나의 쌍이죠.

만약 게임을 만드려고 하는데 플레이어가 5000명이고 검색을 해야된다면..?

플레이어가 어떤 기준을 통해서 정렬이 되어있다면... binary search등으로 금방 찾을 수 있을겁니다.

하지만 정렬되어있지 않으면 항상 5000번을 순회하면서 찾아내야겠죠.

그래서 key를 그냥 그사람의 아이디나 닉네임등으로 해둔다면?

그냥 

players[playerID] 를 통해서 바로 접근 가능한겁니다.

 

O(n)을 O(1)로 만들어버리는거죠... 이게 좋은 방법이아닐까요? 물론 그냥 배열에 비해서 공간은 많이 먹을겁니다. 하지만 매우 빨라지잖아요? 이정도면 된거아닌가요??

 

사실 playerID가 key값이긴 하지만... 직접 그 index가 될순없죠.

그 와중엔 당연히 hash function이라고하는 어떠한 값을 도출해내는 함수를 돌려야되는데 이걸 그냥 해싱한다고 표현하겠습니다.

해싱을 하게되면 하나의 인덱스가 나오게 되는데요 그 인덱스가 결국 내 아이디가 저장되어있는 인덱스다 라고 하는거죠. 사실 이부분에서 고민을 해야될것이 많습니다. 해시는 당연히 아무리 잘짜도 충돌이 일어날수 밖에 없는데요... 충돌이 적인 해시가 좋은 해시인겁니다.

 

뭐... 해시를 다양한 곳에서 쓰긴하는데 제가 생각하기에는 역시 패스워드에 해시를 쓰는 것이 가장 흔하게 접할 수 있는 케이스인것같아요.

아마 예전같은 경우에는 db에 패스워드를 저장했을거에요... 그래서 비밀번호 찾기가 가능했지만 요새는 비밀번호 찾기를 지원하는 경우가 별로없어요... 비밀번호를 잊어버리면 그냥 새로 만들어라고 하는거죠.

 

그 이유가 뭘까요.

db에 그냥 비밀번호를 해싱해서 나온 값을 저장하는거에요.

그렇게되면 유저가 패스워드를 입력했을때 그걸 해싱해서 db에 저장된 값과 똑같으면 통과 아니면 논패스에요... 플레이어를 제외하고 그 누구도 이제 패스워드를 알수가 없는거죠. 심지어 게임회사도 모르는거죠...

물론 이런 방법이 100% 완벽한 방법은 아닙니다... 조합가능한 모든 문자열을 때려 맞춰버리는 브루트포스 같은 경우에는 뚫어낼수있는데요... 어... 요새는 패스워드 + salt값을 뿌려서 매우 긴 하나의 문자열로 만든다음에 그걸 해시를 돌리는것같더라구요 혹은 해시를 돌렸던것을 가지고 또 해시를 돌려버리거나... 이러면 브루트포스를 돌리는 입장에서는... 매우 난감해지겠죠... 단위 한자리가 늘때마다 케이스가 어마어마하게 늘어나니까요... 

그래서 여러분들 이제 패스워드를 까먹었을때 비밀번호를 알려주는 회사는 db에 내 패스워드가 있는 회사니까... 조심하시고 패스워드를 새로 만들라는 회사는 거의 패스워드가 유출될 가능성을 0로 보셔도 되기때문에... 심지어 패스워드를 구한다고해도 어디부터 어디까지가 패스워드고 어디까지가 솔트인지도 알수가없어요 ㅋㅋㅋ... 

 

뭐 어떤 해시가 좋은 해시인지에 대한 정보는 검색해보면 많이 나오니까요... 정리는 스킵하겠습니다. 꼭알아야 하는것도 아니고 필요할때 검색해서 즉석에서 쓰시면 될것 같군요.

 

그럼 대충 해시를 정리해보면... 순회목적으로 만든 자료구조는 아니구요. 검색을 하기위한 자료구조이며 키는 어느 타입이든 다가능하고 그 키가 올바르면 value가 튀어나온다 정도입니다. 복잡도는 1이구요. 이정도면 됬을까요 허허

 

자 그럼 오늘도 긴글 읽어주셔서 감사합니다. 그래도 평소보단 글이좀 짧은편입니다.

다들 그럼 안녕히계십쇼

320x100

'c,c++ 기본 > next step' 카테고리의 다른 글

게임서버 이론1  (0) 2021.11.09
시간과 랜덤에 관한 얘기  (0) 2021.11.08
메모리에 대한 얘기  (0) 2021.11.07
바이트 패딩과 캐시에 대한 얘기  (0) 2021.11.06
전처리기, 바이트패딩룰에대한 얘기  (0) 2021.11.01