책 소개
요약
세상에 드러나 악용되는 소프트웨어 취약점은 대부분 소프트웨어 결함에서 비롯된다. 1988년 이래로 수만 가지 취약점을 분석해온 CERT는 대다수 취약점이 비교적 많지 않은 원인으로 인해 발생한다는 사실을 알아냈다.
이 책에서는 취약점의 원인을 밝히고 취약점이 악용되는 일을 방지하기 위해 취할 수 있는 대책을 설명한다. 프로그래머는 이 책에 나오는 좋은 보안 사례를 읽어 보안에 대한 자세를 갖추고, 현재뿐만 아니라 미래에 일어날 수 있는 소프트웨어 공격을 사전에 예방할 수 있는 방법을 배울 수 있다. 저자 로버트 시코드는 CERT 보고서에서 얻은 내용을 바탕으로, 보안 취약점을 유발하는 프로그램 오류를 찾아내서, 취약점이 어떻게 악용되
는지를 알아보고, 잠재적인 영향을 고찰해보며, 보안상의 대안을 제시한다.
추천의 글
사회가 네트워크 소프트웨어 시스템에 의존할수록 이런 시스템을 겨냥한 공격 횟수는 나날이 증가했다. 또한 이런 공격(정부, 기업, 교육 기관, 개인이 대상)으로 중요 데이터의 파괴와 손상, 시스템 피해, 생산성 하락, 경제적 손실이 발생했다.
오늘날 인터넷에서 행해지는 공격의 대부분은 단순한 골칫거리 수준에서 끝나긴 하지만, 점점 범죄, 테러, 그 외의 악의적인 목적을 띠고 소프트웨어의 취약점을 노리는 일들이 점점 늘어나는 추세다. 최근 통계에 따르면 연간 4,000개 이상의 소프트웨어 취약점이 발견된다고 한다. 이런 취약점은 소프트웨어를 개발할 때 시스템 보호를 적절히 하지 않았거나 보안 결함을 제거하는 데 충분히 집중하지 못했기 때문에 발생한다.
취약점이 증가함에 따라 공격의 복잡성과 효율성도 꾸준히 발전했다. 침입자는 제품에서 발견된 취약점을 파고들 스크립트를 재빨리 개발했으며, 스크립트를 이용해 컴퓨터를 손상시키는 것은 물론, 다른 공격자들이 사용할 수 있게 스크립트를 공유하기까지 한다. 이렇게 공유된 스크립트를 활용한 프로그램이 만들어지면 취약점이 있는 시스템을 자동 검색해 공격하고 손상시키며, 그런 시스템들을 경유해 더 많은 시스템을 공격한다.
매년 막대한 수의 취약점이 발견되므로 관리자들은 기존 시스템을 패치하는 데 더욱 골머리를 앓고 있다. 패치 적용이 어려울 수 있고 예상치 못한 부작용이 있을지도 모른다. 업체가 보안 패치를 내놓은 후에 취약점이 있는 컴퓨터의 90~95%가 패치를 적용하기까지는 수개월, 심지어 수년이 걸릴 수 있다.
인터넷 사용자들은 공신력 있는 보안 관련 인터넷 커뮤니티에 크게 의존한다. 그러나 오늘날 그런 해결 방식은 점점 한계에 부딪히고 있다. 각각의 처리 조직이 절차를 능률적이고도 자동화하는 데 모두 열심이긴 하지만, 상용 소프트웨어 제품의 취약점 수가 너무 많아 아주 뛰어난 조직이 아니라면 이제 해결이 불가능한 수준에 와 있다.
대부분 제품에서 보안이 더 강화됐다고는 볼 수 없다. 즉, 많은 소프트웨어 개발자들은 취약점의 원인으로 얻은 교훈을 이해하지 못하거나 적절한 완화 전략을 펼치지 못하고 있다. CERT/CC가 초기 버전의 제품에서 발견했던 것과 똑같은 종류의 취약점을 이후 버전에서도 계속 발견하는 사실로 미루어 이 점을 알 수 있다.
위 요소들을 하나로 합쳐 생각해보면 실제로 시스템 속도가 빨라진 만큼 단시간 내에 많은 공격으로 상당한 경제적 손실과 서비스 혼란이 발생할 것으로 보인다. 일사불란한 처리가 계속 필요하지만 쉽게 공격 당하지 않게 더 많은 보안 시스템을 갖춰야 한다.
이 책에서는 1988년에 CERT를 설립한 이래로 기록된 가장 흔하고 위험하며 파괴적인 소프트웨어 취약점에 대한 C/C++ 프로그래밍 주요 에러를 다룬다. 이 책으로 프로그래밍 에러를 깊이 있게 기술적으로 분석하고, 침투 위험을 줄이거나 제거하는 데 효과적이고도 실용적인 완화 전략 모두를 배울 수 있다.
1987년 4월, 로버트가 소프트웨어 공학 연구소(SEI, Software Engineering Institute)에 처음으로 합류할 때부터 줄곧 함께 일해 왔다. 로버트는 노련하고도 지식이 풍부한 소프트웨어 엔지니어인데, 소프트웨어 취약점을 세세하게 분석하고 자신이 관찰하고 발견한 내용을 잘 설명했다. 그렇기 때문에 이 책은 소프트웨어 개발자들이 직면하는 가장 일반적인 문제들에 대해 매우 신중한 처리와 실질적 해결책을 제공한다. 로버트는 소프트웨어 개발에 대해 광범위한 배경 지식을 갖고 있어 소스코드 개발 시에 이해득실을 따져야 하는 성능 균형, 유용성, 기타 품질 속성에도 신경을 기울였다. 로버트의 능력에 더해 이 책에는 CERT를 운용하면서 얻은 축적된 지식들이 들어있고, SEI의 CERT/CC 취약점 분석 팀, CERT 운영 스태프, 그리고 편집 및 지원 스태프의 특별한 작업도 들어있다.
- 리차드 페시아(Richard D. Phethia)/CERT 디렉터
이 책에서 다루는 내용
■ C나 C++ 애플리케이션의 전체 보안을 향상시킬 수 있는 방법
■ 불안전한 조작 로직을 무단 이용하는 버퍼 오버플로, 스택 스매싱, 반환 지향적 프로그래밍 공격 등을 무력화시킬 방법
■ 동적 메모리 관리 함수의 잘못된 사용으로 발생하는 취약점과 보안 허점을 예방하는 방법
■ 부호 있는 정수 오버플로, 부호 없는 정수 래핑, 잘림 에러로 인해 발생하는 정수 관련 문제를 제거하는 방법
■ 시큐어 I/O를 수행해 파일 시스템 취약점을 예방하는 방법
■ 형식 문자열 취약점을 만들지 않고 형식화된 출력 함수를 올바르게 사용하는 방법
■ 동시 실행 코드를 개발하는 동안에 경합 상태와 기타 무단 이용 가능한 취약점을 예방하는 방법
개정판에서 달라진 내용
■ C11와 C++11에 대한 내용 업데이트
■ 문자열, 동적 메모리 관리, 정수 보안 관련 내용 업데이트
■ 동시성에 관련된 새로운 단원 추가
이 책의 대상 독자
이 책은 C와 C++로 소프트웨어 개발과 유지 보수에 종사하는 사람에게 유용하다.
■ C/C++ 프로그래머라면 소프트웨어 취약점이 되는 통상적인 프로그래밍 에러를 확인하고 이러한 에러가 어떻게 무단 이용되는지를 이해하며 안전 방식으로 해결책을 구현하는 방법을 알 수 있다.
■ 소프트웨어 프로젝트 관리자라면 소프트웨어 취약점의 중요성과 위험을 식별해 시큐어 소프트웨어 개발에 투자 해야 할지를 판단할 수 있다.
■ 컴퓨터공학과 학생이라면 나쁜 습관이 드는 것을 막아주며 프로 직업인이 돼서 시큐어 프로그램을 개발할 수 있는 프로그래밍 실기를 익힐 수 있다.
■ 보안 분석가라면 일상적인 취약점에 대해 세부적인 설명을 이해하고 이러한 취약점을 발견하는 방법을 알게 되며 실제적인 예방 전략을 익힐 수 있다.
이 책의 구성
1장, '가위들고 뛰기'에서는 문제의 개략을 제공하고, 보안 용어와 개념을 소개하며 그토록 많은 취약점이 C와 C++ 프로그램에서 발견되는 이유를 알려준다.
2장, '문자열'에서는 C와 C++에서의 버퍼 오버플로와 스택 스매싱 같은 문자열 조작, 통상적인 보안 결함, 그 결과로 나타나는 취약점을 설명한다. 코드 인젝션과 아크 인젝션 익스플로잇을 모두 알아본다.
3장, '포인터 변조'에서는 공격자가 메모리의 어느 위치에서든 주소를 쓸 수 있는 임의 메모리 쓰기 익스플로잇을 소개하고, 이런 익스플로잇이 들어간 머신에서 임의 코드를 실행하는 데 사용될 수 있는 방법을 설명한다. 임의 메모리 쓰기로 발생한 취약점은 이후의 장에서 다룬다.
4장, '동적 메모리 관리'에서는 동적 메모리 관리를 설명한다. 동적으로 할당된 버퍼 오버플로, 해제된 메모리에 쓰기, 이중 해제 취약점을 설명한다.
5장, '정수 보안'에서는 정수 오버플로, 부호 에러, 잘림 에러 등의 필수 보안 문제(정수를 취급하는 보안 문제)를 다룬다.
6장, '형식화된 출력'에서는 형식화된 출력 함수의 적절하거나 부적절한 사용을 설명한다. 이들 함수의 부적절한 사용으로 인한 형식 문자열과 버퍼 오버플로 취약점 모두를 설명한다.
7장, '동시성'에서는 교착 상태, 경합 상태, 부적절한 메모리 접근 순서로 인해 발생하는 동시성과 취약점에 초점을 맞춘다.
8장, '파일 I/O'에서는 파일 I/O와 관련된 통상적인 취약점을 설명하는데, 여기에는 경합 상태와 TOCTOU 취약점이 포함된다.
9장, '권고 관행'에서는 C/C++ 애플리케이션의 전체 보안을 향상시키기 위해 특정 개발 관행을 권고한다. 이런 권고안은 특정 취약점 부류의 문제를 해결하기 위해 각 장에 있는 권장 사항들에 이어지는 것이다.
목차
목차
- 1장. 가위들고 뛰기
- 1.1 위협 알아보기
- 손실 비용은 얼마인가?
- 누가 위협하는가?
- 소프트웨어 보안
- 1.2 보안 개념
- 보안 정책
- 보안 결함
- 취약점
- 익스플로잇
- 완화
- 1.3 C와 C++
- 간략한 역사
- C 언어에서의 문제점
- 레거시 코드
- 기타 언어
- 1.4 개발 플랫폼
- 운영체제
- 컴파일러
- 1.5 정리
- 1.6 추가 참고 자료
- 2장. 문자열
- 2.1 문자열
- 문자열 데이터 형
- UTF-8
- 확장 문자열
- 문자열 리터럴
- C++에서의 문자열
- 문자형
- int
- unsigned char
- wchar_t
- 문자열 크기 지정
- 2.2 일반 문자열 처리 에러
- 길이 제한 없는 문자열 복사
- 1바이트 오버플로 에러
- null 종료 에러
- 문자열 잘림
- 함수 없이 발생하는 문자열 에러
- 2.3 문자열 취약점과 익스플로잇
- 오염된 데이터
- 보안 결함: IsPasswordOK
- 버퍼 오버플로
- 프로세스 메모리 조직
- 스택 관리
- 스택 스매싱
- 코드 인젝션
- 아크 인젝션
- 복귀 지향 프로그래밍
- 2.4 문자열에 대한 완화 전략
- 문자열 처리
- C11 Annex K 경계 점검 인터페이스
- 동적 할당 함수
- C++ std::basic_string
- 문자열 객체 참조의 무효화
- basic_string 사용에서의 기타 일반적 실수
- 2.5 문자열 처리 함수
- gets()
- C99
- C11 Annex K 경계 점검 인터페이스: gets_s()
- 동적 할당 함수
- strcpy()와 strcat()
- C99
- strncpy()와 strncat()
- memcpy()와 memmove()
- strlen()
- 2.6 런타임 방지 전략
- 검출과 복구
- 입력 유효화
- 객체 크기 점검
- 비주얼 스튜디오 컴파일러가 생성하는 런타임 점검
- 스택 카나리아
- 스택 스매싱 프로텍터(프로폴리스)
- 운영체제 전략
- 검출과 복구
- 비실행 스택
- W^X
- PaX
- 향후 방향
- 2.7 주목할 만한 취약점
- 원격 로그인
- 커버로스
- 2.8 정리
- 2.9 추가 참고 자료
- 3장. 포인터 변조
- 3.1 데이터 위치
- 3.2 함수 포인터
- 3.3 객체 포인터
- 3.4 명령 포인터 수정
- 3.5 전역 오프셋 테이블
- 3.6 .dtors 섹션
- 3.7 가상 포인터
- 3.8 atexit()와 on_exit() 함수
- 3.9 longjmp() 함수
- 3.10 예외 처리
- 구조적 예외 처리
- 시스템 기본 예외 처리
- 3.11 완화 전략
- 스택 카나리아
- W^X
- 함수 포인터의 인코딩/디코딩
- 3.12 정리
- 3.13 추가 참고 자료
- 4장. 동적 메모리 관리
- 4.1 C 메모리 관리
- C 표준 메모리 관리 함수
- 정렬
- alloca()과 가변 크기 배열
- 4.2 일반적인 C 메모리 관리 에러
- 초기화 에러
- 반환 값 점검 생략
- Null이나 부적절한 포인터의 역참조
- 해제된 메모리 참조
- 메모리의 여러 번 해제
- 메모리 누수
- 0 길이 할당
- DR #400
- 4.3 C++ 동적 메모리 관리
- 할당 함수
- 할당 해제 함수
- 가비지 콜렉션
- 4.4 일반적인 C++ 메모리 관리 에러
- 할당 실패를 올바로 처리 못함
- 짝이 잘못된 메모리 관리 함수
- 메모리 여러 번 해제
- 할당 해제 함수의 예외 처리
- 4.5 메모리 관리자
- 4.6 더그 리의 메모리 할당자
- 힙에서의 버퍼 오버플로
- 4.7 이중 해제 취약점
- 해제된 메모리에 쓰기
- RtlHeap
- 버퍼 오버플로(리덕스)
- 4.8 완화 전략
- Null 포인터
- 일관성 있는 메모리 관리 습관
- phkmalloc
- 무작위화
- OpenBSD
- jemalloc 메모리 관리자
- 정적 분석
- 런타임 분석 도구
- 4.9 중요한 취약점
- CVS 버퍼 오버플로 취약점
- 마이크로소프트 데이터 액세스 컴포넌트(MDAC)
- CVS 서버의 이중 해제
- MIT 커버로스 5의 취약점
- 4.10 정리
- 5장. 정수 보안
- 5.1 정수 보안 소개
- 5.2 정수 데이터 형
- 부호 없는 정수형
- 랩어라운드
- 부호 있는 정수형
- 부호 있는 정수형의 범위
- 정수 오버플로
- 문자형
- 데이터 모델
- 기타 정수형
- 5.3 정수 변환
- 정수 변환
- 정수 변환 순위
- 정수 프로모션
- 보통의 산술 변환
- 부호 없는 정수형의 변환
- 부호 있는 정수형의 변환
- 암시적 변환
- 5.4 정수 연산
- 대입
- 덧셈
- 뺄셈
- 곱셈
- 나눗셈과 나머지
- 시프트
- 5.5 정수 취약점
- 취약점
- 랩어라운드
- 변환과 잘림 에러
- 예외가 아닌 정수 로직 에러
- 5.6 완화 전략
- 정수형 선택
- 추상 데이터 형
- 임의 정확 연산
- 범위 점검
- 선행 조건과 후행 조건 검사
- 시큐어 정수 라이브러리
- 오버플로 검출
- 컴파일러가 생성한 런타임 검사
- 입증 가능한 범위 내 연산
- AIR 정수 모델
- 검사와 분석
- 5.7 정리
- 6장. 형식화된 출력
- 6.1 배리어딕 함수
- 6.2 형식화된 출력 함수
- 형식 문자열
- GCC
- 비주얼 C++
- 6.3 형식화된 출력 함수 공격
- 버퍼 오버플로
- 출력 스트림
- 프로그램의 비정상적 종료
- 스택 내용 보기
- 메모리 내용 보기
- 메모리 덮어쓰기
- 국제화
- 확장 문자 형식 문자열 취약점
- 6.4 스택 무작위화
- 스택 무작위화 없애기
- 두 개의 워드로 주소 기록
- 직접 인자 접근
- 6.5 완화 전략
- 형식 문자열에서 사용자 입력 배제
- 정적 내용의 동적 사용
- 기록될 바이트 제한
- C11 Annex K 경계 점검 인터페이스
- iostream과 stdio
- 검사
- 컴파일러 점검
- 정적 오염 분석
- 배리어딕 함수 구현 수정
- Exec 실드
- 포맷가드
- 정적 바이너리 분석
- 6.6 주목할 만한 취약점
- 워싱턴 대학의 FTP 데몬
- CDE 툴토크
- 이터캡 버전 NG-0.7.2
- 6.7 정리
- 6.8 추가 참고 자료
- 7장. 동시성
- 7.1 멀티스레딩
- 7.2 병렬 처리
- 데이터 병렬 처리
- 작업 병렬 처리
- 7.3 성능 목표
- 암달의 법칙
- 7.4 일반적인 에러
- 경합 상태
- 손상된 값
- 휘발성 객체
- 7.5 완화 전략
- 메모리 모델
- 동기화 기본 연산
- 스레드 역할 분석(연구)
- 불변 데이터 구조
- 현재 코드 성질
- 7.6 완화 함정
- 교착 상태
- 일찍 해제한 잠금
- 쟁탈
- ABA 문제
- 7.7 주목할 만한 취약점
- 멀티코어 DRAM 시스템에서의 DoS 공격
- 시스템 호출 래퍼에서의 동시성 취약점
- 7.8 정리
- 8장. 파일 I/O
- 8.1 파일 I/O 기본
- 파일 시스템
- 특수 파일
- 8.2 파일 I/O 인터페이스
- 데이터 스트림
- 파일 열고 닫기
- POSIX
- C++에서의 파일 I/O
- 8.3 접근 제어
- 유닉스 파일 권한
- 프로세스 특권
- 특권 변경
- 특권 관리
- 권한 관리
- 8.4 파일 식별
- 디렉토리 이동
- 동일 에러
- 심볼릭 링크
- 정규화
- 하드 링크
- 장치 파일
- 파일 속성
- 8.5 경합 상태
- 점검 시간, 사용 시간
- 교체 없이 생성
- 배타적 접근
- 공유 디렉토리
- 8.6 완화 전략
- 경합 윈도우 닫기
- 경합 객체 제거
- 경합 객체에 대한 접근 제어
- 경합 검출 도구
- 8.7 정리
- 9장. 권고 관행
- 9.1 보안 개발 생명주기
- TSP 시큐어
- 계획과 추적
- 품질 관리
- 9.2 보안 훈련
- 9.3 요구 사항
- 시큐어 코딩 표준
- 보안 품질 요구 공학
- 유즈/미스유즈 케이스
- 9.4 설계
- 시큐어 소프트웨어 개발 원칙
- 위협 모델링
- 공격면 분석
- 기존 코드의 취약점
- 시큐어 래퍼
- 입력 검증
- 신뢰 경계
- 블랙리스팅
- 화이트리스팅
- 검사
- 9.5 구현
- 컴파일러 보안 기능
- 무한 범위 정수 모델
- 안전한 시큐어 C/C++
- 정적 분석
- 소스코드 분석 실험기
- 철저한 방어
- 9.6 검증
- 정적 분석
- 침투 테스팅
- 퍼즈 검사
- 코드 감사
- 개발자 지침과 점검표
- 독립 보안 리뷰
- 공격면 리뷰
- 9.7 정리
- 9.8 참고 자료
도서 오류 신고
정오표
정오표
[p.99 : 코드 박스 안 4행]
puts("접근이 겁됐습니다.");
→
puts("접근이 거부됐습니다.");
[p.103 : 코드 박스 오른쪽 6행]
[p.382 : 하단 역자주]
a<=x<=a
→
a<=x<=b