끝까지 당신과 함께: 코드 품질에서 종합 보안까지
보안은 아직도 뜨거운 화제입니다. 거의 매주, 어떤 어떤 회사가 공격을 당해 사용자 수 백만 명의 개인 정보가 누출되었다는 뉴스를 듣게 됩니다.
이와 같이 보안 상 문제가 많이 눈에 띄는 이유는 우리가 보안을 어떻게 취급하고 있는가에 기인합니다. 즉, 우리는 보안을 기기의 개발이 다 끝나고 나서, 마지막에 빗장을 걸듯 추가한다는 개념으로 생각하고 있습니다. 하지만 복잡한 시스템, 특히 임베디드 시스템의 경우, 해커가 갑옷의 구멍을 찾을 수 있는 공격 표면적이 상당히 넓습니다. 만일 대부분의 해커들이 시스템의 장벽을 뚫는 데에 사용하는 수법을 공부해 보신다면, 이들이 가장 좋아하는 수법은 다름 아닌 기기 소프트웨어 상의 허점을 찾아 이를 이용하는 것이라는 사실을 알게 되실 것입니다.
소프트웨어 상의 결함이 해커가 사용할 수 있는 문이라면, 우리는 우리의 코드 품질 관리를 한층 강화해, 이러한 문제를 해결해야 할 것입니다. 그렇지만 이것은 과연 얼마나 심각한 문제일까요? 그리고 이를 해결하기 위해 우리가 할 수 있는 일은 무엇일까요?
코드 취약성- 해커의 손쉬운 먹잇감
코드의 품질 불량은 사실 우리 주변에서 흔히 볼 수 있는 문제로, 잘못된 코딩 습관이 취약점으로 이어진다는 주장의 증거는 아주 쉽게 찾아볼 수가 있습니다. 여러 소프트웨어 공학 전문가들이 오래 전부터 주장해 왔지만, 이러한 문제가 일반 대중에 처음 알려지기 시작한 것은 지난 2001년, Code Red 웜이 마이크로소프트 IIS(인터넷 정보 서비스)의 버퍼 오버플로우를 악용했던 사건부터 입니다. [1] 비록 기록 상에 남아 있는 갖아 오래된 버퍼 오버플로우 공격은 1988년 유닉스 핑거 명령어 상에 이루어진 것이었습니다. 당시의 공격은 일반 시민에 미치는 영향력은 제한적이었으므로, 뉴스에서도 크게 다루지 않았던 것입니다.
Code Red는 대규모 인터넷 속도 저하 사태를 불러왔으며, 뉴스에 온통 도배가 되었던 사건입니다. 그리고 이와 함께, 갑자기 여기 저기서 버퍼 오버플로우 공격이 늘어나기 시작했습니다. 보안 연구원들은 물론, 해커들도 이러한 버그를 여기 저기 다양한 시스템에서 발견하기 시작했습니다. 그 중에는 임베디드 시스템도 포함되어 있었습니다. 이러한 공격에서는 해커가 감염된 시스템 상에서 텍스트, 또는 데이터를 저장하기 위해 고정된 길이의 버퍼를 사용하는 어떠한 코드던 마음대로 실행할 수가 있습니다. 해커는 버퍼 공간을 최대한으로 채운 다음, 정상 버퍼 공간의 마지막에 실행 가능한 코드를 작성합니다. 이어서 공격을 받는 시스템은 버퍼의 마지막 부분에 있는 코드를 실행하게 됩니다. 그렇게 되면 해커는 어떤 짓이던 마음대로 할 수 있게 됩니다. [2]
이와 같은 종류의 공격에 대한 대책이 특히 시급한 이유는 버퍼의 한계를 점검 및 감독하는 행위가 일반적인 코딩 실무에는 포함되지 않았었기 때문입니다. 하지만 mitre.org에서 제공하는 CWE와 같은 다수의 코딩 표준에서는 이와 같은 종류의 취약점은 없는지, 버퍼를 검사할 것을 권장하고 있습니다. [3] 유감스럽게도, 아직도 개발자들이 개발 과정에서 코드 내에 이러한 문제점이 있는지의 여부를 확인해 보는 비중은 높지 않습니다. 때로는 코드 분석 툴의 힘을 빌려야만 개발자가 이러한 문제점을 파악해 고칠 수 있는 경우도 있습니다. 이렇게 간단한 코드 품질 개선 작업을 통해 해커들이 가장 자주 사용하는 공격 방법의 하나를 미연에 저지할 수가 있습니다. 이는 코드의 보안성을 비약적으로 향상시켜 주며, 따라서 코드를 작성할 때에는 코드 내의 버퍼 길이를 확인하고, 규칙에 어긋나지 않도록 관리하는 것이 바람직합니다.
버퍼 오버플로우가 전부는 아닙니다.
그렇지만 버퍼 오버플로우만이 문제가 되는 것은 아닙니다. 부주의한 코드 작성 행태는 시스템 전반의 문제로서, 그로 인해 수없이 많은 보안상 허점이 발생합니다. 해커들은 이러한 허점을 이용해 시스템을 무력화시키지요. SEI(Software Engineering Institute)에서 발표한 한 논문에서는 다음과 같이 한 마디로 이러한 상황을 요약하고 있습니다.
“... 품질 관리 지표는 우수한 품질의 제품 판별을 위한 맥락을 제공하며, 이를 통해 안전성과 보안 결과를 예측할 수 있습니다. 프로그래밍 언어 구조의 부적절한 사용이나 버퍼 오버플로우, 입력 수치의 검증 실패 등CWE에 수록된 수 많은 보안 취약점은 결국 코드 품질 관리 실패, 그리고 잘못된 개발 업무 관행으로 귀결됩니다. 품질을 개선하는 것은 일부 소프트웨어 보안 이슈를 해결하기 위한 필요 조건이 되는 것입니다.” [4]
해당 논문에서는 또한 보안 문제를 일반적인 코드 결함과 동일한 방식으로 해결할 수 있다는 점을 보여주고 있습니다. 이는 상당수의 보안 문제가 결국 소프트웨어 버그에 기인하기 때문입니다. 따라서 개발자 여러분 역시 전통적인 품질관리 기법을 적용해, 전체 보안 문제 중 최소한 일부분에 대해 해결책을 찾을 수 있을 것입니다.
일반적인 소프트웨어 품질 관리 절차에서는 개발자로 하여금 시스템 내에 남아 있는 결함의 수를 예측할 수 있도록 해 줍니다. 그러니 보안 취약점에 대해서도 같은 효과를 얻을 수 있지는 않을까요? SEI에서는 코드의 품질과 보안 간의 수학적인 상관 관계를 제시하는 선까지는 미치지 못합니다. 하지만 여기서는 분명히 소프트웨어 결함의 1%~5%가 보안 취약점에 해당한다고 적고 있으며, 이들이 파악한 증거에 따르면 보안 취약점을 추적할 시 시스템의 코드 품질 수준을 정확하게 예측해 낼 수 있다고 합니다. [4] 이는 코드 품질이 보안을 위해 (충분 조건이 아닌) 필요 조건에 해당한다는 사실을 잘 보여주고 있으며, 보안을 개발 마지막 단계에서 빗장을 채우듯이 해결할 수 있는 문제라는 생각이 얼마나 틀린 생각인지를 입증해 줍니다. 보안은 이와 반대로, 설계 단계부터 코드, 그리고 생산 단계에 이르기까지 프로젝트의 DNA를 통해 내재화 되어야 합니다.
코딩 표준의 유용성
흔히 발생하는 보안 허점의 상당수는 CWE(mitre.org)와 같은 코딩 표준에서 이미 다루고 있습니다. 뿐만 아니라 이러한 표 준에서는 0으로 나누는 에러, 데이터 주입, 루프 불규칙성, 널 포인터 공격, 문자열 파싱 에러 등의 다른 보안 취약점에 대해서도 다루고 있습니다. MISRA C 및 MISRA C++은 또한 코드 내에 보안 취약점이 침투하지 못하도록 안전하고 신뢰성 있는 코드 작성 방식을 장려하고 있습니다. 이들 표준은 통상적으로 공격에 악용되고 있는 취약점 중 상당수를 포착해 낼 수가 있지만, 코드를 작성하는 개발자는 여기서 한 걸음 더 나아가, ‘내가 작성하는 코드를 해커가 어떻게 이용할까?’라는 물음을 스스로에게 던져 보아야 합니다. 과연 어디에 구멍이 있는 것일까요? 혹시 내가 혹시 입력 데이터나 출력 이용 방법에 대해 뭔가 가정하고 있는 것은 아닌가? 여기서 한 가지 원칙은 뭔가를 가정한다면 이러한 가정을 코드 내에 구현해야 한다는 것입니다. 이를 통해 코드 작성 시 예상했던 것 만이 이용될 수 있도록 해야 합니다. 만일 이러한 과정을 건너 뛰게 되면 해커가 이러한 허점을 바로 이용하게 될 것입니다.
그렇다면, 오픈소스 소프트웨어는 어떻게 되는 것일까요? 설계 상에서 오픈 소스 컴포넌트를 사용하려는 경우 주로 근거로 삼는 이유가 “사용을 통해 검증”되었다는 것입니다. 많은 사람들이 쓰고 있다면 안 좋을 리가 없다는 것이지요. 앞서 소개한 SEI 논문에서는 여기에 대해서도 다음과 같이 적고 있습니다.
“오픈 소스가 지닌 가장 큰 장점 중의 하나라고 홍보하고 있는 것이 바라 무료라는 점, 그리고 많은 사람들이 소스 코드를 이미 확인 했으니 보안 문제가 있다면 누군가 당장 눈치를 챘을 것이고, 버그를 고쳤을 것이라는 점입니다. 즉, 벤더에게 의존할 필요가 없어 집니다. 그렇지만 실제로는 체계적이고 일관된 결함 제거 노력이 없는 한, 각종 버그, 특히 보안 버그는 코드 상에서 없어지지 않습니다.” [4]
이를 바꾸어 말하면, SEI의 논문에서는 “사용을 통한 검증”의 주장은 의미가 없으며, 보안에 있어서도 ‘모두의 것은 누구의 것도 아니다’라는 진리를 다시 한 번 일깨워 주고 있다고 하겠습니다. 이 뿐만 아닙니다. 개발자의 테스팅 만으로는 코드를 충분하게 검증할 수가 없다고 합니다. SEI에 따르면 CWE와 같은 코드 품질 표준에서는 일반적인 테스팅 과정에서는 결코 발견할 수 없고, 오직 취약점을 악용하려는 해커 만이 찾아 낼 수 있는 코드 상 보안 허점까지도 발견해 준다고 합니다. [4] 그 증거로, 2020년 5월 퍼듀 대학 연구진들은 리눅스, macOS, 윈도우스, FreeBSD에 서 사용하고 있는 오픈소스 USB 스택에서 26 개의 보안 취약점을 발견해 내기도 하였습니다. [5]보안에 있어서는 코드 품질이 핵심이며 모든 코드가 중요한 것입니다.
코드 분석 툴은 표준에 맞는 코드 작성을 도와줍니다.
코드 품질 문제를 해결하고, 보안을 개선하기 위해 우리가 할 수 있는 일에는 무엇이 있을까요? 답은 간단합니다. 코드 보안 툴을 사용하는 것입니다. 코드 보안 툴은 기본적으로 두 가지 종류가 있습니다. 하나는 정적 분석 툴로서, 어플리케이션의 소스 코드 만을 분석하는 방식이며, 나머지 하나는 런타임(또는 동적) 분석 방식의 것으로 널 포인터나 데이터 주입 방식과 같은 약점의 존재 여부를 파악할 때 사용합니다.
고급 코드 분석 툴은 CWE, MISRA, CERT C등의 표준 부합 여부도 점검합니다. CERT C는 또 다른 코딩 표준으로, 코드 작성 과정에서의 보안성 확보를 위해 개발된 것입니다. 이 3 대 표준을 하나로 모아 사용할 때, 좀 더 쉽게 보안을 확보할 수 있습니다. 이들 간에는 일부 중복되는 규칙도 존재하지만, 각기 고유한 규칙도 보유하고 있어, 코드가 높은 수준의 보안을 유지할 수 있도록 해 줍니다. 이렇게 표준을 사용함으로써 개발자가 최대한의 코드 품질을 확보할 수 있도록 해 주며, 코드 내에 잠재되어 있는 결함도 일부 발견해 주는 효과가 있습니다.
코드 품질은 보안에 직결
코드 품질이 없다면 보안도 없다고 볼 수 있습니다. 그리고 코드 품질에 대해서는 다른 사람에게 책임을 떠 넘길 수도 없습니다. 보안 상 악몽과도 같은 결과를 불러오기 때문입니다. 그래도 희망은 있습니다. 코드 분석 툴을 사용해, 보안 문제로 곤란해지기 전에 미리 검출해 낼 수가 있기 때문입니다. 보안으로 향하는 길은 모두 코드 품질이라는 관문을 지나고 있습니다.
참고문헌
[1] https://www.caida.org/research/security/code-red/
[2] https://malware.wikia.org/wiki/Buffer_overflow
[3] https://cwe.mitre.org/data/definitions/121.html
[4] https://resources.sei.cmu.edu/asset_files/TechnicalNote/2014_004_001_428597.pdf
[5] https://www.techradar.com/news/usb-systems-may-have-some-serious-security-flaws-especially-on-linux