테스트 코드는 필요없지 않나?

2020. 9. 11. 00:14_

테스트 코드 짤 시간에 비즈니스 로직을 짜는 게 엔지니어를 잘 활용하는 방법 아닌가?

굳이 기능도 아닌데 테스트 코드를 짜는 게 의미가 있나?

주의

제목과 서두가 자극적이다. (제목과 서두만 자극적일 수 있다)

 

 

"비즈니스 로직이 급한데 테스트 코드가 굳이 필요한가?"

그렇게 생각할 수 있다. 그리고 실제로 그럴 수 있다. 빠르게 제품을 만들어내고 문제점을 찾아내야 하는데 테스트 코드만 작성하다가는 투자금은 몽땅 날리고 제품은 최소 실행 가능한 수준도 안되고 엔지니어는 지치는 상황이 발생할 수 있다.

 

비슷한 예로, 백오피스는 관리자가 사용할 부분인데 굳이 이 기능에까지 테스트 코드가 필요한가? 에 대해 아래 글에서 언급한 그 multiple vulnerability에 대한 글과 함께 테스트 코드가 필요한지에 대해 말해볼 것이다.

https://pjongy.tistory.com/entry/Reflected-XSS는-더-이상-유효하지-않은가

 

 

정답은 보장 못한다.

 

이 취약점은 gnuboard 5.3.2.1 버전에서 발생했던 취약점이고, 실제로 위 글에서 언급한 Reflected XSS와 엮어 공격을 진행했다.

(기본적으로 krcert에서는 패치 후 4달 이후에 공개할 수 있고, 해당 취약점이 완전히 패치된 커밋이 올라온 후 이미 4달은 지났다.)

 

$sst, $sv는 필터하던데 $st는 안하더라

gnuboard의 경우 REQUEST에 전달된 인자들을 변수에 그대로 맵핑하기 때문에 $_GET['st']이런 코드 없이도 $st만으로 REQUEST의 st를 가져올 수 있는데, 이 경우 $st 변수가 $sql_search에 저장되어 그대로 SQL 쿼리에 흘러들어 가기 때문에 취약함을 확인할 수 있다.

 

하지만 이 코드는 /adm/sms_admin/history_num.php 에 위치해 있으며 도달하기 위해서는 다음 조건이 필수적으로 만족되어야 한다.

  • SMS가 그누보드 서비스에 설치되어 있어야 한다. (서비스 등록은 관리자만 진행할 수 있다.)
  • 관리자 권한이 있어야 한다.

 

1. SMS가 그누보드 서비스에 설치되어 있어야 한다.

 

먼저, SMS 서비스가 설치되지 않은 공격 대상일 수 있기 때문에 이걸 설치하기 위해 구조를 파악해봤는데, SMS 서비스 등록은 관리자의 세션과 함께 /adm/sms_admin/install.php 에 접근하는 것으로 완료 가능했다.

<img src='/adm/sms_admin/install.php' />

아-주 간단한 CSRF로 서비스를 설치한다. (sms_admin install 기능은 idempotent 기능이라 여러 번 실행해도 문제없다.)

 

2. 관리자 권한이 있어야 한다.

 

다음으로 관리자 권한이 있어야 하는데, 이 부분에서는 Reflected XSS는 더 이상 유효하지 않은가 에서 언급한 gnuboard 5.3.2.1의 reflected xss를 활용했다.

 

이렇게 gnuboard 5.3.2.1의 풀 익스플로잇을 완성하고 krcert를 통해 취약점을 제보했다.

 

 

그리고 얼마 후...

 

패치 커밋이 푸시되었다.

https://github.com/gnuboard/gnuboard5/commit/4cc8284016941159ea0f0c4848c3a55982ebec2f

ㅇㅇ 님들이 $st가 취약하댔죠? 얘기한대로 $st 변수에 필터링 추가했음

아마 권고가 내려진 후 조치를 취하고 패치 완료했다고 회신했을 것이다.

 

 

패치가 공개되고 다시 얼마 후...

 

ㅇㅇ 취약점 맛집이네

취약점 패치에 대해 검증을 받는 프로세스가 있는지는 정확히 모르겠다. 아마 다들 바빠서 기관이 직접 코드 리뷰 해주거나 제대로 고쳐졌나 확인할 것 같지는 않고 내부적으로 할 수도 있긴 한데 이건 잘 모르겠다.

 

근데 님들 왜 필터링이 변수 할당 아래에 있음???

취약점 패치 커밋 내용을 보니 $st 변수를 필터링하는 내용이 $sql_search를 생성한 이후에 진행되도록 코드를 수정했다.

 

$st를 이미 쓸 곳에 다 사용한 후에 $st를 필터링한다

이미 $sql_search에 $st 변수로 값은 다 복사하고 $st에 필터링을 진행한다. 근데 아래에서는 이미 만들어진 $sql_search를 사용해서 쿼리를 생성하니, 제대로 된 패치가 아니라고 할 수 있겠다.

 

