사전 동적 분석
개념

동적 분석

gabury1고친 사람 github-actions[bot]

동적 분석은 프로그램을 돌려 놓고 그 움직임을 지켜봐서 문제를 찾아냅니다. 코드만 읽어서는 알 수 없던 일이 실행하는 동안 드러납니다. 찾아낸 문제는 눈앞에서 일어난 일이라 믿을 만합니다. 대신 돌려 본 길에서 일어난 일만 알 수 있습니다.

쉽고 빠른 이해

동적 분석은 프로그램을 직접 실행해 무슨 일이 일어나는지 재는 검사입니다. 요청 하나에 쿼리가 몇 번 나가는지, 이미 반납한 메모리를 다시 건드리는 곳이 있는지를 실행 중에 잡아냅니다.

이게 없으면 들어온 데이터에 따라 달라지는 문제는 운영에 나가서야 보입니다. 코드는 멀쩡해 보여도 데이터가 많아지면 쿼리가 쏟아지는 문제가 그렇습니다.

어떻게 도나:

  1. 프로그램 안에 기록용 코드를 심거나, 밖에서 지켜볼 도구를 붙입니다
  2. 테스트나 자동으로 만든 입력을 넣어 프로그램을 돌립니다
  3. 실행 중에 남은 기록을 규칙과 대조해 어긋난 곳을 보고합니다

대가는 둘입니다. 돌려 보지 않은 길의 문제는 끝내 모릅니다. 기록용 코드가 붙는 만큼 프로그램이 느려집니다.

기록용 코드를 많이 심는 검사는 테스트할 때 켭니다. 운영에서는 프로그램을 거의 안 늦추는 관찰만 합니다.

상세

새 차를 점검하는 방법은 둘입니다. 설계도를 펴 놓고 읽는 방법과 차를 몰고 나가 보는 방법입니다. 몰고 나가면 설계도에는 안 보이던 떨림과 소음이 귀에 들립니다. 동적 분석은 몰고 나가 보는 쪽입니다.

동적 분석은 프로그램을 실행해 그 동작을 지켜봅니다. 그렇게 해서 프로그램의 성질을 알아냅니다. 예를 들어 요청 하나를 처리하는 동안 메모리가 얼마나 늘었는지를 잽니다. 요청이 끝났는데도 줄지 않는 메모리가 있으면 어딘가에서 새고 있다는 뜻입니다.

동적이라는 말은 움직이고 있다는 뜻입니다. 멈춰 있는 코드가 아니라 돌고 있는 프로그램을 본다는 것을 가리킵니다. 반대편에는 코드를 돌리지 않고 읽기만 하는 정적 분석이 있습니다.

실행 중에 기록하는 것

지켜보는 대상은 실행 중에 일어나는 일입니다. 무엇을 기록하느냐에 따라 찾아지는 문제가 달라집니다.

기록하는 것 그 기록으로 찾는 문제
함수가 불린 순서와 걸린 시간 시간을 잡아먹는 함수
메모리를 읽고 쓴 주소 이미 반납한 메모리를 다시 쓰는 해제 후 사용 · 배열 끝을 넘어 쓰는 버퍼 오버플로
메모리 사용량이 늘고 준 추이 쓰고 나서 반납하지 않아 쌓이는 메모리 누수
스레드마다 같은 값을 건드린 순서 두 스레드가 순서 없이 한 값을 고치는 경쟁 상태
운영체제에 보낸 요청 프로그램이 어떤 파일을 열고 어디로 접속하나

표의 마지막 줄에서 운영체제에 보낸 요청을 시스템 콜이라고 합니다. 프로그램이 파일을 열거나 네트워크로 데이터를 보낼 때 운영체제에 맡기는 요청입니다. 이 요청만 모아 봐도 프로그램이 바깥과 무엇을 주고받는지가 보입니다.

돌려 봐야 보이는 문제

요청 하나가 사용자 목록을 불러오는 자바 코드를 놓고, 코드만 읽어서는 답이 안 나오는 물음을 보겠습니다. ids 는 앞에서 쿼리 한 번으로 받아 온 사용자 번호 목록입니다.

