Uway.com
은 대부분의 대학에서 이용하는 원서 접수 대리 사이트이다. 또한 물리 올림피아드
, 수학 올림피아드
등 각종 올림피아드의 수상자 발표 서비스 및 성적 컨설팅 서비스도 제공한다.
때문에 굉장히 많은 학생들의 신상 정보를 포함하기 마련이다. 사실 대한민국의 모든 수험생의 신상 정보를 담고 있다고 해도 과언이 아니다.
지난 8월 13일 한국 물리올림피아드의 수상자가 온라인으로 발표되었다. 발표 페이지는 Uway에서 제작한 대행 서비스를 사용하고 있었다. 주소는 http://ipsi9.uway.com/ksi/kps/pass/1/index.htm
이며, 이 포스트를 작성할 때는 서비스를 중단한 상태였다.
페이지는 단순했다. 성명과 주민등록번호를 입력하고 확인을 누르면 수상 정보를 알려 주는 것이었다. HTML 코드를 분석해 본 결과 form1이라는 폼에 namekor, juminno라는 이름의 Text field와 mode라는 이름의 hidden field가 있었다. method는 POST 방식을 사용하고 있었으며 onsubmit에는 값 검사 함수가 할당되어 있어 각 필드의 유효성을 검사했다.
여기서 두 가지 흥미로운 점을 발견할 수 있었다. 즉, form1의 action 페이지가 설정되어있지 않았다는 점과 mode라는 hidden field의 값은 check로 설정된 채 바뀌지 않는다는 점이었다. action에 주소가 할당되지 않으면 submit시 현재 페이지가 리로딩되며 데이터가 전달된다. 즉, action의 기본값은 현재 페이지이다. 따라서 이 페이지는 한 페이지에서 값의 입력과 출력을 모두 담당하며, 입력 페이지를 표시할 것인지 출력 페이지를 표시할 것인지는 mode라는 변수가 check라는 값을 가지고 있는지를 이용해 판단할 것임을 추측할 수 있다.
기본적으로 POST 방식으로 전송되는 데이터는 변조가 쉽지 않다. 따라서 입력 필드를 통해서만 공격을 해야 되기 때문에 공격이 많이 제한될 수밖에 없다. 특히 필드 검사 함수가 있는 상황에서는 더욱 힘들다. 그러나 만약 GET 방식의 파라미터를 받아들일 수 있다면, 입력 검사 함수를 회피하여 더욱 다양한 형태의 공격이 가능하다. 혹시나 하는 기대를 품고 주소 표시줄에 다음을 입력했다.
(생략)/index.htm?mode=check&namekor=홍길동
mode=check은 입력 페이지가 아닌 출력 페이지를 호출하기 위함이며, namekor 필드를 GET 형식으로 넘기고 있다. 결과는 놀라웠다. 데이터 에러가 아닌 '수상내역 없음' 페이지가 떴다. 알고 있는 수상자의 이름을 입력하여 보았더니 수험 번호와 이름, 주민등록번호, 상급, 여름학교 참가 여부가 정상적으로 표시되었다!
이 공격에서 POST로는 어떠한 값도 넘기지 않았다. 심지어 GET으로도 주민등록번호는 넘기지 않았다. 그러나 이름만 가지고도 정상적인 출력을 했으며, 이는 매우 위험하다.
결과를 통해 이 페이지가 범하고 있는 실수들을 추측해보면 다음과 같다.
- POST value와 GET value를 구분하지 않고 사용한다.
서버사이드 언어를 PHP로 사용했을 경우를 예로 들어 보자. PHP에서 POST value와 GET value에 접근하기 위해서는 각각 $_POST['varname'], $_GET['varname']과 같이 사용해야 한다. 그러나 PHP 배포판의 기본 설정상, POST value와 GET value 모두 단순히 $varname으로도 접근할 수 있다. 만약 POST value에 접근하기 위해 $varname을 사용하였는데 공격자가 POST value 대신 GET value를 전달한다면, 프로그램은 GET value를 POST value로 인식하고 사용할 것이다. 물리 올림피아드 수상자 발표 페이지에 대한 공격이 성공한 결정적인 이유는, POST value 대신 GET value를 넘길 수 있었기 때문이다. 만약 두 값이 구분되었다면, mode를 인위적으로 check로 바꾸기는 힘들었을 것이다.
- 폼 값을 검사하지 않는다.
웹 페이지를 작성하며 가장 하기 쉬운 잘못은 바로 값을 전달할 때 송신자와 수신자 중 한 쪽에서만 검사를 한다는 것이다. 로컬로 실행되는 프로그램은 상대적으로 값이 조작될 가능성이 낮기 때문에 값 전달시 한 쪽에서만 검사하는 것도 큰 문제가 되지 않는다. 그러나 웹 프로그래밍에서 값은 언제 어디서 어떻게 조작될 지 알 수 없으므로 언제나 값을 사용하기 전에는 검사해야 한다. 즉, 폼 값을 전송하기 전에 Javascript를 이용하여 유효성 검사를 했다고 하더라도, 서버사이드에서 이 값을 사용할 때에는 다시 유효성 검사를 해야 한다는 것이다. 물리 올림피아드 수상자 발표 페이지가 서버사이드에서 다시 유효성 검사를 했더라면, juminno 필드가 비어 있는 것을 발견하고 에러를 냈을 것이다.
- 적절한 Query문을 작성하지 않는다.
주민등록번호가 전달되지 않으므로 juminno=""라고 예상할 수 있다. 만약 Query가 적절히 작성되었다면, 주민등록번호가 ""인 데이터는 검색되지 않아야 한다. 예상컨대 Query를 작성할 때 해당 필드가 빈 문자열이 아닐 때에만 조건을 추가하도록 한 것으로 보이며, 이는 이 코드가 재활용되기 때문일 것이다. 어떤 수상자 발표 페이지에서는 이름과 수험번호를, 어떤 페이지에서는 수험 번호와 주민등록번호를 요구하는 등 서로 다른 필드를 요구하기 때문에 빈 문자열이 아닌 경우에만 조건을 추가하도록 한 것이 문제의 원인으로 보인다.
만약 모든 가능한 이름에 대하여 Brute-force 공격(마구잡이 대입 공격)을 시도한다면 1초에 10개의 이름을 처리할 수 있다는 가정 하에 약 900시간만에 모든 이름에 대해 공격을 시도할 수 있으며 한국인의 이름은 주요 성씨에 대부분이 모여 있다는 점을 이용하면 훨씬 짧은 시간 안에도 대다수의 이름에 대해 공격을 시도할 수 있다. 이름만으로 주민등록번호를 포함한 수상 정보를 얻을 수 있다는 것은 매우 위험한 일이다.
9월 초에는 한국 수학올림피아드의 수상자가 또 Uway의 페이지를 통해 발표되었다. 코드를 재활용할 것이라는 예상과 같이, 수학 올림피아드와 물리 올림피아드의 수상자 발표 페이지는 출력 형식을 제외하고는 거의 100% 일치했다.
가장 큰 문제는 보안 의식이다. Uway에서 발생한 문제는 절대 기술적인 문제가 아니다. 공격법이 새로운 것도 아니고, 그렇다고 복잡한 것도 아니다. 심지어 기본적인 PHP 입문서에나 나올 법한 보안 예제이다. 아주 단순하고 초보적인 공격에 개인 주민등록번호를 노출하고 만 것이다. 조금만 보안에 관심을 가졌다면 쉽게 발견하고 고칠 수 있는 문제이다.
문제는 이러한 보안 문제가 이 페이지에만 국한된 것이 아니라는 것이다. 얼마 전 전자신문
에 '학교에서 주민번호가 줄~줄 샌다
'라는 기사가 떴다. Uway와 같이 온라인 서비스가 주된 사업 영역이고, 그 규모도 동종 기업 중 1위라고 자부하는 회사에서조차 너무나 허술한 보안의식으로 심각한 보안 문제가 나타났는데, 학교야 어련하랴 싶은 마음이 들었다. 이제 프로그램을 개발할 때 보안에 신경을 쓰는 것은 필수이다. 프로그래머가 보안을 담당할 수 없다면, 보안 기술자를 고용하는 것이 현명할 것이다. 이 공격법은 주민등록번호 노출에서 그쳤지만, 이와 같은 보안 의식으로 설계한 프로그램이라면 다른 허점도 존재할 것이고, 이는 더 심각한 문제를 야기할 수 있다. 이러한 사태를 맞는 것보다는 보안 분야에 예산을 더 투입하는 것이 훨씬 경제적인 판단이다.
복잡하고 교묘한 공격을 막기는 힘들더라도, 기초적 보안 의식 결여에 의한 보안 허점은 이제 사라지길 바라며 마친다.
퍼가고자 하시는 분은 링크를 달아 주시기 바랍니다.







댓글을 달아 주세요
헐 ㅋㅋㅋ
대단한 해리님~!!!