보안에 대한 깊은 이해가 있는 개발자들은 그리 많지 않다. 아마 그 정도 경력을 갖췄다면 실무진보다는 헤드급으로 위치할 가능성이 높다. 개발자는 개발자의 역할이 있는 거고 보안 전문가는 보안 전문가의 역할이 있는 것이란 생각이 만연하니까. 그런데 $st 변수로 인해 취약점이 발생하는 문제가 있으니 그쪽에 필터링을 적용하라는 식으로 전달하고 마무리 지어버리면 그 원리를 이해할 수 없는(혹은 않는) 개발자가 어떻게 보안 전문가의 요청대로 정확히 패치할 수 있을까 그러한 리스크 또한 관리하는 것이 진정 보안을 전문적으로 다루는것이 아닐까 라는 생각을 들게 한 부분이었다.

 

사실 이 취약점은 패치가 잘 되었나 확인할 겸 보다가 패치 커밋 보고 다시 제보했는데, 앞서 "해당 취약점이 완전히 패치된 커밋이 올라온 후 이미 4달은 지났다"라고 말했던 이유는 여기 있다. 5.4.2.1 버전의 해당 취약점 제보 이후 바로 패치된 것이 아니라 다시 발견되고 제보된 5.3.2.4 버전의 "완전한" 패치 이후 4달이 지났다는 의미였다.

 

다시 돌아가서, gnuboard 5.3.2.1 상황에 이미 테스트 코드가 작성되고 관리되고 있었다고 가정해보자.

어떠한 이유로 취약점이 발견되었고, 그 취약점에 대해 패치했다면 아마 다음과 같은 구성의 테스트 코드가 추가되었을 것이다.

test block( 'Should ignore $st variable if unacceptable value at $st') {
  unacceptable_values = list('a\'', 'nonono', '123');
  for (st in unacceptable_values){
    generated_query = generate_query(st=st);
    assert(generated_query, 'SELECT ...')
  }
}

test block( 'Should populate query if acceptable value at $st') {
  acceptable_values = list('hs_name', 'hs_hp', 'bk_no');
  for (st in acceptable_values){
    generated_query = generate_query(st=st);
    assert(CHECK ST VARIABLE IS APPLIED)
  }
}

그리고 이 테스트가 성공함을 보장할 때, 잘못된 패치로 인해 5.3.2.4에서 다시 발생한 동일한 취약점이나, 그 미래에서 어떠한 이유(아마 리팩터링이겠지만)로 아래 코드가 사라지게 되어도 취약하지 않음을 보장할 수 있을 것이다.

 

모양새가 바뀌더라도 파라미터에 대해 필터는 어쨋든 필요할 것이다.

 

이 취약점들은 분명 관리자 기능에서 발생했고, 공격을 위한 pre-requisite도 필요한 상황이었지만, unrechable로 보이는 포인트( 당연히 실제로 unrechable인 문제 로직도 있다. 개인적으로 이런 경우는 다른 쪽의 코드가 업데이트되면서 취약지점으로 가는 경로가 만들어질 때까지 유지한 후 혹시라도 운이 좋게 경로가 만들어지면 공격을 완성한다 )는 다른 구성요소와 함께 극복할 수 있었다.

 

테스트 코드가 필요할까? 매번 초기 실행 가능 제품만을 만들 것이라면 테스트 코드는 필요 없다. 대부분의 경우 그렇지 않을텐데 테스트 코드 부재는 제품 개발 시간에 지수적으로 증가하는 작업 스트레스가 될 것이다.

 

관리자 기능에서도 테스트 코드가 필요할까? 상황에 따라 다르긴 하나 분리해서 생각하기보다는 일반적인 로직과 같이 신경 쓰는 것이 좋을 것이다.

 

언제나 그렇듯 엔지니어링에 silver bullet은 없다. Test Driven Development가 부상하는데 항상 현실적인 어려움으로 나오는 내용이 "잦은 요구사항(비즈니스 로직) 변경으로 인한 테스트 코드 유지의 어려움"이다. 빠르게 내놓고  앞서 말한 대로 기능에 대한 검증이 완성되고 로직이 안정화된 상태라면 TDD를 도입하던지 뭐 어떻게 하던지 테스트 코드를 잘 관리해야한다.

 

여유가 있어 테스트 코드를 작성하는 것이 아니라 여유를 내서 테스트 코드를 작성해야 할 것이다.

'_' 카테고리의 다른 글

인재 유치에 대하여  (1) 2021.03.03
Is ORM really fxxk  (0) 2020.06.22
안전한 사용자 인증은 어떻게 만들어지는가  (0) 2020.01.12
Reflected XSS는 더 이상 유효하지 않은가  (0) 2019.10.18