Java
for (String id : ids) {      // 운영: 5만 개
  users.add(repo.load(id));  // 쿼리 5만 번
}

코드만 보면 반복문 하나에 데이터베이스 조회 하나입니다. 몇 번 도는지는 ids 에 무엇이 들어오느냐가 정합니다. 테스트에서 세 개만 넣었다면 쿼리도 세 번이라 아무 문제가 안 보입니다.

동적 분석은 실행 중에 쿼리가 나갈 때마다 셉니다. 요청 하나에 쿼리가 5만 번 넘게 나갔다는 기록이 남습니다. 목록을 한 번에 읽지 못하고 한 건씩 따로 조회했다는 뜻입니다.

목록을 가져오는 쿼리 1번에 항목마다 1번씩 N번이 더 붙어, 모두 N+1번이 나갑니다. 이렇게 목록 하나에 쿼리가 건수만큼 따라 나가는 문제가 N+1 문제입니다.

이 문제는 코드를 읽는 검사로는 잘 안 드러납니다. 동적 분석이 없으면 데이터가 많은 운영에 나가서야 느려진 응답으로 처음 드러납니다.

관찰을 심는 두 방법

프로그램을 지켜보려면 실행 중에 일어난 일을 밖으로 꺼내 줄 통로가 있어야 합니다. 그 통로를 만드는 방법은 크게 둘입니다. 프로그램 안에 심거나, 프로그램 밖에서 지켜봅니다.

flowchart TD
    subgraph s1["프로그램 안에 심는다 · 계측"]
        A["컴파일러가 번역할 때 기록용 코드를 넣는다"]
        B["실행 파일에 실행하는 순간 기록용 코드를 끼운다"]
    end
    subgraph s2["프로그램 밖에서 지켜본다"]
        C["프로그램이 운영체제에 보낸 요청을 옆에서 기록한다"]
        D["분석하는 쪽이 요청을 보내 돌아온 응답을 본다"]
    end
    s1 --> R["실행 기록"]
    s2 --> R
    R --> S["규칙과 대조해 어긋난 곳을 보고한다"]

안에 심는 방법은 프로그램에 기록용 코드를 끼워 넣는 계측입니다. 메모리를 읽고 쓰는 줄마다 「이 주소가 아직 써도 되는 곳인가」를 묻는 코드를 덧붙이는 식입니다.

계측은 끼워 넣는 시점에 따라 갈립니다. 컴파일러가 코드를 번역할 때 기록용 코드를 함께 넣을 수 있습니다. 다 만들어진 실행 파일에 실행하는 순간 끼워 넣는 방법도 있습니다.

컴파일러가 넣어 주는 검사 장치를 새니타이저라고 합니다. 메모리를 잘못 건드리거나 두 스레드가 한 값을 동시에 고치는 순간 프로그램을 멈춥니다. 그리고 어느 줄에서 그랬는지를 알려 줍니다.

밖에서 지켜보는 방법은 프로그램을 고치지 않습니다. 운영체제에 보낸 요청을 옆에서 기록합니다. 돌고 있는 서비스라면 분석하는 쪽이 요청을 보내 돌아온 응답만 봅니다. 소스 코드가 없어도 됩니다. 프로그램 속 메모리 접근은 밖에서 보이지 않습니다.

어느 방법이든 끝에는 실행 기록이 남습니다. 그 기록을 미리 정해 둔 규칙과 대조해 어긋난 곳을 보고하는 것이 동적 분석의 마지막 단계입니다.

보안에서 쓰는 동적 분석

보안 쪽에서는 동적 분석을 두 모양으로 자주 씁니다. 하나는 돌고 있는 웹 서비스를 두드려 보는 검사입니다. 다른 하나는 의심스러운 파일을 가둬 놓고 돌려 보는 검사입니다.

돌고 있는 웹 서비스에 공격처럼 생긴 요청을 보내 응답을 살피는 검사가 DAST(Dynamic Application Security Testing, 동적 애플리케이션 보안 테스트)입니다. 소스 코드는 보지 않습니다. 밖에서 요청을 보내고 돌아온 응답만으로 판단합니다.

SQL(Structured Query Language, 구조화 질의 언어)은 데이터베이스에 데이터를 달라고 묻는 언어입니다. 웹 서비스는 입력칸에 들어온 값을 SQL 문장의 따옴표 안에 끼워 넣어 데이터베이스에 보냅니다.

예를 들어 입력칸에 따옴표 하나를 넣은 요청을 보내 봅니다. 값을 검사 없이 끼워 넣는 서비스라면 그 따옴표가 문자열을 도중에 닫아 버립니다. 문법이 깨진 SQL 문장을 받은 데이터베이스는 오류를 냅니다. 그 오류가 응답에 섞여 나옵니다.

응답 속 오류는 사용자가 넣은 값이 검사 없이 SQL 문장에 들어갔다는 신호입니다. 그 틈을 노려 입력으로 SQL 문장을 바꿔 치는 공격이 SQL 인젝션입니다.

의심스러운 파일은 바깥과 막힌 실행 환경인 샌드박스에 넣어 돌려 봅니다. 파일이 무엇을 만들고 어디로 접속하는지를 지켜보면 악성 코드인지 가려낼 수 있습니다.

입력이 분석의 폭을 정한다

동적 분석은 넣어 준 입력이 밟게 한 길만 봅니다. 그래서 어떤 입력을 얼마나 넣느냐가 곧 분석이 닿는 범위입니다.

실행 중에 코드의 어느 줄과 어느 갈림길을 지났는지 세는 잣대가 코드 커버리지입니다. 커버리지가 낮으면 동적 분석이 조용해도 안심할 수 없습니다. 안 지나간 줄은 검사를 안 받은 줄이기 때문입니다.

사람이 고른 입력은 사람이 떠올린 경우에 그칩니다. 입력을 자동으로 조금씩 바꿔 가며 수없이 넣어 보는 방법이 퍼징입니다. 퍼징은 흔히 새니타이저를 켠 채로 돌립니다. 이상한 입력이 메모리를 잘못 건드리는 순간을 새니타이저가 바로 붙잡습니다.

밟은 길만 보이는 한계

동적 분석이 잡은 문제는 실행 중에 진짜로 일어난 일입니다. 그래서 문제가 아닌 곳을 문제라고 하는 오탐이 드뭅니다. 보고를 받은 사람이 「진짜 문제인가」를 따지느라 시간을 덜 씁니다.

반대로 안 밟은 길의 문제는 보이지 않습니다. 관리자만 지나는 갈림길에 결함이 숨어 있는 경우를 보겠습니다. 테스트가 일반 사용자로만 돌았다면 동적 분석은 그 결함을 한 번도 못 봅니다.

flowchart TD
    A["요청이 들어온다"] --> B{"관리자 요청인가"}
    subgraph s1["테스트가 밟은 길 · 동적 분석이 본다"]
        C["일반 사용자 처리 · 아무 일도 없다"]
    end
    subgraph s2["테스트가 안 밟은 길 · 동적 분석이 못 본다"]
        D["관리자 처리 · 결함이 숨어 있다"]
    end
    B -->|"아니다"| C
    B -->|"맞다"| D

있는 문제를 못 보고 지나치는 것이 미탐입니다. 동적 분석이 조용하다는 것은 「돌려 본 길에는 문제가 없었다」는 뜻일 뿐입니다.

지켜보는 데 드는 비용

계측으로 심은 코드는 원래 코드와 함께 돌기 때문에 프로그램이 느려집니다. 메모리도 더 씁니다. 그래서 메모리 접근마다 붙는 계측은 테스트 환경에서만 켭니다.

느려진다는 것 자체가 결과를 바꾸기도 합니다. 두 스레드의 실행 순서가 어긋날 때만 터지는 문제는 기록용 코드가 끼어 박자가 바뀌면 사라지기도 합니다. 지켜볼 때만 숨는 이런 버그를 하이젠버그라고 부릅니다.

테스트와 다른 점

단위 테스트도 프로그램을 돌립니다. 테스트는 돌린 결과가 기대한 값과 같은지를 봅니다. 동적 분석은 결과가 맞았더라도 그사이에 무슨 일이 있었는지를 봅니다.

답은 맞게 나왔지만 그사이에 반납한 메모리를 한 번 건드렸다고 합시다. 테스트는 통과합니다. 동적 분석은 그 한 번을 찾아 경고합니다.

둘은 함께 돕니다. 테스트가 입력을 대 줍니다. 동적 분석은 그 입력으로 돈 실행을 지켜봅니다.

정적 분석과 나누는 몫

정적 분석과 동적 분석은 같은 물음에 반대편에서 답합니다. 정적 분석은 모든 길을 넘겨짚습니다. 동적 분석은 몇 길만 확실히 봅니다.

정적 분석 동적 분석
프로그램을 돌리나 안 돌린다 돌린다
보는 범위 코드의 모든 길 입력이 밟게 한 길
흔히 섞이는 잘못 문제가 아닌 곳을 문제라고 한다 있는 문제를 지나친다
필요한 것 코드 돌릴 환경과 입력
잘 잡는 문제 규칙 위반 · 타입 오류 데이터에 달린 문제 · 시간을 잡아먹는 곳 · 메모리 오류

표의 셋째 줄이 둘을 겹쳐 쓰는 까닭입니다. 정적 분석이 의심스러운 곳을 넓게 훑습니다. 동적 분석은 그곳이 실행 중에도 정말 터지는지 확인합니다.

검사를 돌리는 시점

오래 걸리는 검사일수록 개발 앞단에서 돌립니다. 운영에 가까워질수록 프로그램을 덜 늦추는 관찰만 남깁니다.

언제 무엇을 켜나 대가
개발자가 테스트를 돌릴 때 새니타이저를 심은 빌드 테스트가 느려진다
지속적 통합에서 밤마다 퍼징처럼 오래 걸리는 검사 결과가 다음 날 온다
배포 전 검증 환경 돌고 있는 서비스를 두드리는 보안 검사 운영과 닮은 환경을 하나 더 띄운다
운영 중 실행 중인 함수를 가끔씩 표본으로 뜨는 프로파일링 볼 수 있는 깊이가 얕다

운영에서 켤 수 있는 것은 프로그램을 거의 안 늦추는 관찰뿐입니다. 대신 운영에서만 나오는 데이터 양과 요청 몰림을 볼 수 있습니다.

관련 항목

동적 분석이 실행 중에 붙잡는 결함

메모리 누수 · 해제 후 사용 · 버퍼 오버플로 · 경쟁 상태 · 데이터 경쟁 · N+1 문제 · 정의되지 않은 동작 · SQL 인젝션 · 하이젠버그

실행을 지켜보는 기법

계측 · 프로파일링 · 트레이싱 · 퍼징 · 콘콜릭 실행 · 런타임 검증 · 시스템 콜

동적 분석을 수행하는 도구

새니타이저 · 프로파일러 · 디버거 · 샌드박스 · 취약점 스캐너

실행을 일으켜 동적 분석에 입력을 대는 테스트

단위 테스트 · 통합 테스트 · 부하 테스트 · 성능 테스트 · 카오스 엔지니어링

동적 분석을 쓰는 보안 검사

DAST · IAST · 침투 테스트 · 버그 바운티 · 악성 코드 분석

같은 결함을 코드 읽기로 찾는 검증 수단

정적 분석 · 심볼릭 실행 · 형식 검증 · 코드 리뷰 · 타입 검사

분석 결과를 재고 분류하는 잣대

코드 커버리지 · 오탐 · 미탐 · 오버헤드 · CWE · 취약점

운영에서 실행을 지켜보는 관측 수단

모니터링 · 관측 가능성 · 메트릭 · 분산 트레이싱 · 로깅 · APM

다른 이름: dynamic analysis · 동적분석 · 동적 프로그램 분석