무제

Similar documents
2007 상반기 실적회의 - DRM Extension

다른 JSP 페이지호출 forward() 메서드 - 하나의 JSP 페이지실행이끝나고다른 JSP 페이지를호출할때사용한다. 예 ) <% RequestDispatcher dispatcher = request.getrequestdispatcher(" 실행할페이지.jsp");

Observational Determinism for Concurrent Program Security

Connection 8 22 UniSQLConnection / / 9 3 UniSQL OID SET

Microsoft PowerPoint - GUI _DB연동.ppt [호환 모드]

Java XPath API (한글)

PowerPoint Template

J2EE Concepts

14-Servlet

mytalk

PowerPoint 프레젠테이션

I T C o t e n s P r o v i d e r h t t p : / / w w w. h a n b i t b o o k. c o. k r

Microsoft PowerPoint - file

쉽게 풀어쓴 C 프로그래밊

로거 자료실

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

목차 BUG DEQUEUE 의 WAIT TIME 이 1 초미만인경우, 설정한시간만큼대기하지않는문제가있습니다... 3 BUG [qp-select-pvo] group by 표현식에있는컬럼을참조하는집합연산이존재하지않으면결괏값오류가발생할수있습니다... 4

XSS Attack - Real-World XSS Attacks, Chaining XSS and Other Attacks, Payloads for XSS Attacks

Research & Technique Apache Tomcat RCE 취약점 (CVE ) 취약점개요 지난 4월 15일전세계적으로가장많이사용되는웹애플리케이션서버인 Apache Tomcat에서 RCE 취약점이공개되었다. CVE 취약점은 W

Microsoft PowerPoint - Supplement-03-TCP Programming.ppt [호환 모드]

Microsoft PowerPoint - Java7.pptx

PowerPoint Template

게시판 스팸 실시간 차단 시스템

혼자서일을다하는 JSP. 이젠일을 Servlet 과나눠서한다. JSP와서블릿의표현적인차이 - JSP는 <html> 내에서자바를사용할수있는수단을제공한다. - 서블릿은자바내에서 <html> 을작성할수있는수단을제공한다. - JSP나서블릿으로만웹페이지를작성하면자바와다양한코드가

제목 레이아웃

Microsoft PowerPoint - 03-TCP Programming.ppt

기술문서 작성 XXE Attacks 작성자 : 인천대학교 OneScore 김영성 I. 소개 2 II. 본문 2 가. XML external entities 2 나. XXE Attack 3 다. 점검방법 3 라.

PowerPoint Presentation

02 C h a p t e r Java

< FC8A8C6E4C0CCC1F620B0B3B9DF20BAB8BEC8B0A1C0CCB5E5C3D6C1BE28C0FAC0DBB1C7BBE8C1A6292E687770>

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

KYO_SCCD.PDF

<4D F736F F F696E74202D20C1A63234C0E520C0D4C3E2B7C228B0ADC0C729205BC8A3C8AF20B8F0B5E55D>

歯JavaExceptionHandling.PDF

Microsoft PowerPoint - 04-UDP Programming.ppt

** 5 개이발생한주요소프트웨어별취약점세 EDB 번호취약점종류공격난이도공격위험도취약점이름소프트웨어이름

Spring Data JPA Many To Many 양방향 관계 예제

Eclipse 와 Firefox 를이용한 Javascript 개발 발표자 : 문경대 11 년 10 월 26 일수요일

Microsoft PowerPoint 웹 연동 기술.pptx

제이쿼리 (JQuery) 정의 자바스크립트함수를쉽게사용하기위해만든자바스크립트라이브러리. 웹페이지를즉석에서변경하는기능에특화된자바스크립트라이브러리. 사용법 $( 제이쿼리객체 ) 혹은 $( 엘리먼트 ) 참고 ) $() 이기호를제이쿼리래퍼라고한다. 즉, 제이쿼리를호출하는기호

10.ppt

rmi_박준용_final.PDF

문서 템플릿

PowerPoint 프레젠테이션

12-file.key

Cluster management software

JSP 의내장객체 response 객체 - response 객체는 JSP 페이지의실행결과를웹프라우저로돌려줄때사용되는객체이다. - 이객체는주로켄텐츠타입이나문자셋등의데이터의부가정보 ( 헤더정보 ) 나쿠키 ( 다음에설명 ) 등을지정할수있다. - 이객체를사용해서출력의방향을다른

금오공대 컴퓨터공학전공 강의자료

<4D F736F F F696E74202D20B5A5C0CCC5CDBAA3C0CCBDBA5F3130C1D6C2F75F31C2F7BDC32E >

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

PowerPoint Presentation

Spring Boot/JDBC JdbcTemplate/CRUD 예제

3장

JUNIT 실습및발표

chap 5: Trees

슬라이드 1

목차 BUG offline replicator 에서유효하지않은로그를읽을경우비정상종료할수있다... 3 BUG 각 partition 이서로다른 tablespace 를가지고, column type 이 CLOB 이며, 해당 table 을 truncate

Poison null byte Excuse the ads! We need some help to keep our site up. List 1 Conditions 2 Exploit plan 2.1 chunksize(p)!= prev_size (next_chunk(p) 3

SOFTBASE XFRAME DEVELOPMENT GUIDE SERIES HTML 연동가이드 서울특별시구로구구로 3 동한신 IT 타워 1215 호 Phone Fax Co

Microsoft PowerPoint 세션.ppt

API STORE 키발급및 API 사용가이드 Document Information 문서명 : API STORE 언어별 Client 사용가이드작성자 : 작성일 : 업무영역 : 버전 : 1 st Draft. 서브시스템 : 문서번호 : 단계 : Docum

2장 변수와 프로시저 작성하기

@OneToOne(cascade = = "addr_id") private Addr addr; public Emp(String ename, Addr addr) { this.ename = ename; this.a

신림프로그래머_클린코드.key

Microsoft Word - src.doc

var answer = confirm(" 확인이나취소를누르세요."); // 확인창은사용자의의사를묻는데사용합니다. if(answer == true){ document.write(" 확인을눌렀습니다."); else { document.write(" 취소를눌렀습니다.");

PowerPoint 프레젠테이션

어댑터뷰

PowerPoint Presentation

Microsoft PowerPoint - CSharp-10-예외처리

PowerPoint 프레젠테이션

PowerPoint Presentation

Ext JS À¥¾ÖÇø®ÄÉÀ̼ǰ³¹ß-³¹Àå.PDF

Java ...

Secure Programming Lecture1 : Introduction

제11장 프로세스와 쓰레드

Analytics > Log & Crash Search > Unity ios SDK [Deprecated] Log & Crash Unity ios SDK. TOAST SDK. Log & Crash Unity SDK Log & Crash Search. Log & Cras

<C0CCBCBCBFB52DC1A4B4EBBFF82DBCAEBBE7B3EDB9AE2D D382E687770>

PowerPoint 프레젠테이션

JMF2_심빈구.PDF

PowerPoint Presentation

- JPA를사용하는경우의스프링설정파일에다음을기술한다. <bean id="entitymanagerfactory" class="org.springframework.orm.jpa.localentitymanagerfactorybean" p:persistenceunitname=

교육2 ? 그림

JAVA PROGRAMMING 실습 09. 예외처리

강의 개요

DBMS & SQL Server Installation Database Laboratory

슬라이드 1

서현수

JavaGeneralProgramming.PDF

4. #include <stdio.h> #include <stdlib.h> int main() { functiona(); } void functiona() { printf("hihi\n"); } warning: conflicting types for functiona

초보자를 위한 C# 21일 완성

2) 활동하기 활동개요 활동과정 [ 예제 10-1]main.xml 1 <LinearLayout xmlns:android=" 2 xmlns:tools="

SKINFOSEC-CHR-028-ASP Mssql Cookie Sql Injection Tool 분석 보고서.doc

Microsoft PowerPoint 자바-기본문법(Ch2).pptx

서블릿의라이프사이클 뇌를자극하는 JSP & Servlet

목차 INDEX JSON? - JSON 개요 - JSONObject - JSONArray 서울시공공데이터 API 살펴보기 - 요청인자살펴보기 - Result Code - 출력값 HttpClient - HttpHelper 클래스작성 - JSONParser 클래스작성 공공

PowerPoint 프레젠테이션

Microsoft Word - SKINFOSEC-CHR-026- Mass SQL Injection 탐지 우회분석 보고서.doc

<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CC0E7B0EDB0FCB8AE5C53746F636B5F4D616E D656E74732E637070>

Transcription:

v0.8 2012년월4

목차 제1 장개요 0 제1절배경 0 제2 절가이드목적및구성 0 제2 장소프트웨어개발보안 0 제1 절개요 0 제2 절개발보안체계 0 제3 절보안약점진단제거 0 제3 장보안약점진단방법 0 제1 절입력데이터검증및표현 0 제2 절보안기능 0 제3 절시간및상태 0 제4 절에러처리 0 제5 절코드오류 0 제6 절캡슐화 0 제7절 API 악용 0 3

부록 0 제1 절용어정의 0 제2절 SW 보안약점항목 0 제3 절참고자료 0 4

제1장개요 제1절배경 최근의사이버공격은침입차단시스템등보안장비를우회하거나, 보안패치 가발표되기이전의보안취약점을악용하는제로데이공격, 웹사이트해킹등 이많은부분을차지하고있으며, 이러한공격은 SW 자체의보안취약점을이 용하는경우가대부분 1) 이다. 웹사이트의경우, 불특정다수가쉽게접근할수있고, 사용자가입력한정 보를처리하는프로그램의특성상외부공격에항상노출되어있어사이버침 해사고가발생할가능성이높다. 특히, 웹서버, 웹어플리케이션서버등웹 관련 SW 자체의보안취약점을이용한공격은침입차단시스템등보안장비로 대응하기가쉽지않다. 최근 SQL 삽입공격등 SW 자체보안취약점을이용하 여개인정보등의중요정보가유출된웹서버공격인 Sony 社 플레이스테이션 사이트공격, 페이팔(PayPal) 웹사이트공격, 홍콩증권거래소공격이대표적 인예라고할수있다. 또한, 최근증가추세에있는 APT(Advance Persistent Threat) 2) 공격의경 우, 기업이나기관의내부기밀자료를목표로내부에서구축 운영되고있는 특정의 SW 보안취약점을대상으로지능화된방법을통해지속적으로공격하 기때문에이에대한근본적인대책이필요하다. APT 공격의대표적인예로 는이란핵시설대상스턱스넷, 글로벌에너지기업대상나이트드래곤(Night Dragon) 3), 국내 OO 전산망해킹등이있다. 1) 가트너社 ( 05.5 월) 에따르면, 사이버공격의약가 75% 응용프로그램의취약점을악용한것이라고함 2) 다양한 IT 기술과방식들을이용해조직적으로특정기업이나조직네트워크에침투해활동거점을마련한뒤정보를외부로빼돌리는형태의공격들을총칭. 공격방법은내부자에게악성코드가포함된이메일을오랜기간동안꾸준히발송해한번이라도클릭되길기다리는형태나, 스턱스넷(Stuxnet) 과같이악성코드가담긴이동식디스크(USB) 등으로전파하는형태, 악성코드에감염된 P2 P 사이트에접속하면악성코드에감염되는형태등이있음 [ 출처:TT A 용어사전] 3) APT 형태의공격의한형태로년 2011 맥아피(macafee) 에서발표한공격방법임 [ 참고: http://www.mcafee.com/us/resources/white-papers/wp-global-energy-cyberattacks -night-dragon.pdf] 5

사이버침해사고에근본적으로대응및예방하기위해서는 SW 개발단계부 터 SW 보안취약점을진단하여제거하여안전한 SW를개발하는체계도입이 필수적이다. 미국의경우, 국토안전부(DHS) 를중심으로시큐어코딩을포함한 SW 개발전과정( 설계 구현 시험등) 보안활동에대해활발히연구하고있으 며, 이는 2011년 11 월발표한 안전한사이버미래를위한청사진(Blueprint for a Secure Cyber Future) 4) 에도나타나있다. 미국의 Microsoft 社사례를살펴보면자사의제품출시후보안패치횟수를 분석한결과, 개발과정에서 MS-SDL 5) 을적용한운영체제는보안취약점이 45% 감소하였으며, DB 서버의경우보안취약점이 91% 감소하였다. [ 출처 : 마이크로소프트홈페이지(www.microsoft.com/security/sdl/learn/measurable.aspx)] < 그림 1-3> SDL 적용와 SW 미적용 SW의취약점수 다음의표는 SW 설계단계에서제품출시까지보안취약점등결함을제거하 는시점에따른비용차이를분석한결과로서, 보안취약점제거비용이최대 4) 참고 : http://www.dhs.gov/xlibrary/assets/nppd/blueprint-for-a-secure-cyber-future.pdf 5) MS-SDL(Microsoft Software Development Lifec-ycle) : MS 사의안전한개발생명주기( 개발보안생명주기) 6

30 배까지차이가발생할수있음을나타내고있다. 즉, 설계과정에서발생한 결함이개발완료후에발견 조치되는경우엔설계단계에서결함을수정하는 비용대비배30 의비용이발생한다. 또한, 통합과정에발생한결함의경우엔 20 배의수정비용이발생하는등개발완료이전의보안취약점진단및제거 활동이무엇보다중요함을나타내고있다. < 표 1-1> SW 개발단계별결함수정비용분석 구분설계단계코딩단계통합단계베타제품제품출시 설계과정 1배 5배 10배 15배 30배 코딩과정 - 1배 10배 20배 30배 통합과정 - - 1배 10배 20배 [ 출처년 : NIST, The Economic Impacts of Inadequate Infrasturcture for Software Testing, 2002 월5 ] 이는 SW 개발단계에서의보안취약점제거가 SW 보안품질제고를위한근 본적인접근으로운영단계에서수행하는것보다비용대비효과가훨씬우수 하고볼수있다. 따라서, SW 개발단계에서부터 SW 보안취약점을진단하여 제거하는등 SW 개발보안( 시큐어코딩) 체계를도입 운영하면신뢰할수있는 전자정부서비스를구현하여안전한서비스를제공할수있다. 또한, 개발보안 의실효성제고를위해개발자가보안취약점을이해하고보안조치를할수있 도록개발보안사전교육을강화하고검, 수과정에서보안취약점잔존여부를 확인해야한다. 7

제2절가이드목적및구성 목적 정보화사업수행시시큐어코딩을준수하여를 SW 개발했는지 여부를진단하기위해 SW 보안약점진단기법제시 대상 전자정부 SW 보안약점진단원 범위 행정기관등이추진하는정보화사업 유지보수로변경되거나신규로개발되는소스코드전체 (2 장) SW 개발보안의무화대상 범위 기준과정보화사업단 계별 기관별 주체별개발보안활동소개 구성 (3 장) SW 보안약점에대한설명및보안대책과 JAVA 언어로 작성된시큐어코딩예제기반의구체적인진단방법소개 ( 부록) 용어정리, SW 보안약점항목, 참고자료 ( 발주자) SW 보안약점진단 제거요구사항도출시활용 활용 ( 사업자) 자체적으로 SW 보안약점을진단 제거시활용 ( 진단원) 사업자가수행한 SW 보안약점제거결과진단시활용 ( 기타) SW 개발보안체계및보안약점진단 제거방법에대한이해 8

제2장소프트웨어개발보안 본장에서는 SW 개발보안전반을살펴본후, 보안약점진단 제거를중심으 로살펴본다. 제1절개요 SW 개발보안은해킹등사이버공격의원인인보안약점을 SW 개발단계에서 사전에제거하여안전한를 SW 개발하기위한목적으로추진하는 SW 개발체계이다. SW 개발보안의무화는년 2012 10 월정보시스템감리대상정보화사업억(40 원이상) 을대상으로시행하여, 2014년까지감리대상전체정보화사업으로 SW 개발보안의무화대상을확대해나갈예정이다. SW 개발보안적용대상및 범위, 기준등을요약하면다음과같다. 구분내용비고 대상 정보시스템감리대상정보화사업 ( 12.10 월) 40 억원이상 ( 13.7 월) 20 억원이상 ( 14.7 월) 감리대상전사업 단계적확대 범위 소스코드신규개발 ( 전체, 유지보수로변경된부분 ) 상용SW 제외 기준 SW 보안약점기준(SQL 삽입등개 41 항목) 정보시스템구축운영지침 별표3 정보시스템감리기준( 제10조제1 항세부검사항목) 에포함 공통평가기준 (Common Criteria, CC) 정보보호시스템평가인증지침 진단원자격기준 정보시스템구축운영지침 별표4 SW 보안약점진단 진단도구 평가 인증 진단원 자격부여 9

제2절 SW 개발보안체계 전자정부법에따른정보시스템을기획 구축 운영 유지보수하기위한사업인 정보화사업은정보화사업발주, 계약( 사업자선정 ), SW 개발( 사업수행), 검사및 인수( 운영) 단계로구분할수있다. 정보화사업단계별 SW 개발보안관련보 안활동은다음과같이요약할수있다. 구분내용비고 발주 제안요청서에 SW 개발보안적용 명시 행정기관등 계약 SW 개발보안을위한적절한개발절차 방법, SW 약점진단도구사용여부등확인 보안 행정기관등 개발인력대상 SW 개발보안관련교육실시 개발 SW 개발보안가이드를참조하여개발 자체적으로보안약점진단및제거 사업자 SW 보안약점의부적합사항( 감리수행결과보고서) 조치 검사/ 인수 SW 보안약점제거여부진단 SW 보안약점의부적합사항( 감리수행결과보고서) 조치여부확인 감리법인 10

SW 개발보안관련보안활동을행안부, 한국인터넷진흥원(KISA) 등참여기 관별역할로구분하면다음과같이요약할수있다. 구분내용비고 국정원과협의하여 SW 개발보안가이드 공지 행안부 보안약점진단과관련된기술지원 진단원양성과관련된업무수행 교육및자격부여, 자격유무확인등 KISA 진단원양성과관련된위탁업무수행및세부사항공지 교육자격부여, 자격유무확인등 제안요청서에 SW 개발보안적용 명시 행정기관등 SW 보안약점진단 제거결과확인 감리법인( 감리대상사업) 또는자체적( 감리대상外사업) 으로수행 보안약점진단과관련한기술지원( 행안부) 요청 개발인력대상 SW 개발보안관련교육실시 사업자 SW 개발보안가이드를참조하여개발 자체적으로보안약점진단및제거 SW 보안약점의부적합사항( 감리수행결과보고서) 조치 감리법인 SW 보안약점진단시, 진단원우선배치 SW 보안약점제거여부진단및조치결과확인 11

SW 개발보안관련보안활동을발주자, 개발자, 진단원등 SW 개발보안참 여인력별역할로구분하면다음과같이요약할수있다. 구분내용비고 제안요청서에 SW 개발보안적용 명시 발주자 감리법인을통해 SW 정보화사업감리수행대상사업 보안약점제거여부확인 사업자에의한보안약점진단 제거결과확인 정보화사업감리수행대상에해당하지않는사업 보안약점진단과관련한기술지원( 행안부) 요청 개발인력대상 SW 개발보안관련교육실시 개발자 SW 개발보안가이드를참조하여개발 자체적으로보안약점진단및제거 SW 보안약점의부적합사항( 감리수행결과보고서) 조치 진단원 SW 보안약점제거여부진단및조치결과확인 12

제3 절보안약점진단 제거 본절에서는 SW 개발보안준수여부검증을수행하는진단원에대한역할을 정의하는한편, 구체적인업무지침을제시한다. SW 보안약점을진단하는방법은다음과같이정적분석과동적분석으로구분 할수있으며, 서로상호보완적이라고할수있다. 구분내용특징 정적분석 SW 를실행하지않고, 소스코드 수준으로보안약점분석 SW 개발과정에서주로사용 SW 개발초기에보안약점발견으로 수정비용절감 컴포넌트간발생할수있는통합된 보안약점발견은제한적임 설계 구조관점의보안약점은 발견할수없음 동적분석 SW 실행환경에서보안약점분석 SW 시험단계에서주로사용 소스코드필요없음 정확도와커버리지향상 도구사용자의수준에영향을받음 구조관점의보안약점은발견할수없음 본가이드는동적분석에비해상대적으로 SW 개발초기에보안약점을발견 하여제거할수있어비용절감효과가높은정적분석기반의 단기법을소개한다. SW 보안약점진 SW 보안약점진단원은본가이드를참고하여사업자가자체적으로수행한 SW 보안약점진단 제거결과의적합성여부를확인하여야한다. 참고로사 업자는 SW 보안약점진단 제거를위해행정안전부장관이고시한 정보보호 13

시스템평가 인증지침 에따라국가정보원장이인증한보안약점진단도구를 구매하여사용하여야하며, 진단원은사업자가사용한진단도구를이용하여 SW 보안약점진단 제거결과를확인하여야한다. 정적분석을기반으로 SW 보안약점을진단하는구체적인기법은제3장에서 설명한다. 정보화사업에따른정보시스템구축시활용되는전자정부프레임워 크는언JAVA 어로구현되었으며, 실제 JAVA 언어가많이활용되기때문에 SW 보안약점진단기법설명의이해를돕기위해사용된예제소스코드는 JAVA 언어를기반으로작성하였다. 14

제3장보안약점진단방법 제1절입력데이터검증및표현 프로그램입력값에대한검증누락또는부적절한검증, 데이터의잘못된 형식지정등으로인해발생되는보안약점으로 SQL 삽입, 크로스사이트스크립 트등의공격을유발할수있다. 1. SQL 삽입 가. 개요 데이터베이스와 (DB) 연동된웹어플리케이션의입력폼및 URL 파라미터에 SQL 문을삽입하여 DB로부터정보를열람하거나조작할수있는취약점을유 발할수있다. < 그림삽입 3-1> SQL 예를들어, < 그림 3-1> 에서나타낸것처럼취약한웹어플리케이션에서는사 용자로부터입력된값을필터링과정없이넘겨받아동적쿼리 (Dynamic Query) 6) 를 6) 동적쿼리(Dynamic Query) : DB 에서실시간으로받는쿼리. parameterized statement가동적쿼리가됨 15

생성하기때문에개발자가의도하지않은쿼리가생성되어정보유출에악용될 수있다. 나. 보안대책 prepared statement 7) 객체등을이용하여 DB에컴파일된쿼리문 ( 상수) 를전달 하는방법을사용한다. parameterized statement 를사용하는경우에는 DB 쿼리 에사용되는외부입력데이터에대하여특수문자및쿼리예약어를필터링하 고, Struts, Spring 등과같은프레임워크를사용하는경우에는외부입력값 검증모듈및보안모듈을상황에맞추어적절하게사용한다. 다. 코드예제 다음의예제는안전하지않은코드의예를나타낸것이다. 외부입력으로부터 tablename과 name을받아서쿼리를 SQL 생성하고있으며, name 의값으로 "name' OR 'a'='a" 를할당하여조작된쿼리를생성하는문자열전달이가능하 다. 안전하지않은코드의예 JAVA 1: try { 2: String tablename = props.getproperty("jdbc.tablename"); 3: String name = props.getproperty("jdbc.name"); 4: String query = "SELECT * FROM " + tablename + " WHERE Name =" + name; 5: stmt = con.preparestatement(query); 6: rs = stmt.executequery(); 7:...... 8: catch (SQLException sqle) { 9: finally { 이를안전한코드로변환하면다음과같다. 인자를받는 preparedstatement 객체를상수스트링으로생성하고, 인자부분을 setxxx() 메소드로설정하여, 7) Prepared Statement : 사전에를 Compiled Query 만드는객체로등에서 MySQL, Oracle, DB2, SQL Server 지원하며, Java의의 JDBC, Perl 의 DBI, PHP 의 PDO, ASP 를 ADO 이용하여사용가능 16

외부의입력이쿼리문의구조를바꾸는것을방지하는것이필요하다. 안전한코드의예 JAVA 1: try { 2: String tablename = props.getproperty("jdbc.tablename"); 3: String name = props.getproperty("jdbc.name"); 4: String query = "SELECT * FROM? WHERE Name =? "; 5: stmt = con.preparestatement(query); 6: stmt.setstring(1, tablename); 7: stmt.setstring(2, name); 8: rs = stmt.executequery(); 9:...... 10: catch (SQLException sqle) { 11: finally { 라. 진단방법 statement 8) 객체를통해서쿼리가실행되는부분을확인하고 ( ➀), statement 객체가객Prepared Statement 체인지확인한다( ➁). Prepared Statement 객체를 사용하는경우엔기본적으로안전하다고판정한다. 이후쿼리문에사용되는변수가외부입력값인지확인하고( ➂), 인경우엔해당변수에대한필터링모듈이존재하는지확인한다. 외부입력값 필터링모듈 이존재하거나관련프레임워크에서적절히조치할경우에도안전하고판정하고, 그외에는취약하다고판정한다. class Login { public Connection getconnection() throws SQLException { DriverManager.registerDriver(new com.microsoft.sqlserver.jdbc.sqlserverdriver()); String dbconnection = PropertyManager.getProperty("db.connection"); return DriverManager.getConnection(dbConnection); String hashpassword(char[] password) { // create hash of password // 외부입력값을 (username, password) 제공받음 8) statement : 자바에서사용하는객체중하나 17

public void doprivilegedaction(string username, char[] password) throws SQLException { Connection connection = getconnection(); if (connection == null) { // handle error try { String pwd = hashpassword(password); String sqlstring = "SELECT * FROM db_user WHERE username = '"+ username + "' AND password = '" + pwd + "'"; ------------------------ ➂ Statement stmt = connection.createstatement(); ------------------------ ➁ ResultSet rs = stmt.executequery(sqlstring); ------------------------ ➀ if (!rs.next()) { throw new SecurityException( "User name or password incorrect" ); // Authenticated; proceed finally {... 다음의예제를살펴보면, 15라인에서사용자의입력값이변수 pid에저장되 고 19라인에서 commentdao.getprojectcommenttblbywhere 함수의파라미터로 사용된다. 해당함수에서 134 라인에서문자열을만들게되고, 138번라인에서 해당문자열을이용해서쿼리를실행하므로취약하다고판정한다. 1C : projectdetail.jsp <% 15: String pid = request.getparameter("projectid") == null? "" : request.getparameter("projectid"); MashupProjectDao mashupdao = (MashupProjectDao)DAOHelper.getMashupProjectDao(applicatio n); ProjectCommentDao commentdao = 18

(ProjectCommentDao)DAOHelper.getProjectCommentDao(application); MashupProjectTbl prjtbl = mas hupdao.getmashupprojecttbl(pid); 19: ArrayList commentlist = commentdao.getprojectcommenttblbywhere("where projectid="+pid+" order by writedate desc"); String loginid = session.getattribute("userid") == null? "" : (String)session.getAttribute("userid"); int category = prjtbl.getcategory(); String categorystr = ""; 1C : ProjectCommentDAO.java public ArrayList getprojectcommenttblbywhere(string where) { String sql_ = this.getmessagesourceaccessor().getmessage("proj ectcommentdao.getprojectcommenttbl"); ArrayList ret = new ArrayList(); 134: sql_ += " "+where; try { JdbcTemplate template_ = this.getjdbctemplate(); 138: List tmp = template_.query( sql_, new ProjectCommentRowMapper()); ret = Util.trimToResize(tmp); catch(dataaccessexception e) <SQL 삽입 : JDO> 외부의신뢰할수없는입력을적절한검사과정을거치지않고, JDO(Java Data Objects) API의 SQL 또는쿼JDOQL 리생성을위한문자열로사용하면, 공격자가프로그래머가의도하지않았던문자열을전달함으로써쿼리의의미를 왜곡시키거나그구조를변경하여임의의쿼리명령어를수행할수있다. 19

다음의예제에서는공격자가외부의입력값(name) 을 "name'; DELETE FROM MYTABLE; --" 로주게되면, 다음과같은쿼리가실행되어테이블이삭 제될수있으므로취약하다고판정한다. (SELECT col1 FROM MYTABLE WHERE name = 'name' ; DELETE FROM MYTABLE; --') 1: 2: public class U9102 implements ContactDAO { 3: public List<Contact> listcontacts() { 4: PersistenceManager pm = getpersistencemanagerfactory().getpersistencemanager(); 5: String query = "select from " + Contact.class.getName(); 6: try { 7: Properties props = new Properties(); 8: String filename = "contacts.txt"; 9: FileInputStream in = new FileInputStream(fileName); 10: if( in!= null ) { props.load(in); 11: in.close(); 12: // 외부로부터입력을받는다 13: String name = props.getprop erty("name"); 14: if( name!= null ) { 15: query += " where name = '" + name + "'"; 16: 17: catch (IOException e) { 18: 19: // 와부입력값이객JDO 체의인자로사용된다. 20: return (List<Contact>) pm.newquery(query).execute(); 21: 22: 20

<SQL 삽입 : Persistence> J2EE Persistence API 를사용하는응용프로그램에서외부의입력을아무검 증없이쿼리에그대로사용하면, 쿼리의의미를왜곡시키거나그구조를변경 하여임의의쿼리명령어가수행될수있다. 공격자가외부의입력(id) 의값으 로을 "foo'; DELETE FROM MYTABLE; --" 주게되면, 다음과같은쿼리가실 행되어테이블이삭제될수있으므로취약하다고판정한다. (SELECT col1 FROM MYTABLE WHERE name = 'foo' ; DELETE FROM MYTABLE; --') 1: 2: public class U9103 implements ServletContextListener { 3: public List<?> getallitemsinwildcardcollection() { 4: EntityManager em = getentitymanager(); 5: List<U9103> r_type = null; 6: try { 7: Properties props = new Properties(); 8: String filename = "conditions.txt"; 9: FileInputStream in = new FileInputStream(fileName); 10: props.load(in); 11: 12: // 외부로부터입력을받는다. 13: String id = props.getproperty("id"); 14: // 외부입력값이의 query 인자로사용이된다. 15: Query query = 16: em.createnativequery("select OBJECT(i) FROM Item i WHERE i.itemid > " + id); 17: List<U9103> items = query.getresultlist(); 18: 19: return r_type; 20: 21

21: <SQL 삽입 : mybatis Data Map> 외부에서입력된값이쿼리의인자값으로만사용되지않고쿼, 리명령어에 연결되는문자열로사용되면, 공격자가의도하지않았던문자열을전달함으로 써쿼리의의미를왜곡시키거나, 그구조를변경하여임의의 DB 명령어를수 행할수있다. 다음의예제는 mybatis Data Map 에서사용하는쿼리설정파일(XML) 이다. 정의된쿼리중 delstudent 명령어선언에서쿼리에삽입되는인자들중 $name$ 으로전달되는문자열값은그대로연결되어쿼리가만들어진다. 따라 서만약 name 의값으로을 "' OR 'x'='x'" 전달하면다음과같은쿼리가실행되 어테이블의모든원소를삭제할수있으므로취약하다고판정한다. (DELETE STUDENTS WHERE NUM = #num# and Name = '' OR 'x'='x') 1: <?xml version="1.0" encoding="utf-8"?> 2: <!DOCTYPE sqlmap PUBLIC "-//ibatis.com//dtd SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd"> 3: <sqlmap namespace="student"> 4: <resultmap id="studentresult" class="student"> 5: <result column="id" property="id" /> 6: <result column="name" property="name" /> 7: </resultmap> 8: <select id="liststudents" resultmap="studentresult"> 9: SELECT NUM, NAME 10: FROM STUDENTS 11: ORDER BY NUM 12: </select> 13: <select id="namestudent" parameterclass="integer" resultclass="student"> 22

14: SELECT NUM, NAME 15: FROM STUDENTS 16: WHERE NUM = #num# 17: </select> 18: <!-- dynamic SQL 사용 --> 19: <delete id="delstudent" parameterclass="student"> 20: DELETE STUDENTS 21: WHERE NUM = #num# AND Name = '$name$' 22: </delete> 23: </sqlmap> <SQL 삽입 : Hibernate> 외부의신뢰할수없는입력을적절한검사과정을거치지않고 Hibernate API의쿼SQL 리생성을위한문자열로사용하면, 공격자가프로그래머가의도 하지않았던문자열을전달함으로써쿼리의의미를왜곡시키거나그구조를변 경하여임의의 DB 명령어가수행되도록할수있다. 다음의예제에서는외부의입력(idValue) 을아무검증과정없이쿼리에그대 로사용하고있다. 만일, 외부의입력으로과 "n' or '1'='1" 같은문자열이입력 되면, 다음과같은쿼리가실행되어테이블내의모든레코드가반환될수있 으므로취약하다고판정한다. ("from Address a where a.name='n' or '1'='1'") 1: 2: public void listhoney() { 3: Session sessio n = new Configuratio n().configure().buildsessio nfactor y().opensession(); 4: try { 5: Properties props = new Properties(); 6: String filename = "Hibernate.properties"; 23

7: FileInputStream in = new FileInputStream(fileName); 8: props.load(in); 9: 10: // 외부로부터입력을받음 11: String idvalue = props.getproperty("idlo w"); 12: // 외부입력을검증없이 SQL qeuery 문의인자로사용한다. 13: Query query = session.createquery("from Address a where a.name='" + idvalue); 14: query.list(); 15: catch (IOException e) { 16: 마. 참고문헌 [1] CWE-89 SQL Injection - http://cwe.mitre.org/data/definitions/89.html [2] OWASP Top 10 2010 - (OWASP 2010) A1 Injection [3] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Insecure Interaction Between Components, RANK 1 CWE-89 Improper Neutralization of Special Elements used in an SQL Command('SQL Injection') 24

2. 자원삽입 가. 개요 외부입력값을검증하지않고시스템자원 (resource) 에대한식별자로사용하는 경우, 공격자는입력값조작을통해시스템이보호하는자원에접근하거나수 정할수있다. < 그림자3-2> 원삽입 나. 보안대책 외부의입력을자원 ( 파일, 소켓의포트등 ) 식별자로사용하는경우, 식별자가적절 한검증을거치거나사전에정의된적합한리스트에서선택되도록작성한다. 외부의입력이파일명인경우에는경로순회 (directory traversal) 9) 를수행하는위험한 문자를제거한다. 9) 경로순회등을 :./,../ 사용하여상대경로참조방식을이용해웹루트디렉토리를벗어나다른디렉토리의중요파일에접근하는공격법. 경로추적이라고도함 25

다. 코드예제 다음의예제는안전하지않은코드의예를나타낸것으로, 외부의입력 (service) 을포트번호로그대로사용하고있다만. 일, 공격자가 Service No 의 값으로 -2920 과같은값을지정하면기존의 80 포트에서구동되는서비스와 충돌되어에러를야기할수있다. 안전하지않은코드의예 - JAVA 1: 2: public void f() throws IOException { 3: int def = 1000; 4: ServerSocket serversocket; 5: Properties props = new Properties(); 6: String filename = "file_list"; 7: FileInputStream in = new FileInputStream(fileName); 8: props.load(in); 9: 10: // 외부에서입력한데이터를받는다. 11: String service = props.getproperty("service No"); 12: int port = Integer.parseInt(service); 13: 14: // 외부에서입력받은값으로소켓을생성한다. 15: if (port!= 0) 16: serversocket = new ServerSocket(port + 3000); 17: else 18: serversocket = new ServerSocket(def + 3000); 19: 20: 21: 다음은안전한코드예제를나타낸것이다. 내부자원에접근할때외부입력 값을포트번호와같은식별자로직접사용하는것은바람직하지않으며꼭, 필 요한경우엔가능한리스트를설정하고, 해당범위내에서할당되도록작성한 다. 26

안전한코드의예 - JAVA 1: 2: public void f() throws IOException { 3: ServerSocket serversocket; 4: Properties props = new Properties(); 5: String filename = "file_list"; 6: FileInputStream in = new FileInputStream(fileName); 7: String service = ""; 8: 9: if (in!= null && in.available() > 0) { 10: props.load(in); 11: // 외부로부터데이터를입력받는다. 12: service = props.getproperty("service No"); 13: 14: // 외부의입력을기본적인내용검사를한다. 15: if ("".equals(service)) service = "8080"; 16: 17: int port = Integer.parseInt(service); 18: // 외부입력에서포트번호를검사한후리스트에서적합한값을할당한다. 19: switch (port) { 20: case 1: 21: port = 3001; break; 22: case 2: 23: port = 3002; break; 24: case 3: 25: port = 3003; break; 26: default: 27: port = 3000; 28: 29: // 서버소켓에검사완료된포트를할당한다. 30: serversocket = new ServerSocket(port); 31: 32: 33: 라. 진단방법 파일명, 소켓의포트등과같은자원을사용하는지확인하고( ➀), 해당자원 에대한접근이외부에서직접접근하는지확인한다( ➁). 직접접근하지않고 매핑표나리스트를가질경우엔기본적으로안전하다고판단하고, 그외의경우 27

엔취약하다고판정한다. public class U99 { public void f() throws IOException { int def = 1000; ServerSocket serversocket; Properties props = new Properties(); String filename = "file_list"; FileInputStream in = new FileInputStream(fileName); props.load(in); String service = props.getproperty("service No"); int port = Integer.parseInt(service); ------------------------ ➁ if (port!= 0) serversocket = new ServerSocket(port + 3000); ------------------------ ➀ else serversocket = new ServerSocket(def + 3000); ------------------------ ➀ serversocket.close(); 중요자원( 파일, 소켓등) 을사용할때, 식별자를구성하는외부입력값에대한 검증프로세스가존재하지않으면취약한것으로판정한다. 다음의예제를살펴보면, EgovFileCmprs.jsp 에서 70라인에외부입력값인 source를받아서 74라인에서 static 함수인 EgovFileCmprs.cmprsFile를호출하 고있다. 이함수내에서 48라인에서 source 파라미터를 source1 변수에저장하 고, 50라인에서그변수를사용하여파일을생성하고있어취약하다고판정한 다. T2 : EgovFil ecmprs.jsp else if(execflag.equals("cmprs_action")){ boolean iscompressed = false; 70: String source = request.getparameter("so urce"); String target = r equest.getparameter("target"); 28

if (source!= null && source.length() > 0 && target!= null && target.length() > 0) { 74: iscompressed = EgovFileCmprs.cmprsFile(source, target); %> T2 : EgovFil ecmprs.java public static boolean cmprsfile(string source, String target) throwsexception { // boolean result = false int cnt = 0; // byte[] buffer = new byte[buffer_size]; FileInputStream finput = null FileOutputStream foutput = null ZipOutputStream zoutput = null 48: String source1 = source.replace('\\', FILE_SEPARATOR).replace('/', FILE_SEPARATOR); String target1 = target.replace('\\', FILE_SEPARATOR).replace('/', FILE_SEPARATOR); 50: File srcfile = new File(source1); 다음의예제에서사용되는 스가안전하므로취약하지않다고판정한다. getrealpath(), getcontextpath() 등내부함수는소 File file = new File(getServletConfig().getServl etcontext().getrealpath("/")+ "/jsp/gis/gcc/cmm/xml/"+(string)tempchartimglist.get(k)); 다음의예제는파일명에사용자입력값이나가 property 들어가는것이아니 고, 파일의필터에적용되는것이므로자원삽입이취약하지않다고판정한다. 29

File[] f_list = f.listfiles( new FilenameFilter() { public boolean accept(file dir, String name) { // TODOAuto-generated method stub String org_code = SystemierConfig.getPropertiesBean().getProperty("is.OrgCode"); if(name.matches( org_code + ".*CVDTST.*" + ".txt") ) { return true return false ); BufferedReader in = new BufferedReader( new FileReader( f_list[0].getpath() )); 일반적으로프레임워크를사용하면서시스템 Property에서가져오는명령어 는취약하지않다. 하지만시스템프로퍼티등공용으로사용하는프로퍼티가 아닌개별사용프로퍼티일경우는정탐, 시스템프로퍼티사용은오탐으로처 리한다. 마. 참고문헌 [1] CWE-99 자원삽입 - http://cwe.mitre.org/data/definitions/99.html [2] OWASP Top 10 2010 - (OWASP 2010) A1 Injection [3] SANS Top 25 2010 - (SANS 2010) Insecure Interaction 30

3. 크로스사이트스크립트 가. 개요 웹페이지에악의적인스크립트를포함시켜사용자측에서실행되게유도할 수있다. 예를들어, < 그림과 3-3> 같이검증되지않은외부입력이동적웹페 이지생성에사용될경우, 전송된동적웹페이지를열람하는접속자의권한으 로부적절한스크립트가수행되어정보유출등의공격을유발할수있다. < 그림 3-3> 크로스사이트스크립트 나. 보안대책 일반적인경우에는사용자가문자열에스크립트를삽입하여실행하는것을 막기위해사용자가입력한문자열에서 <, >, &,, 등을 replace등의문자변 환함수나메소드를사용하여로 &lt, &gt, &amp, &quot 치환한다. HTML 태 그를허용하는게시판에서는게시판에서지원하는 HTML 태그의리스트(White List) 를선정한후, 해당태그만허용하는방식을적용한다. 31

다. 코드예제 파라미터 (name) 에 <script>alert (document.cookie);</script> 와같은스크립트 코드가입력되고, 이값이그대로사용되면공격자에게피해자의쿠키정보가 전송될수있다. 안전하지않은코드의예 JAVA 1: <h1>xss Sample</h1> 2: <% 3: String name = request.getparameter("name "); 4: %> 5: <p>name:<%=name%></p> 외부입력문자열에서 replaceall() 메소드를사용하여 <, >, &, " " 같이스크 립트생성에사용되는문자열을 &lt, &gt, &amp, &quot 등으로변경하면, 파라 미터 name 에악성코드가포함되더라도스크립트가실행되지않는다. 안전한코드의예 JAVA 1: <% 2: String name = request.getparameter("name"); 3: if ( name!= null ) { 4: name = name.replaceall("<","<"); 5: name = name.replaceall(">",">"); 6: name = name.replaceall("&","&"); 7: name = name.replaceall(" ","""); 8: else { return; 9: %> 라. 진단방법 웹페이지로출력하는변수값이존재하는지확인하고( ➀), 해당변수값이외 부입력값인지확인한( ➁) 후변수값이적절하게필터를거치는지확인한다. 적절한필터를거친후출력되는경우나프레임워크등을사용하여자체적인검증기능이존재하면안전하지만그외에는취약하다. 32

<%@page contenttype="text/html" pageencoding="utf-8"%> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> </head> <body> <h1>xss Sample</h1> <% // 외부로부터이름을받음 String name = request.getparameter("name"); ------------------------ ➁ %> <!-- 외부로부터받은 name이적절한필터없이그대로출력 --> <p>name:<%=name%></p> ------------------------ ➀ </body> </html> 다음의예제에서는번17 째라인에서사용자의입력값인파라미터 prptnames 값을자바스크립트안에직접사용하고있기때문에취약하다. L1 : RexViewer.jsp <script language="javascript"> <!--// function fnopen(){ var oreport = GetfnParamSet(); //oreport.rptname = "testserver"; 17: oreport.rptname = "<%=request.getparameter("prptnames")%>"; 다음의예제에서는 13라인에서외부입력값으로를 password 입력받아저장 하고 54 라인에서저장한변password 수를출력하고있어취약하다. 33

C1 : loginpass.jsp String logintype =(String)gpkirequest.getParameter("loginType"); String loginid =(String)gpkirequest.getParameter("loginId"); 13: String password =(String)gpkirequest.getParameter("password");... <form id="loginform" name="loginform" method="post" action="<c:url value='/afapplogin.do'/>"> <input type="hidden" name="forward" id="forward" value="cmm/access/amcmmaccess020"/> <input type="hidden" name="logintype" id="logintype" value="<%=logint ype%>"/> <input type="hidden" name="afapplogindvo_loginid" id="afapplogindvo_loginid" value="<%=loginid%>"/> 54: <input type="hidden" name="afapplogindvo_password" id="afapplogindvo_password" value="<%=password%>"/> <input type="hidden" name="afapplogindvo_dn" id="afapplogindvo_dn" value="<%=subdn%>"/> </form> 아래소스는의 Sprin g Framework 을 Ann otation 사용한소스이다. 이소스에서 다음의예제에서는 @ModelAttribute("apApsCommonCodeVO") ApApsCommonCodeVO apapscommoncodevo 어노테이션에따라 apsapscommoncodevo 는외부입력값이다. 154라인에서외부입력값의 target값을저장하고번162 째라인에서외부입력값을 response 의 output stream 에출력하고있다. 그과정에서입력값에대한필터링이없으므로취약 한것으로판정한다. 34

C1 : ApApsExcelController.java @RequestMapping("/aps/commo n/makecommoncode.do") public void makecommoncode(httpsession session, @ModelAttribute("apApsCommonCodeVO") ApApsCommonCodeVO apapscommoncodevo, HttpServletRequest request,httpservletresponse respo nse, ModelMap model) throws Exception { 154: String target = apapscommoncodevo.gettarget(); List resultlist = apapsexcelservice.selectcommoncode(apapscommoncodevo); JSONArray jsonarray = new JSONArray(); jsonarray = JSONArray.fromObject(resultList); response.setcontenttype("text/xml;charset=utf-8"); PrintWriter printwriter = response.getwriter(); 162: printwriter.print(target); printwriter.print(jsonarra y.tostring()); printwriter.flush(); printwriter.close(); < 크로스사이트스크립트 : DOM> 다음의예제에서는 request.getparameter() 에서전달된외부의입력(name) 이 document.write() 의인자값생성에그대로사용되어취약한것으로판정한다. 1: 2: <% 3: // 외부로부터입력을받는다. 35

4: String name = request.getparameter("name"); 5: %> 6: <SCRIPT language="javascript"> 7: // 외부의입력을그대로출력한다. 8: document.write("name:" + <%=name%> ); 다음의예제처럼 paget.getcontextpath(), request.getserverport() 등의내부 함수를사용한경우에는 XSS 취약성이없는것으로판정한다. <%@ page language="java" import="java.io.*,java.text.*,java.util.*,java.net.*,com.inswave.system.config.*,com.inswave.system.except ion.*"%> AC_FL_RunContent("src", "<%=request.getcontextpath()%>/mashup/block_editor", <div id="map" class="mapstyle">server port: <%=request.getserverport()%></div></div> document.location = "http://<%=request.getservername()%>:<%=request.getserverport()%>/ntis"; 다음의코드와같이 out 결과가숫자형인경우는안전한것으로판정한다. <%=PageIndex.PageNumber(pageNo, tot_pg, action_url, imageroot, add_tag)%> 다음의예제와같이 out 결과가날자형인경우에는안전한것으로판정한다. String today = Util.getNowDateStri ng("yyyy-mm-dd"); String start = today; <input type=text size=10 name="start" value='<%=start%>' readonly> 다음의예제와같이 것으로판정한다. out 결과가프로그램내부에서정해진경우에는안전한 g1 = new WKTRead er().read(geometryparam); p1 = (Polygon) g1.buffer(double.parsedouble(buffersizeparam)); 36

response.setcontenttype("text/plain;charset=euc-kr"); response.setheader("cache-control", "no-cache"); response.getwriter().write(p1.totext()); 다음의예제와같이 response 가아닌에 System write하는경우에는안전한 것으로판정한다. // 파라메터설정 String exam_tgt_se = request.getparameter("exam_tgt_se"); String mw_take_no = request.getparameter("mw_take_no"); String apv_perm_reg_mgt_no = request.getparameter("apv_perm_reg_mgt_no"); String mw_afr_no = request.getparameter("mw_afr_no"); System.out.println("exam_tgt_se : " + exam_tgt_se); System.out.println("mw_take_no : " + mw_take_no); System.out.println("apv_perm_reg_mgt_no : " + apv_perm_reg_mgt_no); 다음의예제와같이 property가 Source 인경우 : Output String의소스가 Property인경우엔 정한다. XSS 취약점을일으킬가능성이낮으므로안전한것으로판 String swebhome = "http://" + request.getservername() + PropLoader.getWEBLOCATION() String SFTP_CabFile = swebhome +"commo n/cabfiles/filewiz.cab#version=1,0,1,161"; <OBJECT id="filewizard" style="width: 0px; HEIGHT: 0px" name="filewizard" classid="clsid:317642dd-af52-11d4-bc2a-0050da8aee6f" codebase="<%=sftp_cabfile%>"> 다음의예제와같이사용자입력값이아닌경우에는안전한것으로판정한 37

다. L1 : cmc.jsp var CompPath = "%XPLATFORM%\\" + skey + "\\component"; //XLauncher.componentpath = CompPath; XLauncher.o nlyone = "fals e"; var iconstr = "<%= req uest.getrequesturl() %>"; 마. 참고문헌 [1] CWE-80 XSS - http://cwe.mitre.org/data/definitions/80.html [2] OWASP Top 10 2010 - (OWASP 2010) A2 Cross-Site Scripting(XSS) [3] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Insecure Interaction Between Components, RANK 4 CWE-79 Improper Neutralization of Input During Web Page Generation('Cross-site Scripting') 38

4. 운영체제명령어삽입 가. 개요 적절한검증절차를거치지않은사용자입력값이운영체제명령어의일부 또는전부로구성되어실행되는경우, 의도하지않은시스템명령어가실행되 어부적절하게권한이변경되거나시스템동작및운영에악영향을미칠수있 다. 일반적으로명령어라인의인수나스트림입력등외부입력을사용하여시 스템명령어를생성하는프로그램이많이있다. 하지만이러한경우외부입 력문자열은신뢰할수없기때문에적절한처리를해주지않으면, 공격자가 원하는명령어실행이가능하게된다. < 그림 3-4> 운영체제명령어삽입 나. 보안대책 웹인터페이스를통해서버내부로시스템명령어를전달시키지않도록어플 리케이션을구성하고외부에서전달되는값을그대로시스템내부명령어로사 용하지않는다. 외부입력에따라명령어를생성하거나선택이필요한경우에는 명령어생성에필요한값들을미리지정해놓고외부입력에따라선택하여사 39

용한다. 다. 코드예제 다음의예제는 cmd.exe 명령어를사용하여 rmandb.bat 배치명령어를수행 하며, 외부에서전달되는값dir_type 이 mandb.bat의인자값으로서명령어스 트링의생성에사용된다만. 약, 외부의공격자가의도하지되지않은문자열을 전달할시, dir_type 이의도했던인자값이아닐경우, 비정상적인업무를수행 할수있다. 안전하지않은코드의예 JAVA 1: 2: props.load(in); 3: String version = props.getproperty("dir_type"); 4: String cmd = new String("cmd.exe /K \"rmandb.bat \""); 5: Runtime.getRuntime().exec(cmd + " c:\\prog_cmd\\" + version); 6: 7: 다음의예제와같이미리정의된인자값의배열을만들어놓고, 외부의입력 에따라적절한인자값을선택하도록하여, 외부의부적절한입력이명령어로 사용될가능성을배제하여야한다. 안전한코드의예 JAVA 1: 2: props.load(in); 3: String version[] = {"1.0", "1.1"; 4: int versionselection = Integer.parseInt(props.getProperty("version")); 5: String cmd = new String("cmd.exe /K \"rmandb.bat \""); 6: String vs = ""; 7: if (versionselection == 0) 8: vs = version[0]; 9: else if (versionselection == 1) 10: vs = version[1]; 40

11: else 12: vs = version[1]; 13: Runtime.getRuntime().exec(cmd + " c:\\prog_cmd\\" + vs); 14: 라. 진단방법 운영체제명령어(exec(), system(), Runtime.getRuntime().exec 등) 를실행할 수있는함수가호출되는지확인하고( ➀) 외부에서전달되는값이시스템내부 명령어의일부또는전부로사용되는지확인한다( ➁). 정해진후보군에서선택 된값(White List) 이거나적절하게검증하면안전하고그외에는취약하다. public class U78 { public void f() throws IOException { Properties props = new Properties(); String filename = "file_list"; FileInputStream in = new FileInputStream(fileName); props.load(in); // 외부에서전달된값이시스템내부명령어의일부로사용됨. String version = props.getproperty("dir_type"); ---------- ➁ String cmd = new String("cmd.exe /K \"rmandb.bat && cleanup.bat\""); // 입력값검증없이내부명령어의일부로사용됨. Runtime.getRuntime().exec(cmd + " c:\\prog_cmd\\" + version); ---------- ➀ 다음의예제에서외부입력값인 processmon의값을파라미터로 getprocessid 를호출하고, 42라인에서 execstr 문자열을만들며그문자열이 55 라인에서명령어로쓰이고있어취약한것으로판정한다. I3 : EgovProcess MonController.java @RequestMapping("/utl/sys/prm/s electprocesssttus.do") public String selectprocesssttus( @ModelAttribute("processMonVO") ProcessMonVO processmo nvo, ModelMap model ) throws Exception { 41

//System.out.printl n("filesysnm" + filesysmntrngvo.getfilesysnm()); m o d e l. a d d A t t r i b u t e ( " p r o c e s s S t t u s ", Process MonChecker.getProcessId(processMonVO.getProcessNm()));... I3 : ProcessMo nchecker.java public staticstring getprocessid( String process Nm) throws Exception { Process p = null String procssttus = null BufferedReader buf = null String result = null 42: String execstr = "tasklist /fo table /nh /fi \"imagename eq "+processnm+"\"" int cnt = 0; String str = null try { // if (Globals.OS_TYPE == null) { throw new RuntimeException("Globals.OS_TYPE property value is needed!!!"); // if ("WINDOWS".equals(Globals.OS_TYPE)) { cnt = -1;// 55: p = Runtime.getRuntime().exec(execStr); 프레임워크특성상 Property 사용은금지할수없다. 하지만시스템프로퍼 티등공용으로사용하는프로퍼티가아닌개별사용프로퍼티일경우는취약한 42

것으로, 시스템프로퍼티를사용하는경우는안전한것으로판정한다. 다음의예제에서 12라인 cmdstr 변수는 Property에서값을가져온이후 13 라인에서문자열을만드는데사용되고, 해당문자열이 15라인에서명령어로사 용되므로안전한것으로판정한다. I3 : EgovSysInfo.java public static floatgetmoryfreecpcty() throws Exception { // float cpcty = 0; Process p = null try { 12: String cmdstr = EgovProperties.getPathProperty(Globals.SERVER_CONF_PATH, "SHELL."+Globals.OS_TYPE+".getMoryInfo"); 13: Stri ng[] command = {cmd Str.replace('\\', FILE_SEPARATOR).replace('/', FILE_SEPARATOR), "FREE" 15: p = Runtime.getRuntime().exec(command); //p.waitfor(); 사용자의입력값이운영체제명령어를실행하는데들어가지않을때에는안 전한것으로판정한다. 다음의예제에서 fname은 srcfile.getname() 의결과값인파일의이름이므로 명령어에삽입되어공격할수있는 file separator 나.. 등의문자열이없으므 로취약하지않은것으로판정한다. public static String getowner(string file) throws Exception { 43

String owner = "" String src = file.replace('\\', FILE_SEPARATOR).replace('/', FILE_SEPARATOR); BufferedReader b_err=null BufferedReader b_out=null try { File srcfile = newfile(src); if (srcfile.exists()) { String parentpath = srcfile.getparent(); String fname = srcfile.getname(); Process p = null String cmdstr = EgovProperties.getProperty(Globals.SHELL_FILE_PATH, "SHELL."+Globals.OS_TYPE+".getDrctryOwner"); String[] command = {cmdstr.replace('\\', FILE_SEPARATOR).replace('/', FILE_SEPARATOR), parentpath.replace('\\', FILE_SEPARATOR).replace('/', FILE_SEPARATOR), fname; p = Runtime.getRuntime().exec(command); 마. 참고문헌 [1] CWE-78 OS injection - http://cwe.mitre.org/data/definitions/78.html [2] OWASP Top 10 2010 - (OWASP 2010) A1 Injection [3] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Insecure Interaction Between Components, RANK 2 CWE-78 Improper 44

Neutralization of Special Elements used in an OS Command('OS Command Injection') 45

5. 위험한형식파일업로드 가. 개요 서버측에서실행될수있는스크립트파일(asp, jsp, php 파일등) 이업로드가능하고, 이파일을공격자가웹을통해직접실행시킬수있는경우공격자는스크립트파일을업로드하고이파일을통해시스템내부명령어를실행하거나외부와연결하여시스템을제어할수있다. < 그림위3-5> 험한형식파일업로드 나. 보안대책 화이트리스트방식으로허용된확장자만업로드를허용한다. 업로드되는파일 을저장할때에는파일명과확장자를외부사용자가추측할수없는문자열로 변경하여저장하며, 저장경로는 web document root 밖에위치시켜서공격자 가웹을통한직접접근을차단한다. 다. 코드예제 업로드할파일에대한유효성을검사하지않으면, 위험한유형의파일을공 격자가업로드하거나전송할수있다. 안전하지않은코드의예 JAVA 46

1: 2: public void upload(httpservletrequest request) throws ServletException { 3: MultipartHttpServletRequest mrequest = (MultipartHttpServletRequest) request; 4: String next = (String) mrequest.getfilenames().next(); 5: MultipartFile file = mrequest.getfile(next); 6: 7: // MultipartFile 로부터을 file 얻음 8: String filename = file.getoriginalfilename(); 9: 10: // upload 파일에대한확장자체크를하지않음 11: File uploaddir = new File("/app/webapp/data/upload/notice"); 12: String uploadfilepath = uploaddir.getabsolutepath()+"/" + filename; 13: 14: /* 이하 file upload 루틴 */ 15: 업로드파일의확장자를검사하여허용되지않은확장자일경우업로드를제한하고있으며저, 장시외부입력된파일명을그대로사용하지않고있다. 안전한코드의예 JAVA 1: 2: public void upload(httpservletrequest request) throws ServletException { 3: MultipartHttpServletRequest mrequest = (MultipartHttpServletRequest) request; 4: String next = (String) mrequest.getfilenames().next(); 5: MultipartFile file = mrequest.getfile(next); 6: if ( file == null ) return ; 7: // 화이트리스트방식으로업로드파일의확장자를체크한다. 8: if ( filename!= null ) { 9: if ( filename.endswith(".doc") filename.endswith(".hwp") 10: filename.endswith(".pdf") filename.endswith(".xls") ) { 11: /* file 업로드루틴 */ 12: // 저장시파일명을외부사용자가추측할수없는형태로변경 13:... 14: 15: else throw new ServletExeption(" 에러"); 16: 17: /* 이하 file upload 루틴 */ 18: 47

라. 진단방법 외부입력값에서파일명을얻어오는부분이존재하는지확인하고허( ➀), 용된 확장자에대해서만파일업로드를허용하는지확인한다( ➁). 제어문등을사용 하여허용된파일만업로드될경우와업로드된파일명을외부에서알수없는형태로변경할경우엔안전하지만그외에는취약하다. public class U434 { public void upload(httpservletrequest request) throws ServletException { // MultipartHttpServletRequest 를케스팅 MultipartHttpServletRequest mrequest = (MultipartHttpServletRequest) request; String next = (String) mrequest.getfilenames().next(); MultipartFile file = mrequest.getfile(next); // MultipartFile로부터이름 file 을얻어옴 String filename = file.getoriginalfilename(); ------------- ➀ // upload 파일에대한확장자유효성체크를하지않음 File uploaddir = new File("/app/webapp/data/upload/notice"); String uploadfilepath = uploaddir.getabsolutepath()+"/"+filename; ------------- ➁ /* 이하 file upload 루틴 */ getoriginalfilename() 함수를사용하여파일을업로드하면서해당파일의확 장자를체크하지않는경우엔취약하다고판정한다. 다음의예제는업로드된파일의확장자를체크하고있지않으므로취약한것 으로판정한다. while (itr.hasnext()) { Entry<String, List<MultipartFile>> entry = itr.next(); filelist = entry.getvalue(); for(int i=0, s=filelist.size(); i<s; i++) { file = (MultipartFile)fileList.get(i); 48

String orginfilename = file.getorigi nalfilename(); //-------------------------------------- // 원파일명이없는경우처리 // ( 첨부가되지않은 inp ut file type) //-------------------------------------- if ("".equals(orginfilename)) continue; ////------------------------------------ int index = orginfilename.lastindexof("."); //String filename = orgi nfilename.substring(0, index); String fileext = orginfil ename.substring(index + 1); String newname = KeyStr + EgovStringUtil.getTimeStamp() + filekey; long _size = file.getsize(); if (!"".equals(orginfilename)) { filepath = storepathstring + File.separator + newname; file.transferto(new File(filePath)); fvo = new FileVO(); fvo.setfileextsn(fileext); fvo.setfilestrecours(storepathstring); fvo.setfilemg(long.tostring(_size)); fvo.setorignlfilenm(orginfilename); fvo.setstrefilenm(newname); fvo.setatchfileid(atchfileidstring); fvo.setfilesn(string.valueof(fil ekey)); //writefile(file, newname, storepathstri ng); result.add(fvo); filekey++; 49

return result; 다음의예제는파일의확장자를체크하여필터링하고있으므로안전하다. String orginfilename = file.getoriginalfilename() //-------------------------------------- // 원파일명이없는경우처리 // ( 첨부가되지않은 inp ut file type) //-------------------------------------- if ("".equals(orginfilename)) { continue ////------------------------------------ int index = orginfilename.lastindexof("."); //String filename = orgi nfilename.substring(0, index); String fileext = orginfilename.substring(index + 1); /* 확장자체크 */ for(object fileexclusionext : this.fileexclusionextension) { if( ((String) fileexclusionext).equals(fileext.tolowercase())) { throw new Exception("egume.message.error.file.exclusion.extension"); 다음의예제와같이 getoriginalfilename() 의리턴값을저장하여사용하지 않으면, 파일명을사용하기위해다른곳에서해당함수를호출할것이므로취 약하지않다고판정한다. if (!"".equals(file.getoriginalfilename())) { zipmanageservice.insertexcelzip(file.getinputstream()); 50

마. 참고문헌 [1] CWE-434 Unrestricted Upload of File with Dangerous Type - http://cwe.mitre.org/data/definitions/434.html [2] OWASP Top Ten 2007 A3, Malicious File Execution [3] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Insecure Interaction Between Components, RANK 9 CWE-434 Unrestricted Upload of File with Dangrous Type 51

6. 신뢰되지않는 URL 주소로의자동접속연결 가. 개요 사용자로부터입력되는값을외부사이트의주소로사용하여자동으로연결하는 서버프로그램은피싱공격에 (phishing) 노출되는취약점을가질수있다. 일반적으로클라이언트에서전송된주URL 소로연결하기때문에안전하다고 생각할수있으나, 해당폼의요청을변조함으로써공격자는사용자가위험한 URL 로접속할수있도록공격할수있다. 나. 보안대책 자동연결할외부사이트의과 URL 도메인은화이트리스트로관리하고, 사 용자입력값을자동연결할사이트주소로사용하는경우에는입력된값이화 이트리스트에존재하는지확인해야한다. 다. 진단방법 사이트자동연결기능을구현하고있고, 대상사이트주소를사용자입력을 통해동적으로결정하고있으나주소에대한검증절차가없다면취약한것으로 판정한다. 52

다. 코드예제 다음과같은코드가서버에존재할경우공격자는다음과같은링크를희생자가접근하도록함으로써희생자가피싱사이트등으로접근하도록할수있다. (<a href="http://bank.example.com/redirect?url=http://attacker.example.net">click</a>) 안전하지않은코드의예 JAVA 1: 2: protected void doget(httpservletrequest request, HttpServletResponse response) 3: throws ServletException, IOException { 4: String query = request.getquerystring(); 5: if (query.contains("url")) { 6: String url = request.getparameter("url"); 7: response.sendredirect(url); 8: 9: 다음의예제와같이, 외부로연결할과 URL 도메인들은화이트리스트를작 성한후, 그중에서선택함으로써안전하지않은사이트로의접근을차단할수 있다. 안전한코드의예 JAVA 1: protected void doget(httpservletrequest request, HttpServletResponse response) 2: throws ServletException, IOException { 3: 4: // 다른페이지이동하는 URL 리스트를만든다. 5: String allowurl[] = { "http://url1.com", "http://url2.com", "http://url3.com" ; 6: 7: // 입력받는은 url 미리정해진 URL의로 order 받는다. 8: 9: String nurl = request.getparameter("nurl"); 10: 11: try { 12: Integer n = Integer.parseInt(nurl); 53

13: if ( n >= 0 && n < 3) 14: response.sendredirect(allowurl[n]); 15: catch (NumberFormatException nfe) { 16: // 사용자입력값이숫자가아닐경우적절히에러를처리한다. 17: 18: 19: 라. 진단방법 외부사이트로리다이렉션하는함수나메소드(java의경우 response.sendredirect 메소드) 가존재하는지확인하고( ➀), 리다이렉션하는함 수나메소드의인자값이 (url) 외부입력값인지확인한다( ➁). 일반적으로외부에 서입력받는변수가아닌경우안전하다고판정한다. 외부에서입력받는변수 인경우, 다이렉션하는함수나메소드의인자값(url) 을관리하는지확인해서 유효값검사및화이트리스트로관리하는경우안전한것으로판정한다. public class U601 extends HttpServlet { protected void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { String query = request.getquerystring(); --------------------- ➁ if (query.contains("url")) { String url = request.getparameter("url"); // url에대한유효성점검없이의 sendredirect 인자로사용 if( url!= null ) { url = url.replaceall("\r", "").replaceall("\n", ""); response.sendredirect(url); --------------------- ➀ 외부입력값을이용하여 URL을이동하는 response.sendredirect(string url) 함수가사용된다면취약하다. 다음의예제에서 19, 20 라인에서 code, action을 request 에서받아온다. 이후 66번째라인에서페이지이동 URL에값code, action 을사용하여 URL을만든 54

다. M1 : redirect.jsp <% //redirect 19: String code = nvl(request.getattribute("redirectcode")); 20: String action = nvl(request.getattribute("action")); logger.debug(">>> redirectcode - " + code); logger.debug(">>> redirectaction - " + action);... 66: se.sendredirect(cp + action + "?redirectcode=" + code); return; %> 다음의예제에서 HttpRequest.getContextPath() 함수는내장함수로 context path를리턴하므로안전한 URL 이다. response.sendredirect(request.getcontextpath() + "/login.do"); 마. 참고문헌 [1] CWE-601 URL Redirection to Untrusted Site - http://cwe.mitre.org/data/definitions/601.html [2] OWASP Top Ten 2010 Category A10 - Unvalidated Redirects and Forward [3] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Insecure Interaction Between Components, RANK 22 CWE-601 URL Redirection to Untrusted Site('Open Redirect') 55

7. XQuery 삽입 가. 개요 XQuery를사용하여 XML 데이터에대한동적쿼리문을생성할때사용하 는외부입력값에대해적절한검증절차가존재하지않으면공격자가쿼리문의 구조를임의로변경할수있게된다. 이로인해허가되지않은데이터를조회 하거나인증절차를우회할수있다. 나. 보안대책 XQuery 에사용되는외부입력데이터에대하여특수문자및쿼리예약어를 필터링하고, XQuery를사용한쿼리문은스트링을연결하는형태로구성하지 않고인자( 파라메터) 화된쿼리문을사용한다. 다. 코드예제 다음의예제에서는외부의입력(name) 값을 executequery 를사용한쿼리생성 의문자열인자생성에사용하고있다. 만일을 something' or '='1 name의값 으로전달하면다음과같은쿼리문을수행할수있으며, 이를통해파일내의 모든값을출력할수있게된다. (doc('users.xml')/userlist/user[uname='something' or '=') 안전하지않은코드의예 JAVA 1: 2: // 외부로부터입력을받음 3: String name = props.getproperty("name"); 4: Hashtable env = new Hashtable(); 5: env.put(context.initial_context_factory, "com.sun.jndi.ldap.ldapctxfactory"); 6: env.put(context.provider_url, "ldap://localhost:389/o=rootdir"); 7: javax.naming.directory.dircontext ctx = new InitialDirContext(env); 8: javax.xml.xquery.xqdatasource xqds = 56

9: (javax.xml.xquery.xqdatasource) ctx.lookup("xqj/personnel"); 10: javax.xml.xquery.xqconnection conn = xqds.getconnection(); 11: 12: String es = "doc('users.xml')/userlist/user[uname='" + name + "']"; 13: // 입력값이 Xquery 의인자로사용 14: XQPreparedExpression expr = conn.prepareexpression(es); 15: XQResultSequence result = expr.executequery(); 16: while (result.next()) { 17: String str = result.getatomicvalue(); 18: if (str.indexof('>') < 0) { 19: System.out.println(str); 20: 21: 다음의예제에서는외부입력값을받고해당값기반의 XQuery상의쿼리구 조를변경시키지않는 bindxxx 함수를이용함으로써외부의입력으로인하여 쿼리구조가바뀌는것을막을수있다. 안전한코드의예 JAVA 1: 2: // 외부로부터입력을받음 3: String name = props.getproperty("name"); 4: Hashtable env = new Hashtable(); 5: env.put(context.initial_context_factory, "com.sun.jndi.ldap.ldapctxfactory"); 6: env.put(context.provider_url, "ldap://localhost:389/o=rootdir"); 7: javax.naming.directory.dircontext ctx = new InitialDirContext(env); 8: javax.xml.xquery.xqdatasource xqds = 9: (javax.xml.xquery.xqdatasource) ctx.lookup("xqj/personnel"); 10: javax.xml.xquery.xqconnection conn = xqds.getconnection(); 11: 12: String es = "doc('users.xml')/userlist/user[uname='$xpathname']"; 13: // 입력값이 Xquery 의인자로사용 14: XQPreparedExpression expr = conn.prepareexpression(es); 15: expr.bindstring(new QName("xpathname"), name, null); 16: XQResultSequence result = expr.executequery(); 17: while (result.next()) { 18: String str = result.getatomicvalue(); 19: if (str.indexof('>') < 0) { 57

20: System.out.println(str); 21: 22: 라. 진단절차 XQuery 가실행되는부분을확인하고( ➀), XQuery 쿼리스트링에사용되는변 수가외부입력값여부를확인한후( ➁), 변수에대한필터링모듈이존재하는 지확인한다. 필터링모듈이존재하거나관련프레임워크에서적절한조치할 경우안전한것으로판정한다. // 외부로부터입력을받음 String name = props.getproperty("name"); ------------------------ ➁ Hashtable env = new Hashtable(); env.put(context.initial_context_factory, "com.sun.jndi.ldap.ldapctxfactory"); env.put(context.provider_url, "ldap://localhost:389/o=rootdir"); javax.naming.directory.dircontext ctx = new InitialDirContext(env); javax.xml.xquery.xqdatasource xqds = (javax.xml.xquery.xqdatasource) ctx.lookup("xqj/personnel"); javax.xml.xquery.xqconnection conn = xqds.getconnection(); String es = "doc('users.xml')/userlist/user[uname='" + name + "']"; // 입력값이의 Xquery 인자로사용 XQPreparedExpression expr = conn.prepareexpre ssion(es); XQResultSequence result = expr.executequery(); while (result.next()) {... String str = result.getatomicvalue(); if (str.indexof('>') < 0) { System.out.println(str); --------------- ➁ ------------------------ ➀ 다음의코드에서는외부의입력(name) 값을 executequery를사용한쿼리생성 의문자열인자생성에사용하고있다만. 일다음과 something' or '='1 을 name 의값으로전달하면다음과같은쿼리문을수행할수있으며, 이를통해 파일내의모든값을출력할수있게되어취약하다. (doc('users.xml')/userlist/user[uname='something' or '=') 58

1: 2: // 외부로부터입력을받음 3: String name = props.getproperty("name"); 4: Hashtable env = new Hashtable(); 5: env.put(context.initial_context_factory, "com.sun.jndi.ldap.ldapctxfactory"); 6: env.put(context.provider_url, "ldap://localhost:389/o=rootdir"); 7: javax.naming.directory.dircontext ctx = new InitialDirContext(env); 8: javax.xml.xquery.xqdatasource xqds = 9: (javax.xml.xquery.xqdatasource) ctx.lookup("xqj/personnel"); 10: javax.xml.xquery.xqconnection conn = xqds.getconnection(); 11: 12: String es = "doc('us ers.xml')/us erlist/user[uname='" + name + "']"; 13: // 입력값이 Xquery 의인자로사용 14: XQPreparedExpr essio n expr = conn.prepareexpression(es); 15: XQResultSequence result = expr.executequery(); 16: while (result.next()) { 17: String str = res ult.getatomicvalue(); 18: if (str.indexof('>') < 0) { 19: System.out.println(str); 20: 21: 마. 참고문헌 [1] CWE-652 XQuery injection - http://cwe.mitre.org/data/definitions/652.html [2] OWASP Top 10 2010 A1 Injection Flaws 59

8. XPath 삽입 가. 개요 외부입력값을적절한검사과정없이쿼XPath 리문생성을위한문자열로 사용하면, 공격자는프로그래머가의도하지않았던문자열을전달하여쿼리문 의의미를왜곡시키거나그구조를변경하고임의의쿼리를실행하여인가되지 않은데이터를열람할수있다. < 그림삽입 3-6> XPath 나. 보안대책 Xpath 쿼리에사용되는외부입력데이터에대하여특수문자(", [, ], /, =, @ 등) 및쿼리예약어필터링을수행하고인자화된쿼리문을지원하는 XQuery를 사용한다. 다. 코드예제 name 의값으로 user1, passwd 의값으로 ' or ''=' 을전달하면다음과같 은쿼리문이생성되어인증과정을거치지않고로그인할수있다. 60

(//users/user[login/text()= user1' or ''='' and password/text() = or = ]/home_dir/text()) 안전하지않은코드의예 JAVA 1: 2: // 외부로부터입력을받음 3: String name = props.getproperty("name"); 4: String passwd = props.getproperty("password"); 5: 6: XPathFactory factory = XPathFactory.newInstance(); 7: XPath xpath = factory.newxpath(); 8: 9: // 외부입력이의 xpath 인자로사용 10: XPathExpression expr = xpath.compile("//users/user[login/text()='" + name 11: + "' and password/text() = '" + passwd + "']/home_dir/text()"); 12: Object result = expr.evaluate(doc, XPathConstants.NODESET); 13: NodeList nodes = (NodeList) result; 14: for (int i = 0; i < nodes.getlength(); i++) { 15: String value = nodes.item(i).getnodevalue(); 16: if (value.indexof(">") < 0) { 17: System.out.println(value); 18: 19: 인자화된쿼리문을지원하는를 XQuery 사용하여미리쿼리골격을생성하 고, 이에인자값을설정함으로써외부입력으로인해쿼리구조가바뀌는것을 막을수있다. 안전한코드의예 JAVA dologin.xp 파일 1: declare variable $loginid as xs:string external; 2: declare variable $password as xs:string external; 3: //users/user[@loginid=$loginid and @password=$password] XQuery 를이용한 XPath Injection 방지 1: // 외부로부터입력을받음 2: String name = props.getproperty("name"); 3: String passwd = props.getproperty("password"); 61

4: Document doc = new Builder().build("users.xml"); 5: // XQuery 를위한정보로딩 6: XQuery xquery = new XQueryFactory().createXQuery(new File("dologin.xq")); 7: Map vars = new HashMap(); 8: vars.put("loginid", name); 9: vars.put("password", passwd); 10: Nodes results = xquery.execute(doc, null, vars).tonodes(); 11: for (int i=0; i < results.size(); i++) { 12: System.out.println(results.get(i).toXML()); 13: 라. 진단방법 xpath 객체를통해쿼리스트링이컴파일되는부분을확인하고( ➀), xpath 쿼리스트링에사용되는변수가외부입력값인지확인한후( ➁) 변수에대한 필터링모듈이존재하는지확인한다. 필터링모듈이존재하거나관련프레임워 크에서적절하게조치된경우엔안전하다고판정한다.... String acctid = request.getparameter("acctid"); ------------------------ ➁ String query = null; if(acctid!= null) { StringBuffer sb = new StringBuffer("/accounts/account[acctID='"); sb.append(acctid); ------------------------ ➁ sb.append("']/email/text()"); query = sb.tostring(); DocumentBuilderFac tory domfactory = DocumentBuilderFactory.newInstance(); domfactory.setnamespaceaware(true); DocumentBuilder builder = domfactory.newdocumentbuilder(); Document doc = builder.parse("accounts.xml"); XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newxpath(); XPathExpression expr = xpath.compile(query); ------------------------ ➀ Object result = expr.evaluate(doc, XPathConstants.NODESET);... 다음의예제에서는 name 의값으로의 user1, passwd 값으로 ' or ''=' 을전달하면다음과같은쿼리문이생성되어인증과정을거치지않고로 62

그인할수있어취약하다고판정한다. (//users/user[login/text()= user1' or ''='' and password/text() = or = ]/home_dir/text()) 1: 2: // 외부로부터입력을받음 3: String name = props.getproperty("name"); 4: String passwd = props.getproperty("password"); 5: 6: XPathFactory factory = XPathFactory.newInstance(); 7: XPath xpath = factory.newxpath(); 8: 9: // 외부입력이의 xpath 인자로사용 10: XPathExpression expr = xpath.compile("//users/user[login/text()='" + name 11: + "' and password/text() = '" + passwd + "']/home_dir/text()"); 12: Object r esult = expr.evaluate(doc, XPathConstants.NODESET); 13: NodeList nodes = (NodeList) result; 14: for (int i = 0; i < nodes.getlength(); i++) { 15: String value = nodes.item(i).getnod evalue(); 16: if (value.indexof(">") < 0) { 17: System.out.println(value); 18: 19: 마. 참고문헌 [1] CWE-643 XPath Injection - http://cwe.mitre.org/data/definitions/643.html [2] OWASP Top 10 2010 A1 Injection Flaws [3] Web Application Security Consortium. "XPath Injection" http://www.webappsec.org/projects/threat/classes/xpath_injection.shtml 63

9. LDAP 삽입 가. 개요 공격자가외부입력을통해서의도하지않은 LDAP 명령어를수행할수있 다. 즉, 웹애플리케이션이사용자가제공한입력을올바르게처리하지못하면, 공격자가 LDAP 명령문의구성을바꿀수있다. 이로인해프로세스가명령을 실행한컴포넌트와동일한권한(authentication) 을가지고동작하게된다. LDAP 쿼리문이나결과로외부입력이부분적으로적절한처리없이사용되 면, LDAP 쿼리문이실행될때공격자는 LDAP 쿼리문의내용을마음대로변 경할수있다. < 그림삽입 3-7>LDAP 나. 보안대책 DN 과필터에사용되는사용자입력값에는특수문자가포함되지않도록특 수문자를제거한다. 특수문자를사용해야하는경우특수문자(DN 에사용되는 64

특수문자는 \, 필터에사용되는특수문자(=, +, <, >, #, ; \ 등) 에대해서는 실행명령이아닌일반문자로인식되도록처리한다. 다. 코드예제 name 변수의값으로을 "*" 전달할경우필터문자열은 "(name=*)" 가되어항 상참이되며이는의도하지않은동작을유발시킬수있다. 안전하지않은코드의예 JAVA 1: Properties props = new Properties(); 2: String filename = "ldap.properties"; 3: FileInputStream in = new FileInputStream(fileName); 4: props.load(in); 5: String name = props.getproperty("name"); 6: String filter = "(name =" + name + ")"; 7: NamingEnumeration answer = ctx.search("ou=newhires", filter, new SearchControls()); 8: printsearchenumeration(answer); 9: ctx.close(); 검색을위한필터문자열로사용되는외부의입력에서위험한문자열을제거 하여위험성을부분적으로감소시킬수있다. 안전한코드의예 JAVA 1: Properties props = new Properties(); 2: String filename = "ldap.properties"; 3: FileInputStream in = new FileInputStream(fileName); 4: if (in == null in.available() <= 0) return; 5: props.load(in); 6: if (props == null props.isempty()) return; 7: String name = props.getproperty("name"); 8: if (name == null "".equals(name)) return; 9: String filter = "(name =" + name.replaceall("\\*", "") + ")"; 10: NamingEnumeration answer = 11: ctx.search("ou=newhires", filter, new SearchControls()); 12: printsearchenumeration(answer); 13: ctx.close(); 65

라. 진단방법 LDAP 조회쿼리가실행됨을확인하고( ➀), LDAP 조회문의필터에사용되는 변수가외부입력값인지확인한후( ➁), 해당변수에대한필터링모듈이존재 하는지확인한다( ➂). 필터링모듈이존재하거나관련프레임워크에서적절히 조치할경우엔안전한것으로판정한다.... DirContext ctx = new InitialDirContext(env); String managername = request.getparameter("managername"); ------------------ ➂ //retrieve all of the employees who report to a manager String filter = "(manager=" + managername + ")"; ------------------------ ➁ NamingEnumeration employees = ctx.search("ou=people,dc=example,dc=com", filter); -- ➀... 다음의예제에서는외부의입력(name) 이검색을위한필터문자열의생성에 사용되고있다변. name 수의값으로 "*" 을전달할경우, 필터문자열은 "(name=*)" 가할당되어항상참이되므로, 인증을우회하거나 SW가의도하지 않은동작을하게되므로취약한것으로판정한다. 1: 2: public void f() { 3: Hashtable env = new Hashtable(); 4: env.put(context.initial_context_factory, "com.sun.jndi.ldap.ldapctxfactory"); 5: env.put(context.provider_url, "ldap://localhost:389/o=rootdir"); 6: try { 7: javax.naming.directory.dircontext ctx = new InitialDirContext(env); 8: // 프로퍼티를만들고외부파일을로드한다. 9: Properties props = new Properties(); 10: String filename = "ldap.prop erties"; 11: FileInp utstream in = new FileInp utstream(filename); 12: props.load(in); 13: // LDAP Search 를하기위해을 name 읽는다 14: String name = props.getprop erty("name"); 66

15: String filter = "(name =" + name + ")"; 16: // LDAP search가값에 name 대한여과없이그대로통과되어검색이되어진다. 17: NamingEnumeration answer = ctx.search("ou=newhires", filter, new SearchControls()); 18: printsearchenumeration(answer); 19: ctx.close(); 20: catch (NamingException e) { 21: 외부의입력(name) 이검색을위한 base 문자열의생성에사용되고있다. 이 경우임의의루트디렉터리를지정하여정보에접근할수있으며, 적절한접근 제어가동반되지않을경우정보누출이발생할수있다. 1: 2: try { 3: 4: // 외부로부터입력을받는다. 5: String name = props.getproperty( ldap.properties"); 6: // 입력값에대한를 BasicAttribute 생성한다. 7: BasicAttribute attr = new BasicAttribute("name", name); 8: // 외부입력값이의 LDAP search 인자로사용이된다. 9: NamingEnumeration answer = 10: ctx.search("ou=newhires", attr.getid(), new SearchControls()); 11: printsearchenumeration(answer); 12: ctx.close(); 13: catch (NamingException e) { 14: 15: 16: public void printsearchenumeration(nami ngenumeration val ue) { 67

17: try { 18: while (value.hasmore()) { 19: SearchResult sr = (SearchResult) val ue.next(); 20: System.out.println(">>>" + sr.getname() + "\n" + sr.getattributes()); 21: 22: catch (NamingException e) { 23: 마. 참고문헌 [1] CWE-90 LDAP Injection - http://cwe.mitre.org/data/definitions/90.html [2] OWASP Top 10 2010 A1 Injection Flaws [3] SPI Dynamics. "Web Applications and LDAP Injection" [4] SANS Top 25 2009 - (SANS 2009) Insecure Interaction - CWE ID 116 Improper Encoding or Escaping of Output 68

10. 크로스사이트요청위조 가. 개요 특정웹사이트에대해서사용자가인지하지못한상황에서사용자의의도와 는무관하게공격자가의도한행위( 수정, 삭제, 등록등 ) 를요청하게하는공격을말 한다. 웹어플리케이션이사용자로부터받은요청에대해서사용자가의도한 대로작성되고전송된것인지확인하지않는경우발생가능하고특히해당사 용자가관리자인경우사용자권한관리, 게시물삭제, 사용자등록등관리자권 한으로만수행가능한기능을공격자의의도대로실행시킬수있게된다. 공격자는사용자가인증한세션이특정동작을수행하여도계속유지되어정 상적인요청과비정상적인요청을구분하지못하는점을악용하여피해가발생한다. 웹응용프로그램에요청을전달할경우, 해당요청의적법성을입증하기위하여 전달되는값이고정되어있고이러한자료가 GET 방식으로전달된다면공격자 가이를쉽게알아내어원하는요청을보냄으로써위험한작업을요청할수있 게된다. < 그림 3-8> 크로스사이트요청위조 나. 보안대책 입력화면폼작성시 GET 방식보다는 POST 방식을사용하고입력화면폼과 69

해당입력을처리하는프로그램사이에토큰을사용하여, 공격자의직접적인 URL 사용이동작하지않도록처리한다. 특히중요한기능에대해서는사용자 세션검증과더불어재인증을유도한다. 라. 코드예제 GET방식은단순히 form 데이터를 URL 뒤에덧붙여서전송하기때문에 GET 방식의 form을사용하면전달값이노출되므로 CSRF 공격에쉽게노출 될수있다. 안전하지않은코드의예 JAVA 1: 2: <form name="myform" method="get" action="customer.do"> 3: <input type=text name="txt1"> 4: <input type=submit value=" 보내기"> 5: </form> 6: Post 방식을사용하여위협을최소화한다. 안전한코드의예 JAVA 1: 2: <form name="myform" method="post" action="customer.do"> 3: <input type=text name="txt1"> 4: <input type=submit value=" 보내기"> 5: </form> 6: 라. 진단방법 사용자권한변경, 신규정보등록등주요기능을확인하고( ➀), 해당기능 수행시권한확인절차존재여부확인한다( ➁). 권한확인절차가없거나권한확 인방법이세션쿠키, 사용자 IP, SSL 인증과같이자동제출되는자격증명에 의존하는경우취약하다. 70

... try { int level = request.getparameter("level"); int group = request.getparameter("group"); String id = request.getparameter("id"); ------------------------ ➁ String sql="update member set level=?, group=? where id=?;"; pstmt =con.preparestatement(sql); pstmt.setint(1, level); pstmt.setstring(2, group); pstmt.setint(3, id); pstmt.executeupdate();-- ➀ catch (Exception e) { e.printstacktrace(); finally { if ( con!= null ) try { con.close(); catch (SQLException se) {/* 처리*/ if ( pstmt!= null ) try { cpstmt.close(); catch (SQLException se2) {/* 처리*/ GET방식은단순히 form 데이터를 URL 뒤에덧붙여서전송하기때문에 GET 방식의 form을사용하면전달값이노출되므로 CSRF 공격에쉽게노출 될수있다. 1: 2: <form name="myform" method="get" action="customer.do"> 3: <input type=text name="txt1"> 4: <input type=submit value=" 보내기"> 5: </form> 6: 마. 참고문헌 [1] CWE-352 CSRF - http://cwe.mitre.org/data/definitions/352.html [2] OWASP Top Ten 2010 Category A5 - Cross-Site Request Forgery(CSRF) [3] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Insecure 71

Interaction Between Components, RANK 12 CWE-352 Cross-Site Request Forgery(CSRF) 72

11. 디렉토리경로조작 가. 개요 외부의입력을통하여 디렉터리경로문자열 생성이필요한경우, 외부입 력값에대해경로조작에사용될수있는문자를필터링하지않으면, 예상밖 의접근제한영역에대한경로문자열구성이가능해져시스템정보누출, 서 비스장애등을유발시킬수있다. 즉, 경로조작을통해서공격자가허용되지않은권한을획득하여, 설정에관 계된파일을변경할수있거나실행시킬수있다. < 그림디3-9> 렉토리경로조작 나. 보안대책 파일경로와이름을생성할때외부입력값을사용하는경우, 정해진경로이 외의디렉토리와파일에접근할수없도록처리하고외부입력값에대해 replaceall() 등의메소드를사용하여예상밖의경로로의접근을허용하는위 험문자열(",/,\,..) 을제거하는필터링을수행한다. 다. 코드예제 외부의입력 (name) 이삭제할파일의경로설정에사용되고있다만. 일공격자 73

에의해 name 의값으로../../../rootFile.txt와같은값을전달하면의도하지않 았던파일이삭제되어시스템에악영향을준다. 안전하지않은코드의예 JAVA 1: 2: public void f(properties request) { 3: 4: String name = request.getproperty("filename"); 5: if( name!= null ) { 6: File file = new File("/usr/local/tmp/" + name); 7: file.delete(); 8: 9: 10: 외부에서입력되는값에대하여 Null 여부를체크하고, 외부에서입력되는파 일이름 (name) 에서상대경로(/, \\, &,. 등특수문자) 를설정할수없도록 replaceall 을이용하여특수문자를제거한다. 안전한코드의예 JAVA 1: 2: public void f(properties request) { 3: 4: String name = request.getproperty("user"); 5: if ( name!= null &&!"".equals(name) ) { 6: name = name.replaceall("/", ""); 7: name = name.replaceall("\\", ""); 8: name = name.replaceall(".", ""); 9: name = name.replaceall("&", ""); 10: name = name + "-report"; 11: File file = new File("/usr/local/tmp/" + name); 12: if (file!= null) file.delete(); 13: 14: 15: 라. 진단방법 74

디렉토리경로를사용하여파일을생성확인하고( ➀), 파일경로구성시외부 입력값을사용하는지확인한후( ➁) 외부입력값에대한필터링또는검증절차 가있는지확인한다( ➂). 외부입력값에대한필터링절차가없다면취약하다.... MultipartHttpServle tre quest mrequest = (MultipartHttpServletRequest) request; MultipartFile multifile = mrequest.getfile("uploadfile"); String tmpfilename = multifile.getoriginalfilename(); ---------------------------- ➂ String startfilename = "/download/" ; if( multifile!= null ) { in putstream = multifile.getinputstream(); String filepath = startfilename + tmpfilename; outputstream = new FileOutputStream(filePath); --------------------- ➁ ---------------------- ➀ int readbytes = 0; byte[] buffer = new byte[8192]; while((readbytes = inputstream.read(buffer, 0, 8192))!= -1) { outputstream. write(buffer, 0, readbytes); outputstream.close(); in putstream.close();... 마. 참고문헌 [1] CWE-23 Relative Path Traversal - http://cwe.mitre.org/data/definitions/23.html [2] OWASP Top 10 2010 A4 Inseccure Direct Object Reference [3] Security Technical Implementation Guide Version 2 - (STIG 2) APP3510 CAT I, APP3600 CAT II [4] SANS Top 25 2009 - (SANS 2009) Risky Resource Management - CWE ID 426 75

12. HTTP 응답분할 가. 개요 HTTP 요청에들어있는인자값이 HTTP 응답헤더에포함되어사용자에게 다시전달될때입력값에 CR(Carriage Return) 이나와 LF(Line Feed) 같은개 행문자가존재하면 HTTP 응답이개 2 이상으로분리될수있다. 이경우공격 자는개행문자를이용하여첫번째응답을종료시키고두번째응답에악의적 인코드를주입하여및 XSS 캐시훼손(cache poisoning) 공격등을수행할수 있다. < 그림응3-10> HTTP 답분할 나. 보안대책 외부에서입력된인자값을응HTTP 답헤더(Set Cookie 등) 에포함시킬경우 CR, LF 등의개행문자를제거한다. 다. 코드예제 외부의입력값을사용하여반환되는쿠키의값을설정하고있다. 그런데, 공 격자가 "Wiley Hacker\r\nHTTP/1.1 200 OK\r\n" 를 authorname 의값으로설 정할경우, 예와같이의도하지않은두개의페이지가전달되며, 두번째응답 76

페이지는공격자가마음대로수정가능하다. 안전하지않은코드의예 JAVA 1: throws IOException, ServletException { 2: response.setcontenttype("text/html"); 3: String author = request.getparameter("authorname"); 4: Cookie cookie = new Cookie("replidedAuthor", author); 5: cookie.setmaxage(1000); 6: response.addcookie(cookie); 7: RequestDispatcher frd = request.getrequestdispatcher("cookietest.jsp"); 8: frd.forward(request, response); 9: 10: 외부에서입력되는값에대하여 Null 여부를체크하고, 헤더값이두개로나누 어지는것을방지하기위해 replaceall 을이용하여개행문자(\r, \n) 를제거한 다. 안전한코드의예 JAVA 1: throws IOException, ServletException { 2: response.setcontenttype("text/html"); 3: String author = request.getparameter("authorname"); 4: if (author == null "".equals(author)) return; 5: String filtered_author = author.replaceall("\r", "").replaceall("\n", ""); 6: Cookie cookie = new Cookie("replidedAuthor", filtered_author); 7: cookie.setmaxage(1000); 8: cookie.setsecure(true); 9: response.addcookie(cookie); 10: RequestDispatcher frd = request.getrequestdispatcher("cookietest.jsp"); 11: frd.forward(request, response); 12: 13: 라. 진단방법 Response 헤더에변수가사용되는것을확인하고변 ( ➀), 수가외부입력값인 지확인한후( ➁), 개행문자(\r, \n) 제거를위한필터링또는검증절차가있는 지확인한다. 외부입력값에대한필터링절차가없다면취약하다. 77

protected void common(httpservletrequest request, HttpServletResponse response) throws Exception { String fileid = request.getparameter("fileid"); --------------------- ➁ BufferedInputStream in = null; String uploadpath = properties.getstring(propertyname + "UPLOAD"); try { String filename = uploadpath + "/" + fileid; response.setcontenttype(mimetype + ";charset=utf-8"); String filename = java.net.urlencoder.encode(fileid, "UTF-8"); response.setheader("content-disposition", "attachment; filename=\"" + filename + "\""); ---------------------- ➀ response.setheader("content-transfer-encoding", "binary"); response.getoutputstream().flush(); response.getoutputstream().close(); catch(exception e) { if(in!=null) { in.close(); finally { HTTP 응답분할은공격자보낸조작된입력값이헤더에쓰이고이로인해 HTTP 응답이나누어지게되는취약점이다. response.sendredirect 함수는 URL 을다른곳으로보내는함수인데그방식은헤더에서상태값을로 301 바꾸고 가고자하는 URL 을헤더에입력하는것이다. 이때 url에공격자의입력값이 쓰이게되면조작된입력값으로인해응답이변경된다. 다음의예제를살펴보면, 25번째라인에서 request의파라미터에서 filename 값을가져오고번27 째라인에서값filename 을이용하여헤더에값을설정하고 있어취약하다. L1 : DownloadServlet.java 78

public voidservice(httpservletrequest request, HttpServletResponse res) throws ServletException, IOException { String contextrealpath = request.getsession().getservletcontext().getrealpath("/"); String savepath = contextrealpath + "upfolder" res.setcontenttype("utf-8"); 25: String filename = request.getparameter("file"); res.setcontenttype("application/octet;charset=utf-8"); 27: res.setheader("content-disposition", "attachment;filename=" + filename); HttpRequest.getContextPath() 함수는내장함수로 context path를리턴하므 로 HTTP 응답분할이이루어지지않는다. response.sendredirect(request.getcontextpath() + "/login.do"); Long, Integer 값등값Numeric 은문자열로치환했을때 \r\n 등의문자열 이포함될수없으므로취약하지않다. response.setheader("content-length",long.tostring(file.length())); 마. 참고문헌 [1] CWE-113 HTTP Response Splitting - http://cwe.mitre.org/data/definitions/113.html [2] OWASP Top 10 2004 A1 Unvalidated Input [3] OWASP Top 10 2007 A2 Injection Flaws [4] Web Application Security Consortium 24 + 2 HTTP Response Splitting 79

13. 정수오버플로우 가. 개요 정수형변수의오버플로우는정수값이증가하면서, Java에서허용된가장큰 값보다더커져서실제저장되는값은의도하지않게아주작은수이거나음수 가될수있다. 특히반복문제어, 메모리할당, 메모리복사등을위한조건으 로사용자가제공하는입력값을사용하고그과정에서정수오버플로우가발생 하는경우보안상문제를유발할수있다. 나. 보안대책 언어/ 플래폼별정수타입의범위를확인하여사용한다. 정수형변수를연산 에사용하는경우결과값이범위체크하는모듈을사용한다. 특히외부입력값 을동적으로할당하여사용하는경우변수의값범위를검사하여적절한범위 내에존재하는값인지확인한다. 다. 코드예제 다음의예제는외부의입력 (args[0], args[1] ) 을이용하여동적으로계산한값 을배열의크기 (size) 를결정하는데사용하고있다만. 일외부입력으로부터계 산된값 (size) 이오버플로우에의해음수값이되면배열의크기가음수가되어 코드시스템에문제가발생할수있다. 안전하지않은코드의예 JAVA 1: 2: public static void main(string[] args) { 3: int size = new Integer(args[0]).intValue(); 4: size += new Integer(args[1]).intValue(); 5: MyClass[] data = new MyClass[size]; 6: 7: 80

동적메모리할당을위해크기를사용하는경우그값이음수가아닌지검사 하는작업이필요하다. 안전한코드의예 JAVA 1: 2: public static void main(string[] args) { 3: int size = new Integer(args[0]).intValue(); 4: size += new Integer(args[1]).intValue(); 5: // 배열의크기값이음수값이아닌지검사한다. 6: if (size < 0) return ; 7: MyClass[ ] data = new MyClass[size]; 8: 라. 진단방법 변수를사용하여배열의크기를동적으로결정하고있는경우변( ➀), 수가외 부입력값인지확인하고 (➁), 해당변수가의도한범위내에존재하는지확인하는 절차가있는지확인한다. 외부입력값에대한검증절차가없다면취약하다.... String cnt = request.getparameter("cnt"); --------------------- ➁ if( cnt == null ){ cnt = "0"; int cnti = Integer.parseInt(cnt); String[] arr = new String[cntI]; ---------------------- ➀ for( int i = 0 ; i <cnti ; i++ ){ arr[i] = request.getparameter("r"+i);... 정수형변수의값이증가하면서정수형한계값보다더커지는경우아주작 은값이되거나음수가될수있다. 이러한상황에대한검사가이루어지지않 고진행하는경우취약하다. W2 : EgovFileTool.java 81

public static Vector parsfilebysize(string parfile, int[] parlen, int parline) throws Exception { // 파싱결과구조체 Vector parresult = new Vector(); // 파일오픈 String parfile1 = parfile.replace('\\', FILE_SEPARATOR).replace('/', FILE_SEPARATOR); File file = new File(parFile1); BufferedReader br = null; try { // 파일이며, 존재하면파싱시작 if (file.exists() && file.isfile()) { // 1. 입력된라인수만큼파일텍스트내용을읽어서 String[] 에쌓는다. br = new BufferedReader(new InputStreamReader(new FileInputStream(file))); String [] strarr = new String [parline]; String line = ""; int readcnt = 0; while ((line = br.readline())!= null && readc nt < parline) { if (line.length() <= MAX_STR_LEN) strarr[readcnt++] = line; 다음의예제에서 pubkey.available() 함수는 int 값을리턴한다. 그러므로정 수값한계를벗어난값은발생하지않는다. try { URL url = Util.class.getClassLoader() pubkey = url.openstream(); catch (MalformedURLException e) {.getresource(publickeyfilepath); pubkey = new FileInputStream(publicKeyFilepath); 82

byte[] bytes = new byte[pubkey.available()]; 다음의예제에서입력값은 초과할가능성이없다. HTTP 헤더의한계값이있으므로정수의한계를 String[] referer_split = Util.split(request.getHeader("Referer").toString(),"/"); String refer ervalue = referer_split[referer_split.length-1]; 다음의예제에서아규먼트의개수가정수의한계이상으로입력된다는것은 실현가능성이희박하므로취약하지않은것으로판단한다. public static void main(string[] args) { String[] jsargs = {"-j="+args[0]; String[] allargs = new String[jsargs.length + args.length]; 사용자입력값등외부값이아닌경우취약하지않다. 마. 참고문헌 [1] CWE-190 Integer Overflow - http://cwe.mitre.org/data/definitions/190.html [2] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Risky Resource Management, RANK 24 CWE-190 Integer Overflow or Wraparound 83

14. 보호메커니즘을우회할수있는입력값변조 가. 개요 소프트웨어가외부입력값에대한신뢰를전제로보호메커니즘을사용하는경 우공격자가입력값을조작할수있다면보호메커니즘을우회할수있게된다. 개발자들이흔히쿠키, 환경변수또는히든필드와같은입력값이조작될수 없다고가정하지만공격자는다양한방법을통해이러한입력값들을변경할수 있고조작된내용은탐지되지않을수있다. 인증이나인가와같은보안결정이 이런입력값( 쿠키, 환경변수, 히든필드등 ) 에기반해수행되는경우공격자는이런입 력값을조작하여소프트웨어의보안을우회할수있으므로충분한암호화무, 결성체크또는다른메커니즘이없는경우외부사용자에의한입력값을신뢰해 서는안된다. 나. 보안대책 상태정보나민감한데이터특히사용자세션정보와같은중요한정보는서버 에저장하고보안확인절차도서버에서실행한다. 보안설계관점에서신뢰할수 없는입력값이어플리케이션내부로들어올수있는지점과보안결정에사용되 는입력값을식별하고제공되는입력값에의존할필요가없는구조로변경할 수있는지검토한다. 다. 코드예제 사용자의을 role 설정할때사용자웹브라우저의쿠키의에 role 할당된값을 사용하고있어이값이사용자에의해변경되는경우사용자값role 이의도하 지않은값으로할당될수있다. 안전하지않은코드의예 JAVA 1: Cookie[] cookies = request.getcookies(); 2: for (int i =0; i< cookies.length; i++) { 84

3: Cookie c = cookies[i]; 4: if (c.getname().equals("role")) { 5: userrole = c.getvalue(); 6: 7: 사용자권한, 인증여부등보안결정에사용하는값은사용자입력값을사용 하지않고내부세션값을활용한다. 안전한코드의예 JAVA 1:... 2: HttpSession session = context.getsession(id) ; 3: String userrole = (Stirng)session.getValue("role") ; 4:... 라. 진단방법 인증여부를확인하기위해사용하는변수를확인하고변( ➀), 수가세션정보 등서버내부에서검증된값인지확인한다( ➁). 인증결정의기준으로외부입력값 을그대로사용하는경우취약하다.... Cookie[] cookies = request.getcookies() ; --------------------- ➁ for(int i=0 ; i<cookies.length ; i++){ Cookie c = cookies[i] ; if(c.getname().equals("authecticated")&&boolean.true.equals(c.getvalue())){ --- ➀ authenticated = true ;... 다음의예제에서는평문으로사용자의인증정보및 authenticated 를쿠키 에저장하고있다. 공격자는쿠키정보를변경가능하기때문에중요한정보를 쿠키에저장시에는암호화해서사용하고, 가급적해당정보는 WAS(Web Application Server) 서버의세션에저장한다. 85

1: <% 2: String username = request.getparameter("username"); 3: String password = request.getparameter("password"); 4: if (username==nill password==null!isauthenticateduser(usename, password)) { 5: throw new MyException(" 인증에러 "); 6: 7: Cookie usercookie = new Cookie("user",username); 8: Cookie authcookie = new Cookie("authenticated","1"); 9: 10: respons e.addcookie(usercookie); 11: respons e.addcookie(authcookie); 12: %> 마. 참고문헌 [1] CWE-807 Reliance on Untrusted Inputs in a Security Decision - http://cwe.mitre.org/data/definitions/807.html 86

제2절보안기능 보안기능( 인증, 접근제어, 기밀성, 암호화, 권한관리등) 을부적절하게구현 시발생할수있는취약점 1. 적절한인증없이중요기능을허용 가. 개요 적절한인증과정이없이중요정보( 계좌이체정보, 개인정보등 ) 를열람또는변경 때발생하는취약점이다. ( ) 할 나. 보안대책 클라이언트의보안검사를우회하여서버에접근하지못하도록설계하고중요 한정보가있는페이지는재인증을적용( 은행계좌이체등 ) 한다. 또한안전하다고 확인된라이브러리나프레임워크(OpenSSL이나 ESAPI 의보안기능등) 를사용 하는것이중요하다. 다. 코드예제 재인증을거치지않고계좌이체를하고있다. 안전하지않은코드의예 JAVA 1: public void sendbankaccount(string accountnumber,double balance) { 2:... 3: BankAccount account = new BankAccount(); 4: account.setaccountnumber(accountnumber); 5: account.settoperson(toperson); 6: account.setbalance(balance); 7: AccountManager.send(account); 8:... 9: 87

인증을수행된사용자만이재인증을거쳐계좌이체가가능하도록한다. 안전한코드의예 JAVA 1: public void sendbankaccount(httpservletrequest request, HttpSession session, 2: String accountnumber,double balance) { 3:... 4: // 재인증을위한팝업화면을통해사용자의 credential 을받는다. 5: String newusername = request.getparameter("username"); 6: String newpassword = request.getparameter("password"); 7: if ( newusername == null newpassword == null ) { 8: throw new MyEception(" 데이터오류:); 9: 10: 11: // 세션으로부터로긴한사용자의 credential 을읽는다. 12: String password = session.getvalue("password"); 13: String username = session.getvalue("username"); 14: 15: // 재인증을통해서이체여부를판단한다. 16: if ( isauthenticateduser() && newusername.equal(username) && 17: newpassword.equal(password) ) { 18: BankAccount account = new BankAccount(); 19: account.setaccountnumber(accountnumber); 20: account.settoperson(toperson); 21: account.setbalance(balance); 22: AccountManager.send(account); 23: 24:... 25: 라. 진단방법 해당취약점은보안특성중개인정보와관련된취약점으로정적도구를사용 하여이를판단하는것은어려움. 이에따라진단원이직접코드를보며해당 정보가중요정보인지파악해야한다. 패스워드, 개인정보주민등록번호여권번호외국인식별번호등 (,, ), 금융정보 ( 카드번호, 계좌 정보등 ) 등을포함하여접근및사용이제한되어야하는중요정보및기능을 정의하였는지확인하고 ( ➀), 접근및사용이제한되어야하는중요정보및기능 의사용여부를확인한다. 중요정보를사용하는경우접근변경시적절한인증 88

여부를확인하여허용하는기능이구현되었는지확인( ➁) 한다만. 약적절한인 증여부를확인하는경우안전하다고판단하고, 인증여부를확인하지않는경우 취약하다고판단한다. public BankAccount createbankaccount(string accountnumber, String accounttype, String accountname, String accountssn, double balance) { BankAccount account = new BankAccount(); --------------------- ➀ account.setaccountnumber(accountnumber); account.setaccounttype(accounttype); account.setaccountownername(accountname); account.setaccountownerssn(accountssn); account.setbalance(balance); return account; public boolean authenticateuser(string username, String password) {... // createbankaccount 를사용 --------------------- ➁... 다음의예제에서는재인증을거치지않고계좌이체를하고있으므로취약하 다고판정한다. 1: public void sendbankaccount(string accountnumber,double balance) { 2:... 3: BankAccount account = new BankAccount(); 4: account.setaccountnumber(accountnumber); 5: account.settoperson(toperson); 6: account.setbalance(balance); 7: AccountManager.send(account); 8:... 9: 89

마. 참고문헌 [1] CWE-306 Missing Authentication for Critical Function - http://cwe.mitre.org/data/definitions/306.html [2] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Porous Defenses, RANK 5 CWE-306 Missing Authentication for Critical Function 90

2. 부적절한인가 가. 개요 프로그램이모든가능한실행경로에대해서접근제어를검사하지않거나불 완전하게검사하는경우, 공격자는접근가능한실행경로를통해정보를유출 할수있다. < 그림 3-11> 부적적한인가 나. 보안대책 응용프로그램이제공하는정보와기능을역할에따라배분함으로써공격자에 게노출되는공격대상 ( 10) attack surface) 을최소화하고사용자의권한에따른 ACL(Access Control List) 을관리한다. 프레임워크를사용해서취약점을피할 수도있는데예를들면, JAAS uthorization Framework나 OWASP ESAPI Access Control 등이인증프레임워크로사용가능하다. 다. 코드예제 사용자인증이성공적으로끝나면, 어떤 LDAP 검색도가능하다. 또한사용 10) OSSTMM 3 Defines Attack Surface as "The lack of specific separations and functional controls that exist for that vector" 91

자의로그인정보와인자로넘겨오는 username 의비교가없기때문에타인의 정보를볼수가있다. 안전하지않은코드의예 C 1: #define FIND_DN "uid=han,ou=staff,dc=example,dc=com" 2: 3: int searchdata2ldap(ldap *ld, char *username) { 4: unsigned long rc; 5: char filter[20]; 6: LDAPMessage *result; 7: snprintf(filter, sizeof(filter), "(name=%s)", username); 8: rc = ldap_search_ext_s(ld, FIND_DN, LDAP_SCOPE_BASE, filter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &result); 9: 10: return rc; 11: LDAP 검색전에 username 을인증하고, username 이로그인정보와일치하는지 점검한다. 안전한코드의예 C 1: if ( ldap_simple_bind_s(ld, username, password)!= LDAP_SUCCESS ) { 2: printf(" 인증에러 ); 3: return(fail); 4: 5: if ( strcmp(username,getloginname())!= 0 ) { 6: printf(" 인증에러 ); 7: return(fail); 8: 9: snprintf(filter, sizeof(filter), "(name=%s)", username); 10: rc = ldap_search_ext_s(ld, FIND_DN, LDAP_SCOPE_BASE, filter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &result); 라. 진단방법 해당취약점은보안특성중외부시스템( 웹서버, DB 서버, LADP 등) 의권한설정과 관련된취약점으로정적도구를사용하여이를판단하는것은쉽지않다. 92

먼저중요정보를저장할수있는외부시스템이존재하는지식별하고해당시스템에접근( 열람) 및변경 ( 생성 삭제 수정등 ) 에대한권한을사전에적절하게정의하였는지확인한다. 해당정보또는기능 ( 접근변경 ) 을호출하는함수에서, 사전에정의한권한소유여부를검사하는지확인( ➀) 한다. public void f(string ssingleid, int iflag, String sserviceprovider, String suid, String spwd) { env.put(context.initial_context_factory, CommonMySingleConst.INITCTX); env.put(context.provider_url, sserviceprovider); // 익명으로인증을 LDAP 사용 env.put(context.security_authentication, "none"); env.put(context.security_principal, suid); env.put(context.security_credentials, spwd); --------------------- ➀ 외부의입력인값name 이필터가아닌동적인쿼LDAP 리문에서사용자명 으로사용되었으며, 사용자인증을위한별도의접근제어방법이사용되지않 고있다. 이는 anonymous binding 을허용하는것으로볼수있다. 따라서임 의사용자의정보를외부에서접근할수있게된다. 1: public void f(string ssingleid, int iflag, String sserviceprovider, String suid, String spwd) { 2: 3: env.put(context.initial_context_factory, CommonMySingleConst.INITCTX); 4: env.put(context.provider_url, sserviceprovider); 5: // 익명으로 LDAP 인증을사용 6: env.put(context.security_authentication, "none"); 7: env.put(context.security_principal, suid); 8: env.put(context.security_credentials, spwd); 9: 마. 참고문헌 93

[1] CWE-285 Improper Authorization - http://cwe.mitre.org/data/definitions/285.html [2] OWASP Top 10 2010 - (OWASP 2010) A8 Failure to Restrict URL Access [3] SANS Top 25 2010 - (SANS 2010) Porus Defense - CWE ID 285 Improper Authorization [4] NIST. "Role Based Access Control and Role Based Security" [5] M. Howard and D. LeBlanc. "Writing Secure Code". Chapter 4, "Authorization" Page 114; Chapter 6, "Determining Appropriate Access Control" Page 171. 2nd Edition. Microsoft. 2002 94

3. 중요한자원에대한잘못된권한설정 가. 개요 SW가중요한보안관련자원에대하여읽기또는수정하기권한을의도하지않게허가할경우, 권한을갖지않은사용자가해당자원을사용하게된다. 나. 보안대책 설정파일, 실행파일, 라이브러리등은 SW 관리자에의해서만읽고쓰기가가 능하도록설정하고설정파일과같이중요한자원을사용하는경우허가, 받지 않은사용자가중요한자원에접근가능한지검사한다. 다. 코드예제 파일생성시가장많은권한을허가하는형태로 umask를사용하고있어, 모든 사용자가읽기/ 쓰기권한을갖게된다. 안전하지않은코드의예 C 1: // 파일권한디: rw-rw-rw-, 렉터리권한 : rwxrwxrwx 2: umask(0); 3: FILE *out = fopen("important_file", "w"); 4: if (out) { 5: fprintf(out, " 민감한정보 6: fclose(out); 7: \n"); 파일에대한설정을가장제한이많도록, 즉사용자를제외하고는읽고쓰기 가가능하지않도록 umask 를설정하는것이필요하다. 안전한코드의예 C 1: umask(077) ; // 파일권한디: rw-------, 렉터리권한 : rwx------ 2: FILE *out = fopen("important_file", "w"); 3: if (out) { 4: fprintf(out, " 민감한정보 \n"); 95

5: fclose(out); 6: 라. 진단방법 해당취약점은보안특성중중요자원에대한접근권한설정과관련된취약 점으로정적도구를사용하여중요자원이무엇인지판단하는것은어려운작업 니다. 이에따라진단원이직접중요자원을식별하고이에대한취약성을판단 하는것이필요하다. SW에서생성하는중요자원( 파일등 ) 이존재하는지식별한다. 사용자업로드파 일, 프로그램이사용하는설정파일등중요자원에대해읽기쓰, 기실행, 등의 권한을사전에정의하였는지확인하고사전에정의한권한대로중요자원에접 근권한을허용하는지확인한다. 설정파일, 문서파일의경우실행권한이설정되 지않았는지확인하여야한다. // 파일권한디: rw-rw-rw-, 렉터리권한 : rwxrwxrwx String cmd = "umask 0"; File file = new File("/home/report/report.txt");... Runtime.getRuntime().exec(cmd); SW가중요한보안관련자원에대하여읽기또는수정하기권한을의도하지않게허가할경우, 권한을갖지않은사용자가해당자원을사용하게된다. 1: // 파일권한 : rw-rw-rw-, 디렉터리권한 : rwxrwxrwx 2: String cmd = "umask 0"; 3: File file = new File("/home/report/report.txt"); 4:... 5: Runtime.getRuntime().exec(cmd); 마. 참고문헌 96

[1] CWE-732 Incorrect Permission Assignment for Critical Resource - http://cwe.mitre.org/data/definitions/732.html [2] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Porous Defenses, RANK 17 CWE-732 Incorrect Permission Assignment for Critical Resource 97

4. 취약한암호화알고리즘허용 가. SW 개요 개발자들은환경설정파일에저장된패스워드를보호하기위하여간단한 인코딩함수를이용하여패스워드를감추는방법을사용하기도한다. 그렇지만 base64 와같은지나치게간단한인코딩함수를사용하는것은패스워드를제대 로보호할수없다. 정보보호측면에서취약하거나위험한암호화알고리즘을사용해서는안된 다. 표준화되지암호화알고리즘을사용하는것은공격자가알고리즘을분석하 여무력화시킬수있는가능성을높일수도있다. 몇몇오래된암호화알고리 즘의경우는컴퓨터의성능이향상됨에따라취약해지기도해서, 예전에는해 독하는데몇십억년이걸리던알고리즘이며칠이나몇시간내에해독되기도 한다알. RC2, RC4, RC5, RC6, MD4, MD5, SHA1, DES 고리즘이여기에해당 된다. < 그림 3-12> 취약한암호화알고리즘허용 < 그림 3-12> 은취약한대칭키암호화알고리즘를 DES 이용하여평문을암호 화하고복호화하는과정을나타내고있다. 나. 보안대책 자신만의암호화알고리즘을개발하는것은위험하며, 학계및업계에서이 미검증된표준화된알고리즘을사용한다. 기존에취약하다고알려진 DES, RC5 등의암호알고리즘을대신하여, 3DES, AES, SEED 등의안전한암호알고리 98

즘으로대체하여사용한다. 참고 : 안전한암호알고리즘및키길이 블록암호 운영모드 메시지 인증코드 난수발생기 분류 최소안전성수준 블록암호 해쉬함수 기밀성 보호함수목록 112비트 ARIA( 키길이 : 128/192/256), SEED( 키길이 : 128) ECD, CBC, CFB, OFB, CTR 기밀성/ 인증 CCM, GCM 해쉬함수기반 블록기반 해쉬함수 /HMAC 공개키암호 블록기반 기반 SHA-224/256/384/512 HMAC CMAC, GMAC HASH_DRBG, HMAC_DRBG CTR_DRBG RSAES - ( 공개키길이 ) 2048, 3072 - RSA-OAEP 에서사용되는해쉬함수 : SHA-224/256 전자서명 키설정방식 보호함수 RSA-PSS, KCDSA, ECDSA, EC-KCDSA DH, ECDH 보호함수파라미터 시스템 파라미터 RSA-PSS KCDSA, DH ECDSA, EC-KCDSA, ECDH ( 공개키길이) 2048, 3072 ( 공개키길이, 개인키길이) (2048, 224), (2048, 256) (FIPS) B-233, B-283 (FIPS) K-233, K-283 (FIPS) P-224, P-256 출처 : 암호알고리즘검증기준 Ver 2.0(2012.3), 암호모듈시험기관 99

다. 코드예제 취약한 DES 알고리즘으로암호화하고있다. 안전하지않은코드의예 JAVA 1. try { 1: Cipher c = Cipher.getInstance("DES"); 2: c.init(cipher.encrypt_mode, k); 3: rslt = c.update(msg); 4: 5: catch (InvalidKeyException e) { 안전하다고알려진 AES 알고리즘등을적용한다. 안전한코드의예 JAVA 1: try { 2: Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 3: c.init(cipher.encrypt_mode, k); 4: rslt = c.update(msg); 5: 6: catch (InvalidKeyException e) { 라. 진단방법 해당취약점은보안특성중암호알고리즘사용과관련된내용으로정적도구 를사용하여암호알고리즘의적절성여부를판단하는것은어렵다. 다만, 일부 언어들에서는암호화함수를제공하기때문에이언어들에대해서는암호화함 수를통하여취약한암호알고리즘사용여부를진단원이판단할수있다. 하지 만자체암호화알고리즘을사용여부는수동진단이필요하다. 자체구현한암호화관련알고리즘을사용하는지확인하고자체구현한암호 알고리즘을사용할경우취약함으로판단한다. 만약, 암호전문가가존재할경우 해당암호함수의암호알고리즘의적절성을판단하여보증하는것은가능하다. 특정언어에서제공하는암호관련함수를호출하는지식별한다. 100

- 취약한알고리즘등: MD4, MD5, RC2, RC4, RC5, DES, 2DES - 안전한알고리즘등: SHA-256, AES, SEED, ARIA try { Ciphe r c = Cipher.getInstance("DES"); c.init(cipher.encrypt_mode, k); rslt = c.update(msg); catch (InvalidKeyException e) { 패스워드를 base64로인코딩하여 configuration 파일에저장한경우이다. Basea64 인코딩기법자체가가지는취약점때문에이경우패스워드를안전하 게보호할수없다. 1: 2: boolean DBConnect() throws SQLException { 3: url = "DBServer"; 4: usr = "Scott"; 5: con = null; 6: 7: { 8: prop = new Properties(); 9: FileInputStream("config.properties")); 10: 11: // 패스워드를 64bit로한다 decoding. 12: password[] = Base64.decode(prop.getProperty("password")); 13: 14: 유효성점검없이패스워드를문자열로읽는다. 15: = DriverManager.getConnection(url, usr, password.tostring()); 16: catch (FileNotFoundException e) { 17: 18: catch (IOException e) { 19: 20: 21: 101

DES 등의낮은보안수준의알고리즘을사용하는경우는안전하지않다. public static EgovFormBasedUUID nameuuidfrombytes(byte[] name) { MessageDigest md; try { md = MessageDigest.getInstance("MD5"); catch (NoSuchAlgorithmException nsae) { throw new InternalError("MD5 not supported"); byte[] md5bytes = md.digest(name); md5bytes[6] &= 0x0f; /* cl ear version */ md5bytes[6] = 0x30; /* set to version 3 */ md5bytes[8] &= 0x3f; /* cl ear variant */ md5bytes[8] = 0x80; /* set to IETF variant */ return new EgovFormBasedUUID(md5Bytes); Base64 encoding 의경우는암호화가아닌인코딩이다. 개발시 base64 encoding 을사용하는경우는암호화목적이아닌가시성과보관성을위해서이 다. 그러므로 base64 encoding을사용하였을때취약한암호화알고리즘이라 하기어렵다. 하지만개발자가암호화를위해을 base64 encoding 사용하였다 면취약하다고판정한다. 마. 참고문헌 [1] CWE-327 Use of a Broken or Riscky Cryptographic Algorithm - http://cwe.mitre.org/data/definitions/327.html [2] OWASP Top 10 2010 - (OWASP 2010) A7 Insecure Cryptographic Storage 102

[3] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Porous Defenses, RANK 19 CWE-327 Use of a Broken or Risky Cryptographic Algorithm [4] Bruce Schneier. "Applied Cryptography". John Wiley &Sons. 1996. 103

5. 중요정보평문저장( 또는전송) 가. 개요 프로그램이보안과관련된민감한데이터를평문으로통신채널을통해서송 수신할경우, 통신채널스니핑을통해인가되지않은사용자에게민감한데이 터가노출될수있는보안취약점이다. 나. 보안대책 < 그림 3-13> 중요정보평문저장( 또는전송 ) 개인정보( 주민등록번호, 여권번호등 ), 금융정보 ( 카드 계좌번호 ), 패스워드등을저장할 때에는반드시암호화하여저장하고중요한정보를통신채널을전송할때에 도암호화한다. 다. 코드예제 속성파일에서읽어들인패스워드(Plain text) 를네트워크를통하여서버에전송하고있다. 이경우패킷스니핑을통하여 password 가노출될수있다. 안전하지않은코드의예 JAVA 1: void foo() { 2: try { 3: Socket socket = new Socket("taranis", 4444); 4: PrintWriter out = new PrintWriter 104

5: (socket.getoutputstream(), true); 6: String password = getpassword(); 7: out.write(password); 8: catch (FileNotFoundException e) { 9:... 10: 패스워드를네트워크를통하여서버에전송하기전에암호화를수행하였다. 안전한코드의예 JAVA 1: void foo() { 2: try { 3: Socket socket = new Socket("taranis", 4444); 4: PrintStream out = new PrintStream 5: (socket.getoutputstream(), true); 6: Cipher c = Cipher.getInstance 7: ("AES/CBC/PKCS5Padding"); 8: String password = getpassword(); 9: encryptedstr= c.update(password.getbytes()); 10: out.write(encryptedstr,0,encryptedstr.length); 11: catch (FileNotFoundException e) { 라. 진단방법 해당취약점은보안특성중평문전송과관련된내용으로정적도구를사용하 여중요정보의기준을판단하는것은불가능하다. 개인정보( 주민등록번호, 여권번호등 ), 금융정보 ( 카드 계좌번호), 패스워드등민감한정 보를다루는지확인하고( ➀), 해당정보가네트워크등을통하여전송될때암 호화되는지확인한다 ( ➁). void foo() { try { Socket socket = new Socket("taranis", 4444); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); String password = getpassword(); out.write(password); --------------------- ➁ --------------------- ➀ catch (FileNotFoundException e) {... 105

다음의예제에서는인증을통과한사용자의패스워드정보가평문으로에 DB 저장되므로취약하다. 1: String username = request.getparameter("username"); 2: String password = request.getparameter("password"); 3: PreparedStatement p=null; 4: try { 5:... 6: if (username==nill password==null 7:!isAuthenticatedUser(usename, password)) { 8: throw new MyException(" 인증에러 "); 9: 10: p = conn.preparestatement("insert INTO employees VALUES(?,?)"); 11: p.setstring(1,username); 12: p.setstring(2,password); 13: p.execute(); 14:... 15: 속성파일에서읽어들인패스워드(Plain text) 를네트워크를통하여서버에 전송하고있다. 이경우패킷스니핑을통하여패스워드가노출될수있어취 약하다. 1: 2: String getpassword() { 3: return "secret"; 4: 5: 6: void foo() { 7: try { 106

8: Socket socket = new Socket("taranis", 4444); 9: PrintWriter out = new PrintWriter(socket.getOutputStream(), true); 10: String password = getpassword(); 11: out.write(password); 12: catch (FileNotFoundException e) { 13: 14: 마. 참고문헌 [1] CWE-311 Cleartext Transmission of Sensitive Information - http://cwe.mitre.org/data/definitions/311.html [2] OWASP Top 10 2007 - (OWASP 2007) A9 Insecure Communications [3] 2011 CWE/SANS Top 25 Most Dangerous Software Errors - Porous Defenses, RANK 8 CWE-311 Missing Encryption of Sensitive Data 107

6. 하드코드된패스워드 가. 개요 프로그램코드내부에하드코드된패스워드를포함하고, 이를이용하여내부 인증에사용하거나외부컴포넌트와통신을하는경우관리자정보가노출될 수있어위험하다. 또한, 코드내부에하드코드된패스워드가인증실패를야기 하는경우, 시스템관리자가그실패의원인을파악하기쉽지않은단점이있다. < 그림 3-14> 하드코드된패스워드 위에그림은데이터베이스연결을위한아이디및패스워드를하드코딩해놓았다. 이를통해관리자정보가노출될수있다. 나. 보안대책 패스워드는암호화하여별도의파일에저장하여사용하고 SW 설치시사용 하는디폴트패스워드, 키등을사용하는대신 " 최초- 로그인" 모드를두어사용자가직접패스워드나키를입력하도록설계한다. 108

다. 코드예제 데이터베이스를연결하기위하여코드내부에상수형태로정의된패스워드 를사용하면프로그램에취약점을야기할수있다. 안전하지않은코드의예 JAVA 1: public Connection DBConnect(String url, String id) { 2: try { 3: conn = DriverManager.getConnection(url, id, "tiger"); 4: catch (SQLException e) { 5: System.err.println("..."); 6: 7: return conn; 8: 9: 데이터베이스를사용하는프로그램작성시패스워드를구하는로직을따로구현하여, 주어진로직에의하여검증된패스워드를사용한다. 안전한코드의예 JAVA 1: try { 2: String url = props.getproperty("url"); 3: String id = props.getproperty("id"); 4: String pwd = props.getproperty("passwd"); 5: 6: byte[] decrypted_pwd = cipher.dofinal(pwd.getbytes()); 7: pwd = new String(decrypted_pwd); 8: conn = DriverManager.getConnection(url, id, pwd); 라. 진단방법 사용자 관리자로그인페이지 ( 식별인증 ) 를요청하는모듈 함수를확인하여사용 자관리자 패스워드가소스코드안에직접코딩되어있는지확인하고내외부 서버( 업무서버, DB 등) 에접속을관리하는모듈 함수를확인하여서버접속패스워 드가소스코드안에직접코딩되어있는지확인한다. 109

패스워드를별도의파일에저장하여사용하지않고소스코드안에하드코 eld 되어있는경우엔취약하다고판정한다. 다음의예제에서데이터베이스접속정보인비밀번호를 47번라인에서입력 하고있다. private static final String JDBC_DRIVER = "org.gjt.mm.mysql.driver" private static final String JDBC_URL = "jdbc:mysql://192.168.200.24:1621/com" private static final String JDBC_USER = "com" 47: private static final String JDBC_PASSWORD = "com01" 마. 참고문헌 [1] CWE-259 Hard-coded Password - http://cwe.mitre.org/data/definitions/259.html [2] OWASP Top 10 2010 - (OWASP 2010) A7 Insecure Cryptographic Storage [3] SANS Top 25 2010 - (SANS 2010) Porus Defense 110

7. 충분하지않은키길이사용을허용하는취약점 가. 개요 길이가짧은키를사용하는것은암호화알고리즘을취약하게만들수있다. 현재 RSA 알고리즘은적어도 1024 비트이상의길이를가진키와함께사용해 야한다고알려져있다. Symmetric 암호화의경우에는적어도 128비트이상의 키를사용하는것이바람직하다. 나. 보안대책 최소한 128 비트이상의키를사용한다. 다. 코드예제 다음의예제는보안성이강한 RSA 알고리즘을사용함에도불구하고, 키사이 즈를작게설정함으로써프로그램의취약점을야기한경우이다. 안전하지않은코드의예 JAVA 1: 2: public void target() throws NoSuchAlgorithmException { 3: KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA"); 4: // Key generator 의불충분한키크기 5: keygen.initialize(512); 6: KeyPair mykeys = keygen.generatekeypair(); 7: 공개키암호화에사용하는키의길이는적어도 1,024 비트이상으로설정한다. 안전한코드의예 JAVA 1: 2: public void target() throws NoSuchAlgorithmException { 3: KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA"); 4: // Key generator 의값은최소 1024bit 로설정한다. 5: keygen.initialize(1024); 111

6: KeyPair mykeys = keygen.generatekeypair(); 7: 라. 진단방법 대칭키암호알고리즘( 예,AES, ARID, SEED 등) 의경우 128bit 이상, 해쉬함 수예 (, SHA 등의 ) 경우 128bit 이상, 공개키암호알고리즘( 예, RSA, ECC 등 ) 2,048bit 이상(ECC 의경우, 256 이상) 의키를사용하는지확인한다. 다음의예제는보안성이강한알RSA 고리즘을사용함에도불구하고키, 사 이즈를작게설정함으로써프로그램의취약점을야기한경우이다. 1: 2: public void target() throws NoSuchAlgorithmException { 3: KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA"); 4: // Key generator 의불충분한키크기 5: keygen.initialize(512); 6: KeyPair mykeys = keygen.generatekeypair(); 7: 마. 참고문헌 [1] CWE-310 Cryptographic Issues - http://cwe.mitre.org/data/definitions/310.html [2] OWASP Top 10 2010 - (OWASP 2010) A7 Insecure Cryptographic Storage 112

8. 적절하지않은난수값사용 가. 개요 예측가능한난수를사용하는것은시스템에취약점을야기시킨다. 예측불 가능한숫자가필요한상황에서예측가능한난수를사용한다면, 공격자는 SW 에서생성되는다음숫자를예상하여시스템을공격하는것이가능하다. 나. 보안대책 난수발생기에서를 seed 사용하는경우에는예측하기어려운방법으로변경한 다. 일반적으로 Java에서는 java.lang.math.random() 메소드사용을자제하고, java.util.random 클래스를사용, C에서는대신 rand() 를 randomize(seed) 사용하 하는것이좋다. 세션ID, 암호화키등보안결정을위한값을생성하고보안결정 을수행하는경우에는 java.security.securerandom 클래스를사용한다. 다. 코드예제 java.lang.math 클래스의메소드는 random() 를 seed 재설정할수없기때문에 위험하다. 안전하지않은코드의예 JAVA 1: 2: public double roledice() { 3: return Math.random(); 4: 5: java.util.random 클래스는를 seed 재설정하지않아도매번다른난수를생성한 다. 따라서 Random 클래스를사용하는것이보다안전하다. 안전한코드의예 JAVA 1: import java.util.random; 113

2: import java.util.date; 3: 4: public int roledice() { 5: Random r = new Random(); 6: // setseed() 메소드를사용해서 r을예측불가능한 long 타입으로설정한다. 7: r.setseed(new Date().getTime()); 8: // 난수생성 9: return (r.nextint()%6) + 1; 10: 11: 라. 진단방법 Math.random() 메소드를사용하는지확인한다.( ➀), Math.random() 을사용하 는경우취약한다. 난수값을세션ID로설정하여보안결정에사용하는지확인한 다 (➁). 보안결정인경우 java.security.securerandom 를사용하면안전하다.... function setsessionid() { ---------------------------- ➁ if (!Get_Cookie( SessionID )) Set_Cookie( SessionID,Math.random()); --------------------- ➀ Math.random() 함수를사용하여난수값을발생시켰을경우취약하다. public static int[] insertrandom(int[] Cnt, int i,int scope) { int ran = (int) (Math.random() * scope) - 1; if (checkdigit(ran,cnt)) { Cnt[i] = ran; else { insertrandom(cnt,i,scope); return Cnt; 114

자체랜덤키생성으로취약점보완하는경우즉, 다음의예제와같이 Date 요소들로취약점을보완하는경우는안전하다고판정한다. long rand = ((r.nextlong()>>>1)%( enddate.gettimeinmillis()-begindate.gettimeinmillis() + 1)) + begindate.gettimeinmillis(); Java.util.Random, java.security.securityrandom 사용시에는복잡도가상대적으로높으므로취약하지않다. import java.security.securerandom;... SecureRandom r = new SecureRandom(); long rand = ((r.nextlong()>>>1)%( enddate.gettimeinmillis()-begindate.gettimeinmillis() + 1)) + begindate.gettimeinmillis(); 마. 참고문헌 [1] CWE-330 Use of Insufficiently Random Values - http://cwe.mitre.org/data/definitions/330.html [2] SANS Top 25 2009 - (SANS 2009) Porus Defense - CWE ID 330 Use of Insufficiently Random Values [3] J. Viega and G. McGraw. "Building Secure Software: How to Avoid Security Problems the Right Way". 2002. 115

9. 패스워드평문저장 가. 개요 패스워드를암호화되지않은텍스트의형태로저장하는것은시스템손상의 원인이될수있다환. 경설정파일에평문으로패스워드를저장하면, 환경설정 파일에접근할수있는사람은누구나패스워드를알아낼수있다. 패스워드는 높은수준의암호화알고리즘을사용하여관리되어져야한다. 패스워드를설정파일에저장하는것은경우패스워드를암호화되지않은상 태로저장하게되면, 암호가외부에직접적으로드러날위험성이있다. 따라서, 패스워드는쉽게접근할수없는저장소에저장하든지아니면암호화한상태로 저장하여야한다. 나. 보안대책 패스워드를외부환경파일에저장한다면, 암호화하여저장한다. 다. 코드예제 다음의예제는속성파일에서읽어들인패스워드를 String 형태그대로데이 터베이스를연결하는데사용하고있다. 사용자가속성파일에공격을위한문 자열을저장한경우, 프로그램이의도한공격에노출될수있다. 안전하지않은코드의예 JAVA 1: package testbed.unsafe; 2: import java.sql.*; 3: import java.util.properties; 4: import java.io.*; 5: public class U256 { 6: public void f(string url, String name) throws IOException { 7: Connection con = null; 8: try { 9: Properties props = new Properties(); 10: FileInputStream in = new FileInputStream("External.properties"); 116

11: byte[] pass = new byte[8]; 12: // 외부파일로부터 password 를읽는다. 13: in.read(pass); 14: // password 가 DB connection 의인자변수로그대로사용이된다. 15: con = DriverManager.getConnection(url, name, new String(pass)); 16: con.close(); 17: catch (SQLException e) { 18: System.err.println("SQLException Occured "); 19: finally { 20: try { 21: if (con!= null) 22: con.close(); 23: catch (SQLException e) { 24: System.err.println("SQLException Occured "); 25: 26: 27: 28: 외부에서입력된패스워드는사용전에복호화후사용되어야한다. 안전한코드의예 JAVA 1: package testbed.safe; 2: import java.sql.*; 3: import java.util.properties; 4: import java.io.*; 5: public class S256 { 6: public void f(string[] args) throws IOException { 7: Connection con = null; 8: try { 9: Properties props = new Properties(); 10: FileInputStream in = new FileInputStream("External.properties"); 11: props.load(in); 12: String url = props.getproperty("url"); 13: String name = props.getproperty("name"); 14: // 외부파일로부터를 password 읽은후, 복호화한다. 15: String pass = decrypt(props.getproperty("password")); 16: // 외부파일로부터의패스워드를복호화후사용함. 17: con = DriverManager.getConnection(url, name, pass); 18: catch (SQLException e) { 19: System.err.println("SQLException Occured "); 117

20: finally { 21: try { 22: if (con!= null) 23: con.close(); 24: catch (SQLException e) { 25: System.err.println("SQLException Occured "); 26: 27: 28: 29: 라. 진단방법 DB 접속등패스워드사용이필요한로직을확인하고( ➀), 사용된패스워드의 복호화여부확인한다( ➁). 복호화절차가없다면패스워드가평문으로저장되 어있는것이므로취약하다.... properties prop = new Properties() ; prop.load(new FileInputStream("conifig.properties")) ; String password = prop.getprope rty("password"); ----------- ➁ DriverManager.getConnection(url, u sr, password) ; ----------- ➀... 다음의예제는속성파일에서읽어들인패스워드를 String 형태그대로데 이터베이스를연결하는데사용하고있다. 사용자가속성파일에공격을위한 문자열을저장한경우, 프로그램이의도한공격에노출될수있다. 1: package testbed.unsafe; 2: import java.sql.*; 3: import java.util.properties; 4: import java.io.*; 5: public class U256 { 6: public void f(string url, String name) throws IOException { 7: Connection con = null; 8: try { 118

9: Properties props = new Properties(); 10: FileInputStream i n = new FileInputStream("External.properties"); 11: byte[] pass = new byte[8]; 12: // 외부파일로부터 password 를읽는다. 13: in.r ead(pass); 14: // password가db connection 의인자변수로그대로사용이된다. 15: con = DriverManager.getCo nnection(url, name, new String(pass)); 16: con.close(); 17: catch (SQLException e) { 18: System.err.printl n("sqlexceptio n Occured "); 19: finally { 20: try { 21: if (con!= null) 22: con.close(); 23: catch (SQLException e) { 24: System.err.printl n("sqlexceptio n Occured "); 25: 26: 27: 28: configuration 파일( 설정파일) 에저장된패스워드를읽어서그대로데이터베 이스연결에사용하고있다. 이것은다른사람이패스워드에쉽게접근할수 있도록하므로프로그램취약점을유발할수있다. 1: package testbed.unsafe; 2: import java.io.fileinputstream; 3: import java.io.filenotfoundexception; 4: import java.io.ioexception; 5: import java.sql.connection; 119

6: import java.sql.drivermanager; 7: import java.sql.sqlexception; 8: public class U260 { 9: public boolean connecttest(string url, String usr) { 10: Connection con = null; 11: byte[] b = new byte[1024]; 12: boolean result = false; 13: try { 14: FileInputStream fs = new FileInputStream("sample.cfg"); 15: // 외부데이터를배열로읽어온다. 16: fs.read(b); 17: // 패스워드문자열을만든다 18: String password = new String(b); 19: // 패스워드가 DB 연결정보로사용이된다. 20: con = DriverManager.getCo nnection(url, usr, password); 21: catch (FileNotFoundException e) { 22: System.err.printl n("file Not Fo und Exception Occurred!"); 23: catch (IOException e) { 24: System.err.printl n("i/o Exception Occurred!"); 25: catch (SQLException e) { 26: System.err.printl n("sql Exceptio n Occurred!"); 27: finally { 28: try { 29: if (con!= null) { 30: con.close(); 31: result = true; 32: 33: catch (SQLException e) { 34: System.err.printl n("sql Exceptio n Occurred!"); 35: 120

36: 37: return result; 38: 39: 마. 참고문헌 [1] CWE-256 Plantext Storage of a Password - http://cwe.mitre.org/data/definitions/256.html [2] J. Viega and G. McGraw. "Building Secure Software: How to Avoid Security Problems the Right Way". 2002. 121

10. 하드코드된암호화키 가. 개요 코드내부에하드코드된암호화키를사용하여암호화를수행하면암호화된 정보가유출될가능성이높아진다. 많은 SW 개발자들이코드내부의고정된 패스워드의해쉬를계산하여저장하는것이패스워드를악의적인공격자로부터 보호할수있다고믿고있다. 그러나많은해쉬함수들이역계산이가능하며, 적어도 brute-force 공격에는취약하다는것을고려해야만한다. 나. 보안대책 암호화알고리즘에서상수가아닌키를사용해서암호화를수행하도록설계하 여야하며암호화되, 었더라도패스워드를상수의형태로프로그램소스코드내부 에저장하여사용하면안된다. 다. 코드예제 암호화된패스워드를상수의형태로코드내부에서사용하는것은프로그램 소스가노출되는경우패스워드도동시에노출되는취약점을가지게된다. 안전하지않은코드의예 JAVA 1: 2: private Connection con; 3: 4: public String encryptstring (String usr) { 5: Stringc seed = "68af404b513073584c4b6f22b6c63e6b"; 6: 7: try { 8: // 상수로정의된암호화키를이용하여 encrypt 를수행한다. 9: SecretKeySpec skeyspec = new SecretKeySpec(seed.getBytes(), "AES"); 10: 11: // 해당암호화키기반의암호화또는복호화업무수행 12:.. 13: catch (SQLException e) { 122

14: 15: 16: return con; 17: 18: 암호화된패스워드를복호화하기위하여사용되는암호화키도소스코드내 부에상수형태로정의해서사용하면안된다. 안전한코드의예 JAVA 1: 2: private Connection con; 3: 4: public String encryptstring (String usr) { 5: Stringc seed = null; 6: 7: try { 8: // 암호화키를외부환경에서읽음. 9: seed = getpassword("./password.ini"); 10: // 암호화된암호화키를복호화함. 11: seed = decrypt(seed); 12: // 상수로정의된암호화키를이용하여 encrypt 를수행한다. 13: // use key coss2 14: SecretKeySpec skeyspec = new SecretKeySpec(seed.getBytes(), 15: "AES"); 16: // 해당암호화키기반의암호화또는복호화업무수행 17:.. 18: catch (SQLException e) { 19: 20: 21: return con; 22: 23: 라. 진단방법 DB 접속등패스워드사용이필요한로직을확인하고( ➀), 사용된패스워드의 암호화여부확인한다( ➁). 암호화된패스워드가소스내에존재하는지여부확 123

인한다( ➂). 소스코드내에암호화된패스워드를저장하는경우취약하다....... public Connection DBConnect(String url, String usr) { String password = "68af404b513073584c4b6f22b6c63e6b"; try { // 패스워드로상수를사용하고있다. ----------- ➁ con = DriverManager.getConnection(url, usr, password); ----------- ➀ catch (SQLException e) { System.err.println("..."); return con; 암호화를하는데사용되는암호화키를소스에하드코딩해서사용하는경우 취약하다. import java.security.invalidkeyexception; import java.security.nosuchalgorithmexception; import java.sql.sqlexception; import javax.crypto.badpaddingexception; import javax.crypto.illegalblocksizeexception; import javax.crypto.nosuchpaddingexception; public class U321 { String getpassword(){ return "secret"; void foo(string url, String usr){ try { String encryptedpwd = getpassword(); // private key를상수로정의 byte[] privatekey = {'6','8','a','f','4','0','4','b','5','1','3','0','7' javax.crypto.cipher cipher = javax.crypto.cipher.getinstance("rsa"); 124

// 상수를 key 사용하여보안키생성 javax.crypto.secretkey mydeskey = new javax.crypto.spec.secretkeyspec(privatekey, "DES"); cipher.init(javax.crypto.cipher.decrypt_mode,mydeskey); // 암호화된를 password 복호화 byte[] plaintextpwdbytes = cipher.dofinal(encryptedpwd.getbytes()); // DB 연결 java.sql.drivermanager.getconnection(url, usr, new String(plainTextPwdBytes)); catch (InvalidKeyException e) { e.printstacktrace(); catch (NoSuchAlgorithmException e) { e.printstacktrace(); catch (NoSuchPaddingException e) { e.printstacktrace(); catch (IllegalBlockSizeException e) { e.printstacktrace(); catch (BadPaddingException e) { e.printstacktrace(); catch (SQLException e) { e.printstacktrace(); 다. 암호화를위한암호화키가아닌알고리즘명을지정하는경우취약하지않 // 키를설정한다. skey = new SecretKeySpec( key, "DESede" ); 125

마. 참고문헌 [1] CWE-321 Use of Hard-coded Cryptographic Key - http://cwe.mitre.org/data/definitions/321.html 126

11. 취약한패스워드허용 가. 개요 사용자에게강한패스워드조합규칙을요구하지않으면사용자계정이취약 하게된다. 안전한패스워드를생성하기위해서는숫자/ 문자/ 특수문자를조합 하여 7 자리이상으로해야한다. < 그림 3-15> 취약한패스워드허용 나. 보안대책 패스워드생성시강한조건검증을수행한다. 패스워드는문자숫/ 자/ 특수문 자세가지조합을사용하는경우 8 자리이상, 문자/ 숫자/ 특수문자중두가지 조합을사용하는경우 10 자리이상으로생성한다. 다. 코드예제 가입자가입력한패스워드에대한복잡도검증없이가입승인처리를수행 하고있다. 안전하지않은코드의예 JAVA 1: try { 2: String id = request.getparameter("id"); 3: String passwd = request.getparameter("passwd"); 4: catch (SQLException e) 5: { 사용자계정을보호하기위해가입시패스워드복잡도검증후가입승인처 리를수행한다. 127

안전한코드의예 JAVA 1: try { 2: String id = request.getparameter("id"); 3: String passwd = request.getparameter("passwd"); 4: if (passwd == null "".equals(passwd)) 5: return; 6: if (!passwd.matches("") &&(passwd.indexof("@!#")>0) &&(passwd.length() > 7)) 7: catch (SQLException e) 8: { 라. 진단방법 패스워드생성또는변경때입력값을다음과같은규칙으로검사하는모듈 이존재하는지확인한다. ( 참고) 안전한패스워드조합규칙 - 영문자( 대 소구별), 숫자, 특수문자조합한경우, 패스워드길이는자리 8 이상 - 영문자( 대 소구별), 숫자, 특수문자중가지 2 문자열로조합한경우, 패 스워드길이는 10자리이상 - 특정패턴및사용자를 ID 사용금지 다음의예제와같이사용자계정등록등비밀번호를필요로하는정보입력 에서널체크, 비밀번호의자릿수, 특수문자포함등비밀번호요구조건이없거 나약할경우취약하다. 128

<% String id = request.getparameter("id"); String pass = r equest.getparameter("pass"); UserVo us ervo = new UserVo(id,pass);... // 사용자를등록한다. String result = registdao.regist(uservo);... 다음의예제와같이가입자가입력한패스워드에대한복잡도검증없이가 입승인처리를수행하게되면사용자계정을보호하기힘들다. 1: 2: public void dopost(httpservletrequest request, HttpServletResponse response) 3: throws IOException, ServletException { 4: try { 5: String id = request.getparameter("id"); 6: String passwd = request.getparameter("passwd"); 7: // 패스워드복잡도검증없이가입승인처리 8:... 9: catch (SQLException e) { 10: 다음의예제는로그인시에비밀번호를확인하는것은비밀번호를저장하는 것이아니므로취약하지않다. 129

<% // 로그인시입력한값받음 String id = request.getparameter("id"); String pass = r equest.getparameter("pass"); 다음의예제와같이패스워드저장을위한목적이아닌확인의목적일경우취약하지않다고판정한다. fis = new FileInputStream(PROPERTIES_FILE); props.load(new java.io.bufferedinputstream(fis)); username = (String)props.getProperty("id"); password = (String)props.getProperty("password"); 마. 참고문헌 [1] CWE-521 Weak Password Requirements - http://cwe.mitre.org/data/definitions/521.html [2] OWASP Top 10 2010 A3 Broken Authentication Session Management http://www.owasp.org/index.php/category:owasp_top_ten_project [3] Web Application Security Consortium 24 + 2 - (WASC 24 + 2) Information Leakage 130

12. 사용자하드디스크에저장되는쿠키를통한정보노출 가. 개요 대부분의웹응용프로그램에서쿠키는메모리에상주하며브, 라우저의실행 이종료되면사라진다. 프로그래머가원하는경우, 브라우저세션에관계없이 지속적으로저장되도록설정할수있으며, 이것은디스크에기록되고다음브 라우저세션이시작되었을때메모리에로드된다. 개인정보, 인증정보등이영 속적인쿠키 (persistent Cookie) 에저장된다면, 공격자는쿠키에접근할수있는보 다많은기회를가지게되며, 이는시스템을취약하게만든다. 나. 보안대책 쿠키의만료시간은세션이지속되는시간과관련하여최소한으로설정하고 영속적인쿠키에는사용자권한등급, 세션ID 가포함되지않도록한다. 다. 코드예제 javax.servlet.http.cookie.setmaxage 메소드호출에외부의입력이쿠키의유 효시한설정에그대로사용되어프로그램의취약점을야기하는경우이다. 안전하지않은코드의예 JAVA 1: 2: public void makecookie(servletrequest request) { 3: String maxage = request.getparameter("maxage"); 4: if (maxage.matches("[0-9]+")) { 5: String sessionid = request.getparameter("sesionid"); 6: if (sessionid.matches("[a-z=0-9a-z]+")) { 7: Cookie c = new Cookie("sessionID", sessionid); 8: // 외부입력이쿠키유효시한설정에그대로사용되었다. 9: c.setmaxage(integer.parseint(maxage)); 10: 11: 12: 131

사용자가요청한값으로쿠키의유효시한을설정하기전에사용자요청을검증하는로직을별도로작성하여, 메소드호출전에호출한다. 안전한코드의예 JAVA 1: 2: public void makecookie(servletrequest request) { 3: String maxage = request.getparameter("maxage"); 4: 5: if (maxage == null "".equals(maxage)) return; 6: if (maxage.matches("[0-9]+")) { 7: String sessionid = request.getparameter("sesionid"); 8: if (sessionid == null "".equals(sessionid)) return; 9: if (sessionid.matches("[a-z=0-9a-z]+")) { 10: Cookie c = new Cookie("sessionID", sessionid); 11: // 쿠키유효시한의최대값을설정해서, 그아래값으로조정한다. 12: int t = Integer.parseInt(maxAge); 13: if (t > 3600) { 14: t = 3600; 15: 16: c.setmaxage(t); 17: 18: 19: 라. 진단방법 사용자브라우저로쿠키를전송하는지확인하고( ➀), 쿠키유효기간확인한다 ( ➁). 쿠키설정값에 id 정보등중요정보포함여부를확인한다( ➂).... Cookie ck1 = new Cookie("id", "test123") ; ---------------------- ➂ ck.setmaxage(60*60*24*365*10) ; --------------------------- ➁ response.addcookie(ck) ; -------------------------------------- ➀... 다음의예제는 javax.servlet.http.cookie.setmaxage 메소드호출에외부의입 력이쿠키의유효시한설정에그대로사용되어프로그램의취약점을야기하는 경우이다. 132

1: 2: public void makecookie(servletrequest request) { 3: String maxage = request.getparameter("maxage"); 4: if (maxage.matches("[0-9]+")) { 5: String sessio nid = r equest.getparameter("sesio nid"); 6: if (sessionid.matches("[a-z=0-9a-z]+")) { 7: Cookie c = new Cookie("sessionID", sessionid); 8: // 외부입력이쿠키유효시한설정에그대로사용되었다. 9: c.setmaxage(integer.parseint(maxage)); 10: 11: 12: 마. 참고문헌 [1] CWE-539 Information Exposure Through Persistent Cookies - http://cwe.mitre.org/data/definitions/539.html [2] OWASP Top 10 2010 - (OWASP 2010) A7 Insecure Cryptographic Storage [3] Web Application Security Consortium 24 + 2 - (WASC 24 + 2) Information Leakage 133

13. 보안속성미적용으로쿠키가노출될수있는취약점 가. 개요 HTTPS 로만서비스하는경우모든정보가암호화되어안전하게전송된다고 생각한다. 그러나보안에민감한데이터를브라우저쿠키에저장할때보안속 성을세팅하지않으면공격자에게단순한텍스트의형태로노출될수있다. 나. 보안대책 HTTPS 로만서비스하는경우브라우저쿠키에데이터를저장할때반드시 Cookie 객체의 setsecure(true) 메소드를호출하여보안속성을설정한다. 한사이 트도메인에서 ( ) HTTP나 HTTP와 HTTPS를함께사용하는경우 setsecure 메소 드를호출하면해당쿠키가 HTTP를통해서는서버에전송되지않아장애가 발생할수있으므로주의해야한다. 다. 코드예제 HTTPS 로만서비스하는경우민감한정보를가진쿠키를전송하는과정에서, 보안속성을설정하지않으면공격자에게정보가노출될수있다. 안전하지않은코드의예 JAVA 1: 2: private final String ACCOUNT_ID = "account"; 3: 4: public void setupcookies(servletrequest r, HttpServletResponse response) { 5: String acctid = r.getparameter("accountid"); 6: // 보안속성설정되지않은쿠키 7: Cookie c = new Cookie(ACCOUNT_ID, acctid); 8: response.addcookie(c); 9: HTTPS 로만서비스하는경우민감한정보를가진쿠키를사용할경우에는 반드시 Cookie 객체의 setsecure(true) 를호출하여야한다. 134

안전한코드의예 JAVA 1: 2: private final String ACCOUNT_ID = "account"; 3: 4: public void setupcookies(servletrequest r, HttpServletResponse response) { 5: String acctid = r.getparameter("accountid"); 6: // 계정유효성점검 7: if (acctid == null "".equals(acctid)) return; 8: String filtered_id = acctid.replaceall("\r", ""); 9: 10: Cookie c = new Cookie(ACCOUNT_ID, filtered_id); 11: // 민감한정보를가진쿠키를전송할때에는보안속성을설정하여야한다. 12: c.setsecure(true); 13: response.addcookie(c); 14: 라. 진단방법 HTTPS 프로토콜을 통해쿠키에 ID 등의민감한정보를담아전송하는경 우보안속성을설정(setSecure(true) 메서드호출) 하였는지확인한다. 쿠키값을전송할때는보안속성을켜고 [setsecure(true)] 전송하여야안전 하다. 하지만서비스전체를로 https 전송하지않는경우해당보안속성을켜 면서비스가작동하지않으므로취약점에서제외한다. Cookie cookie = new Cookie("emailCookie", email); cookie.setsecure(true); response.addcookie(cookie); 마. 참고문헌 [1] CWE-614 Sensitive Cookie in HTTPS Session Without 'Secure' Attribute - http://cwe.mitre.org/data/definitions/614.html [2] OWASP Top 10 2010 - (OWASP 2010) A9 Insufficient Transport Layer Protection 135

14. 주석문안에포함된패스워드 가. 개요 패스워드를주석문에넣어두면시스템보안이훼손될수있다. SW 개발자가 편의를위해서주석문에패스워드를적어둔경우, SW가완성된후에는그것을제거하는것이매우어렵게된다. 또한공격자가소스코드에접근할수있다면, 아주쉽게시스템에침입할수있다. 나. 보안대책 주석에는 ID, 패스워드등보안과관련된내용을기입하지않는다. 다. 코드예제 다음의예제는디버깅등의목적으로사용자이름과패스워드를주석문안 에서술하고제대로지우지않아서취약점이발생한경우이다. 안전하지않은코드의예 JAVA 1: 2: // Password for administrator is "tiger."<- 주석에패스워드가적혀있다. 3: public boolean DBConnect() { 4: String url = "DBServer"; 5: String password = "tiger"; 6: Connection con = null; 7: 8: try { 9: con = DriverManager.getConnection(url, "scott", password); 10: catch (SQLException e) { 11: 12: 프로그램개발시에주석문등에남겨놓은사용자계정이나패스워드등의정 보는개발완료시에확실하게삭제하여야한다. 136

안전한코드의예 JAVA 1: 2: // 디버깅등의용도로소스주석에적어놓은패스워드는삭제해야한다. 3: public Connection DBConnect(String id, String password) { 4: String url = "DBServer"; 5: Connection conn = null; 6: try { 7: String CONNECT_STRING = url + ":" + id + ":" + password; 8: InitialContext ctx = new InitialContext(); 9: DataSource datasource = (DataSource) ctx.lookup(connect_string); 10: conn = datasource.getconnection(); 11: catch (SQLException e) { 12: return conn; 13: 라. 진단방법 DB 접속, 관리자로그인등이구현된코드를확인하고( ➀), 주석을확인하여 암호포함여부확인한다 ( ➁).... /* * Password for administrator is "tiger." ----------------------------------- ➁ */ public Connection DBConnect(String id, String password) { String url = "DBServer"; Connection conn = null; try { String CONNECT_STRING = url + ":" + id + ":" + password; InitialContext ctx = new InitialContext(); DataSource datasource = (DataSource) ctx.lookup(connect_string); ------ ➀ conn = datasource.getconnection(); catch (SQLException e) { System.err.printf("...");... 다음의예제에서는주석문안에개발자의이해를돕기위한목적등으로패 스워드를적어놓고있으므로취약하다고판정한다. 137

J1 : DaoTest.java public void daotest() throws Exception { // db sampl e : 84d5d0a08a3ec5e2d91a // 암호화전, 후 : 1365ADMIN_01, aa84c40031d808196537ad3dcf81f9af String pwd= "46c165a343fd6841273ae04655af24dd"; String pwd1 = ARIAEngi ne.decaria(pwd); System.out.println(pwd1); 주석문안에 password, passwd, 비밀번호 등의텍스트가존재하지만실 제로그내용이비밀번호가아닌경우에는취약하지않다고판정한다. // 암호화 String Sid = getsessionvalue(session, "ihidnum"); Sid = AEScryptWithSaltKey.encode(StrTool.sNN(Sid)); String sihidnum = Sid; 소스코드내에비밀번호를설정하지않고환경설정 XML에 DB 접속정보 등비밀번호를저장할경우취약하지않다. <!-- hsql <bean id="datasource" class="org.apache.commons.dbcp.basicdatasource" destroy-method="close"> <property name="driverclassname" value="net.sf.log4jdbc.driverspy"/> <property name="url" value="jdbc:log4jdbc:hsqldb:hsql://localhost/bcvpldb"/> <property name="username" value="sa"/> <property name="password" value=""/> <property name="defaultautocommit" value="false"/> <property name="poolpreparedstatements" value="true"/> </bean> 138

마. 참고문헌 [1] CWE-615 Information Exposure Through Comments - http://cwe.mitre.org/data/definitions/615.html [2] OWASP Top 10 2010 - (OWASP 2010) A7 Insecure Cryptographic Storage [3] Web Application Security Consortium 24 + 2 - (WASC 24 + 2) Information Leakage 139

제3절시간및상태 동시또는거의동시수행을지원하는병렬시스템, 하나이상의프로세스가 동작되는환경에서시간및상태를부적절하게관리하여발생할수있는취약점 1. 경쟁조건 : 검사시점과사용시점(TOCTOU) 가. 개요 병렬시스템( 멀티프로세스로구현한응용프로그램) 에서는자원 ( 파일, 소켓등 ) 을사용하기에 앞서자원의상태를검사한다. 하지만자원을사용하는시점과검사하는시점이다르기때문에검, 사하는 시점 (time of check) 에존재하던자원이사용하던시점 (time of use) 에사라지는 등자원의상태가변하는경우가발생한다 < 그림경3-16> 쟁조건검: 사시점과사용시점(TOCTOU) < 그림과 3-16> 같이, 프로세스와 A 가 B 존재하는멀티병렬시스템환경에서 프로세스는 A 사용 ( 파일읽기 ) 에앞서해당파일이존재하는지검사하는과정 (TOC) 을거친다. 이때는프로세스 B에서해당파일을아직사용 ( 삭제) 하지않은 상태이기때문에, 해당자원이문제없음을프로세스는 A 확인하게된다. 하지만 실제사용(TOU) 시점에프로세스는 A 존재하지않는자원을사용하려고하기 140

때문에오류등이발생할수있다. 이와같이하나의자원에대하여동시에검사시점과사용시점이달라생기는 취약점으로인해동기화오류뿐아니라교착상태등과같은문제점이발생한다 나. 보안대책 공유자원( 예: 파일) 을여러프로세스가접근하여사용할경우, 동기화구문을 사용하여한번에하나의프로세스만접근가능하도록 (synchronized, mutex 등) 하고 성능에미치는영향을최소화하기위해임계코드주변만동기화구문을사용한 다. 다. 코드예제 다음의예제는파일의존재를확인하는부분과실제로파일을사용하는부분 을실행하는과정에서시간차가발생하는경우, 파일에대한삭제가발생하 여프로그램이예상하지못하는형태로수행될수있다. 또한시간차를이용하 여파일을변경하는등의공격에취약할수있다. 안전하지않은코드의예 JAVA 1: import java.io.*; 2: 3: class FileAccessThread extends Thread { 4: public void run() { 5: try { 6: File f = new File("Test_367.txt"); 7: if(f.exists()) { // 만약파일이존재하면파일내용을읽음 8: BufferedReader br = new BufferedReader(new FileReader(f)); 9: br.close(); 10: 11: catch(ioexception e) { System.err.println("IOException occured"); 12: 13: 14: class FileDeleteThread extends Thread { 15: public void run() { 141

16: File f = new File("Test_367.txt"); 17: 18: if(f.exists()) { // 만약파일이존재하면파일을삭제함 19: f.delete(); 20: 21: 22: 23: 24: public class U367 { 25: public static void main(string[] args) { 26: // 파일의읽기와파일을삭제하는것을동시에수행한다. 27: FileAccessThread fileaccessthread = new FileAccessThread(); 28: FileDeleteThread filedeletethread = new FileDeleteThread(); 29: fileaccessthread.start(); 30: filedeletethread.start(); 31: 32: 따라서안전한코드예제와같이동기화구문인 synchronized 를사용하여공유자원 ("Test_367.txt") 이동시에사용되지않도록해야한다. 안전한코드의예 JAVA 1: import java.io.*; 2: 3: class FileAccessThread extends Thread { 4: // synchronized 를사용함으로써블록을수행하는동안지정된객체에 lock이 5: // 걸려서다른 Thread 객체 f 에접근할수없다. 6: public synchronized void run() { 7: try { 8: File f = new File("Test_367.txt"); 9: if(f.exists()) { 10: BufferedReader br = new BufferedReader(new FileReader(f)); 11: br.close(); 12: 13: catch(ioexception e) { System.err.println("IOException occured"); 14: 15: 16: class FileDeleteThread extends Thread { 17: public synchronized void run() { 142

18: File f = new File("Test_367.txt"); 19: 20: if(f.exists()) { 21: f.delete(); 22: 23: 24: 25: 26: public class S367 { 27: public static void main(string[] args) { 28: FileAccessThread fileaccessthread = new FileAccessThread(); 29: FileDeleteThread filedeletethread = new FileDeleteThread(); 30: fileaccessthread.start(); 31: filedeletethread.start(); 32: 33: 라. 진단방법 소스코드상에다음과같이공유자원( 파일/ 폴더, 소켓, 드라이버등) 을여러 프로세스가사용하는지확인한다( ➀). 이때, 하나의공유자원을동시에접근할 가능성이존재한다면취약하다고판단하며, 동기화구문등을사용할경우안 전하다고판단한다. 그외공유자원억세스를관리하는 pool 형태의자체모듈 을제작할경우에도안전하다고판단한다. import java.io.*; class FileAccessThread extends Thread { public void run() { try { File f = new File("Test_367.txt"); ------------------------ ➀ if(f.exists()) { // 만약파일이존재하면파일내용을읽음 BufferedReader br = new BufferedReader(new FileReader(f)); br.close(); catch(ioexception e) { System.err.println("IOException occured"); 143

class FileDeleteThread extends Thread { public void run() { File f = new File("Test_367.txt"); ------------------------ ➀ if(f.exists()) { // f.delete(); 만약파일이존재하면 파일을삭제함 public class U367 { public static void main(string[] args) { // 파일의읽기와파일을삭제하는것을동시에수행한다. FileAccessThread fileaccessthread = new FileAccessThread(); ------------------------ ➁ FileDeleteThread filedeletethread = new FileDeleteThread(); fileaccessthread.start(); filedeletethread.start(); 다음의예제와같이검사시점과사용시점이달라발생하는경쟁조건을가정해볼수있다. File f = newfil e("toctou.txt"); if (!f.exists()) { FileOutputStreamfos = null; try { fos = newfileoutputstream("toctou.txt"); // 파일에대한처리를한다. catch (IOException e) { // TODO에러처리를한다. finally { // 자원을해제한다. 하지만자바에서는스트림을생성할때을 IOExceptin 발생시키며이에따라 예외에대한적절한처리를하면되므로문제가되지않는다. 144

TOCTOU는 C 프로그래밍을할때다음과같은경우에발생한다. if (!access(file,w_ok)) { f = fopen(file,"w+"); operate(f);... else { fprintf(stderr,"unable to open file %s.\n",file); 실제로경쟁조건이이루어지지않는경우취약하지않다. file = new File(filename); File[] flist = file.listfiles(); for (int i = 0; i < flist.length; i++) { currentlist.add(flist[i].getabsolutepath() + "$" + getlastmodifiedtime(flist[i]) + "$" + ((flist[i].length() / 1024) > 0? (flist[i].length() / 1024) : 1) + "KB"); 마. 참고문헌 [1] CWE-367 Time-of-check Time-of-use(TOCTOU) Race Condition - http://cwe.mitre.org/data/definitions/367.html 145

3. 제어문을사용하지않는재귀함수 가. 개요 재귀의순환횟수를제어하지못하여할당된메모리나프로그램스택등의 자원을과다하게사용하면위험하다. 대부분의경우, 귀납조건이 (base case) 없 는재귀함수는무한루프에빠져들게되고자원고갈을유발함으로써시스템의 정상적인서비스를제공할수없게한다 < 그림 3-17> 제어문을사용하지않는재귀함수 위그림은함수 Func_A가재귀함수형태로무한반복함으로써자원고갈을 유발하는경우를보여주고있다. 나. 보안대책 모든재귀호출을조건문블럭이나반복문블럭안에서만수행한다. 146

다. 코드예제 다음의예제는적절한나 JAVA C에서적절한제어문없이자기자신을호출하 여무한재귀가되는예이다. 안전하지않은코드의예 JAVA 1: 2: public int factorial(int n) { 3: return n * factorial(n - 1); 4: 5:... 안전하지않은코드의예 C 1: int fac(n) { 2: return n*fac(n-1); 3: 이와같은재귀호출은조건문이나반복문같은제어문을통하여다음과같이 해당루프를빠져나올수있게기술되어야한다. 안전한코드의예 JAVA 1: public int factorial(int n) { 2: int i; 3: if (n == 1) { 4: i = 1; 5: else { 6: i = n * factorial(n - 1); 7: 8: return i; 9: 안전한코드의예 C 1: int fac(n) { 2: if (n <= 0) return 1; 3: else return n*fac(n-1); 4: 10: 147

위의예제들은 n 나씩감소시킴으로써이 n 1 이될때최종, 계산값을 return 함으로써종료된다. 라. 진단방법 자신을호출하는재귀함수나메소드가존재하는지식별하고, 해당함수내에 제어문을통하여해당함수에서리턴될수있는지확인한다. 제어문을통하여 재귀함수를빠져나올수있으면안전하다. public int factorial(int n) { return n * factorial(n - 1);... ------------------------ ➀ 재귀함수는함수내부에서자신을호출하는경우이다. 함수명과파라미터의 개수, 파라미터의자료형이일치하는경우취약하다. public class RecursiveCall { private int a public void func(int a) { this.a = a; public void func(string a) { func(a); 다음의예제와같이같은이름에파라미터가다른함수가있는경우는취약 하지않다. 다음의예제는 Func(String a) 에서 func(int a) 를호출하고있는데 148

recursive call 이아닌다른함수의호출이므로안전하다. public class RecursiveCall { private int a public void func(int a) { this.a = a; public void func(string a) { func(integer.parseint(a)); 제어조건이존재하여제어가되는재귀인경우취약하지않다. public MenuVO getfirstleafchildmenu(int menuseq) { List<MenuVO> childmenulist = menumap.get(menuseq).getchild MenuList(); if (CollectionUtils.isEmpty(childMenuList)) { return menumap.get(menuseq); return getfirstleafchildmenu( childmenulist.get(0).getmenuseq()); 마. 참고문헌 [1] CWE-674 Uncontrolled Recursion - http://cwe.mitre.org/data/definitions/674.html 149

제4절에러처리 에러를처리하지않거나, 불충하게처리하여에러정보에중요정보( 시스템내부정보등 ) 가포함될때발생할수있는취약점 1. 오류메시지통한정보노출 가. 개요 소프트웨어가실행환경, 사용자, 관련데이터에대한민감한정보를포함하는 오류메시지를생성하여외부에제공하는경우공격자의악성행위를도와줄수 있다. 예외발생시예외이름이나스택트레이스를출력하는경우프로그램내부구 조를쉽게파악할수있다. < 그림오3-18> 류메시지통한정보노출 < 그림은 3-18> 오류메시지통한정보노출 을나타내주고있으며, 오류화면 을통해서해당시스템의운영체제가윈도우계열이며데이터베이스는 MS-SQL 을사용함을알수있다. 150

나. 보안대책 오류메시지는정해진사용자에게유용한최소한의정보만포함하도록한다. 소스코드에서 예외상황은내부적으로처리하고사용자에게민감한정보를포함 하는오류를출력하지않도록설정하고적절한환경설정을통해에러정보를노 출하지않고, 미리정의된페이지를제공하도록설정한다. 다. 코드예제 예외이름이나오류추적정보를출력하면프로그램내부정보가유출된다. 안전하지않은코드의예 JAVA 1: public static void main(string[] args) { 2: String urlstring = args[0]; 3: try{ 4: URL url = new URL(urlString); 5: URLConnection cmx = 6: url.openconnection(); 7: cmx.connect(); 8: 9: catch (Exception e) 10: { e.printstacktrace(); 11: 12: 예외이름이나오류추적정보를출력하지않는다. 안전한코드의예 JAVA 1: public static void main(string[] args) { 2: String urlstring = args[0]; 3: try{ 4: URL url = new URL(urlString); 5: URLConnection cmx = 6: url.openconnection(); 151

7: cmx.connect(); 8: 9: catch (Exception e) 10: { System.out.println(" 연결예외발생 "); 11: 12: 라. 진단방법 해당취약점에서시스템환경, 유저정보, 민감한정보등에대한기준을정적 도구가판단하기어려움. 이에따라진단원이출력함수등을통하여외부에출 력되는값중민감한정보등을판단할필요가있다. 오류메시지를출력하는 경우해당오류에시스템환경, 유저정보, 데이터등민감한정보가포함되어있 는지확인한다. public static void main(string[] args) { String urlstring = args[0]; try{ URL url = new URL(urlString); URLConnection cmx = url.openconnection(); cmx.connect(); catch (Exception e) { e.printstacktrace(); ------------------------ ➀ 오류메시지를통해환경, 사용자, 관련데이터등의내부정보가유출될경우 취약하다. W2 : EgovUnitCalc.jsp <%@ page language="java" contenttype="text/html; charset=utf-8" buffer="none"%> <%@page import="egovframework.com.utl.fda.ucc.service.egovunitcalcutil" %> <% 152

String scmd = r equest.getparameter("cmd") == null? "" : (String) r equest.getparameter("cmd"); double nresult=0.0; try{ if(!scmd.equals("")){ EgovUnitCalcUtil egovunitcalcutil= new EgovUnitCalcUtil();. catch(exception e){ e.printstacktrace(); %> 마. 참고문헌 [1] CWE-209 Information Exposure Through an Error Message - http://cwe.mitre.org/data/definitions/209.html 153

2. 오류상황에대응부재 가. 개요 오류가발생할수있는부분을확인하였으나, 이러한오류에대하여예외처 리를하지않을경우에는프로그램이충돌하거나종료되는등의개발자가의도 하지않은결과가발생한다. 나. 보안대책 오류가발생할수있는부분에대하여제어문을사용하여적절하게예외처 리(C/C++ 에서와 if 에서 switch, Java 등try-catch ) 를한다. 다. 코드예제 위예제는 try 블록에서발생하는오류를포착 (catch) 하고있지만그오류에대 해서아무조치를하고있지않다. 따라서프로그램이계속실행되기때문에프 로그램에서어떤일이일어났는지전혀알수없게된다. 안전하지않은코드의예 JAVA 1: 2: private Connection conn; 3: 4: public Connection DBConnect(String url, String id, String password) { 5: try { 6: String CONNECT_STRING = url + ":" + id + ":" + password; 7: InitialContext ctx = new InitialContext(); 8: DataSource datasource = (DataSource) ctx.lookup(connect_string); 9: conn = datasource.getconnection(); 10: catch (SQLException e) { 11: // catch 블록이비어있음 12: catch (NamingException e) { 13: // catch 블록이비어있음 14: 15: return conn; 16: 154

예외를포착 (catch) 한후, 각각의예외사항 (Exception ) 에대하여적절하게처리해야한다. 안전한코드의예 JAVA 1: 2: private Connection conn; 3: 4: public Connection DBConnect(String url, String id, String password) { 5: try { 6: String CONNECT_STRING = url + ":" + id + ":" + password; 7: InitialContext ctx = new InitialContext(); 8: DataSource datasource = (DataSource) ctx.lookup(connect_string); 9: conn = datasource.getconnection(); 10: catch (SQLException e) { 11: // Exception catch 이후에 Exception 대한적절한처리를해야한다. 12: if ( conn!= null ) { 13: try { 14: conn.close(); 15: catch (SQLException e1) { 16: conn = null; 17: 18: 19: catch (NamingException e) { 20: // Exception catch 이후에 Exception 대한적절한처리를해야한다. 21: if ( conn!= null ) { 22: try { 23: conn.close(); 24: catch (SQLException e1) { 25: conn = null; 26: 27: 28: 29: return conn; 30: 라. 진단방법 오류가발생할수있는부분에대하여예외처리를수행했는지확인하고제어문 을통하여예외처리하는루틴이비어있는지확인한다. 155

다음의예제는 try 블록에서발생하는오류를포착(catch) 하고있지만그오 류에대해서아무조치를하고있지않다. 따라서프로그램이계속실행되기 때문에프로그램에서어떤일이일어났는지전혀알수없게된다. 1: 2: private Connection conn; 3: 4: public Connection DBConnect(String url, String id, String password) { 5: try { 6: String CONNECT_STRING = url + ":" + id + ":" + password; 7: InitialContext ctx = new InitialContext(); 8: DataSource datasource = (DataSource) ctx.lookup(connect_string); 9: conn = datasource.getconnection(); 10: catch (SQLException e) { 11: // catch 블록이비어있음 12: catch (NamingException e) { 13: // catch 블록이비어있음 14: 15: return conn; 16: 마. 참고문헌 [1] CWE-390 Detection of Error Condition Without Action - http://cwe.mitre.org/data/definitions/390.html [2] OWASP Top Ten 2004 Category A7 - Improper Error Handling 156

3. 적절하지않은예외처리 가. 개요 프로그램수행중에함수의결과값에대한적절한처리또는예외상황에대한조건을적절하게검사하지않을경우, 예기치않은문제를야기할수있다. 나. 보안대책 값을반환하는모든함수의결과값을검사하여, 그값이기대한값인지 검사하고, 예외처리를사용하는경우에광범위한예외처리대신구체적인예 외처리를수행한다. 다. 코드예제 함수의인자로 filename 에대한 Null 체크없이 File 객체를생성하였으며광범, 위한예외클래스인 Exception 을사용하여예외처리를했다. 안전하지않은코드의예 JAVA 1: public void readfromfile(string filename) { 2: try { 3:... 4: File myfile = new File(fileName); 5: FileReader fr = new FileReader(myFile); 6:... 7: catch (Exception ex) {... 8: filename 이 Null 값인지검사하고 Null 이면에러메시지를출력과예외를발생 시킨다. 또한발생가능한모든예외에대한구체적인예외처리를한다. 안전한코드의예 JAVA 1: public void readfromfile(string filename) throws FileNotFoundException, 2: IOException,MyException { 3: try { 157

4:... 5: // filename 에대한널을조사 6: if ( filename == NULL ) throw new MyException(" 에러 ); 7: File myfile = new File(fileName); 8: FileReader fr = new FileReader(myFile); 9:... 10: // 함수루틴에서모든가능한예외에대해서처리한다. 11: catch (FileNotFoundException fe) {... 12: catch (IOException ie) {... 13: 라. 진단방법 함수또는메서드에대하여반환값을검사하고예외를발생시키는경우구체적 인예외처리를수행하는지확인한다. public void readfromfile(string filename) { try {... File myfile = new File(fileName); FileReader fr = new FileReader(myFile);... catch (Exception ex) {... ------------------------ ➀ 마. 참고문헌 [1] CWE-754 Improper Check for Unusual or Exceptional Conditions - http://cwe.mitre.org/data/definitions/754.html [2] SANS Top 25 Most Dangerous Software Errors, http://www.sans.org/top25-software-errors/ [3] M. Howard, D. LeBlanc, Writing Secure Code, Second Edition, Microsoft Press 158

제5절코드품질 타입변환오류자원, ( 메모리등 ) 의부적절한반환등과같이개발자가범할수 있는코딩오류로인해유발되는취약점 1. 널(Null) 포인터역참조 가. 개요 널포인터역참조는일' 반적으로그객체가이 NULL 될수없다' 라고하는 가정을위반했을때발생한다. 공격자가의도적으로 NULL 포인터역참조를 발생시키는경우, 그결과발생하는예외상황을이용하여추후의공격을계획 하는데사용될수있다. 나. 보안대책 널이될수있는레퍼런스 (reference) 는참조하기전에널값인지를검사하여 안전한경우에만사용한다. 다. 코드예제 다음의예제는 "cmd" 프로퍼티가항상정의되어있다고가정하고있지만, 만 약공격자가프로그램의환경을제어해 "cmd" 프로퍼티가정의되지않게하면, cmd는널이되어 trim() 메소드를호출할때널포인터예외가발생하게된다. 안전하지않은코드의예 JAVA 1: 2: public void f() { 3: String cmd = System.getProperty("cmd"); 4: // cmd 가 null 인지체크하지않았다. 5: cmd = cmd.trim(); 6: System.out.println(cmd); 159

7: 먼저가 cmd 널인지검사한후에사용한다. 안전한코드의예 JAVA 1: 2: public void f() { 3: String cmd = System.getProperty("cmd"); 4: // cmd 가 null 인지체크하여야한다. 5: if (cmd!= null) { 6: cmd = cmd.trim(); 7: System.out.println(cmd); 8: else System.out.println("null command"); 9: 라. 진단방법 표현된객체가널값이될수있는지확인한다. 만약널값이될수있는지체크 하여예외처리를한경우안전으로판단하고널체크를하지않은경우취약한 것으로판단한다. public void f() { String cmd = System.getProperty("cmd"); // cmd가인지 null 체크하지않았다. cmd = cmd.trim(); System.out.println(cmd); ------------------------ ➀ ( 참고) 널값이될가능성은다음의기준으로확인 - - 널이할당된값은널이될수있음 선언이된후객체가생성되지않은값은널이될수있음 - 널인객체로필드접근을하거나널 / 인객체에메소드호출을할경우 결과값은널이될수있음 - 문자열등널 이될수있는객(NULLABLE)' 체들의연산결과값은널 160

이될수있음 변수를 NULL로초기화한후값을대입하지못하는경우에해당변수를 참조하면널포인터역참조가발생한다. 다음의예제는 330 라인에서널로초기화한이후에 338 라인에서 NULL 값인vmrs 값을참조하고있다. 이경우는조건문등에의해분기가이루어지 면서 NULL 로초기화된변수가초기화되지않는경우이다. public Hashtable deleteall(connection con, GenericModel model) throws Exception { Hashtable<String, Object> m = new Hashtable<String, Object>(); 330: VMResultSet vmrs = null int rvalue = 0; try{ ArrayList grid =null ;... con.commit(); con.setautocommit(true); if(rvalue > 0) { vmrs = dao.listgoodknowquestion(con, model); vmrs.setmessage(utilmsg.getinstance().getmsg("suc003")); else{ 383: vmrs.setmessage(utilmsg.getinstance().getmsg("err000")); m.put("result", vmrs); catch (Exception e) {... finally {... return m; 161

다른유형으로자주발생하는경우는의 database Connection, Statement등의 자원을사용한후에해제하는과정에서해당자원이널인지확인하지않는경 우이다. 다음의예제에서는 81번째라인에서널로 Statement 초기화하고있고 85번째 라인에서예외가발생하면번 95 라인의의 Statement 값을대입하지못하고 111 번의 finally 문으로분기한후번 117 라인이실행되면서널값을참조하게된 다. private static void m ak eli st () { Connection con = null StringBuffer sql = new StringBuffer(); 81: PreparedStatement statement = null ResultSet rs = null list.clear(); try { 85: con = GenericDAO.getDataSource().getConnection();... 95: statement = con.preparestatement(sql.tostring()); rs = statement.executequery(); while (rs.next()) { String authgrp = rs.getstring("authority_group");... catch (Exception e) { if (verbose) {... 111: finally { try { rs.close(); catch (Exception e1) { 162

try { 117: statement.close(); catch (Exception e1) { if (con!= null) { try { con.close(); catch (SQLException e) { e.printstacktrace(); Interface Iteration은을 Null 리턴하지않는데를 Null Dereference 탐지해내 는경우나, 함수의설계상 Null을리턴하지않도록정의되어있는함수의리 턴값을참조할경우에는이 NullPointerException 발생하지않는다. 이를확인 하기위해서는함수의 Java document 를유의깊게살펴보아야한다. 함수의 리턴값에대한기술되어있는을 Javadoc 참조해서코드를검토하여야한다. StringTokenizer st = new StringTokenizer(info_url,"?"); int sti=0; while (st.hasmoretokens()) { if(sti==0) if(sti==1) sti++; jsonurl=st.nexttoken(); jsparam=st.nexttoken(); String [] jsonparams = jsparam.split("&"); jsparam=""; for (int i=0;i<jsonparams.length;i++){ jsparam=jsparam+jsonparams[i].split("=")[0]+": '"+jsonparams[i].split("=")[1]+"'"; if(i!=jsonparams.length-1)jsparam=jsparam+","; 163

Try ~ catch 문에서를 throw 하는경우절 catch 이후는실행되지않음으로 Null Dereference 가발생하지않는다. 다음의예제에서은 System.out.println("After Throw 1"); 실행되지않는다. publicstaticvoidte stfunction(string datestr1) throws Exception { SimpleDateFormatsdf = newsimpledateformat("yyyymmdd",locale.getdefault()); Date date1 = null try { date1 = sdf.parse(datestr1); catch (ParseException e) { System.err.println("Inner Function"); e.printstacktrace(); thrownew Exception(e); System.out.println("After Throw 1"); intdays1 = (int)((date1.gettime()/100000)/24); System.out.println("After Throw 2"); 다음의예제에서 intdays1 = (int)((date1.gettime()/100000)/24); 에서 data1이 Null 일경우, NullPointerException 이발생하고다음단계로진행되지않게된 다. 이러한이유로 intdays2 = (int)((date1.gettime()/100000)/24); 문은실행되 지않으며을 Null 참조하지않게된다. SimpleDateFormatsdf = newsimpledateformat("yyyymmdd",locale.getdefault()); Date date1 = null try { date1 = sdf.parse(datestr1); catch (ParseException e) { 164

System.err.println("Inner Function"); e.printstacktrace(); System.out.println("After Throw 1"); intdays1 = (int)((date1.gettime()/100000)/24); intdays2 = (int)((date1.gettime()/100000)/24); System.out.println("After Throw 2"); 다음과같은조건은앞의널체크조건이참이면뒤의 or 조건은판단하지 않는다. 그러므로뒤의문장에서 NullPointerException 은발생하지않는다. if (a == null a.length() == 0) { // 처리한다. 다음의예제에서는 catch 절안에서 return 하고있으므로예외가발생하여 f 에값을넣지못하더라도는 f.write 실행되지않는다. FileOutputStream f = null try { f = newfileoutputstream("toctou"); catch (FileNotFoundException e) { try { // TODO Auto-generated catch block e.printstacktrace(); return f.write(0); catch (IOException e) { // TODO Auto-generated catch block e.printstacktrace(); 165

다음의예제에서는 JSP 내장객체를참조하고있다. 이경우에는 Null Deference 가발생하지않는다. <td align="center" colspan='2'><inp ut type='button' val ue=' 다시로그인' onclick="location.href='${pagecontext.request.contextpath/utl/sec/certlogin.do'"></td> 다음과같은 Integer 클래스의 static 함수호출은 Integer가객체가아니므로 Null 값을참조하지않는다. Integer.parseInt(stringValue); 다음과같이 Data Flow 상이아닌단일함수에서파라미터가널일경우는 취약하지않은것으로판단한다. 해당함수를호출할때널체크를하도록해 야한다. public SmsConnection sendrequsest(smsconnection smsconn) throws Exception { // SMS 전송요청 SmsSender sender = null SmsConnection result = null try { sender = new SmsSender(smeConfigPath); sender.open(); result = sendrequsest(smsconn, sender); finally { if (sender!= null) { sender.close(); smsconn.setresult(result.getresult()); smsconn.setresultmessage(result.getresultmessage()); return smsconn; 166

프로그램의논리상널이아닌경우에는오탐으로처리한다. 다음의예제에서 BsnList는 java.util.list 인터페이스로 List 의사이즈만큼순환참조하고있다. List 의사이즈내에서 get(int offset) 함수는을 Null 리턴하지않는다. hm에는항상널이아니라는것이보장되고을 hm 참조는문제가되지않는 다. 그러므로 Null Dereference 는발생하지않는다. for( int i = 0 ; i < BsnList.size() ; i++){ hm = (HashMap)BsnList.get(i) ; if(hm.get("bsn_se")!= null &&!hm.get("bsn_se").equals("")){ if (hm.get("bsn_se").equals("eta")){ JRE 기본패키지클래스의생성자의경우, Null 을반환할수있다고명시 되어있지않은한을 Null 리턴한다고판단하지않는다. if(infolist == null) infolist = new ArrayList(); for(int i=0; containers!= null && i < containers.length; i++) { hm = new HashMap(); for(int j=0; j < paramrecvdata.length; j++) { h m. p u t ( p a r a m R e c v D a t a [ j ], containers[i].getfield(paramrecvdata[j]).getvalueasstring()); infolist.add(hm); 마. 참고문헌 [1] CWE-476 NULL Pointer Dereference - 167

http://cwe.mitre.org/data/definitions/476.html [2] OWASP Top 10 2004 - (OWASP 2004) A9 Application Denial of Service 168

2. 부적절한자원해제 가. 개요 프로그램의자원예를, 들면열린파일기술자힙 (open file descriptor), 메모리 (heap memory), 소켓(socket) 등은유한한자원이다. 이러한자원을할당받아 사용한후, 더이상사용하지않는경우에는적절히반환하여야하는데프로그램 오류또는에러로사용이끝난자원을반환하지못하는경우이다. < 그림 3-19> 부적절한자원해제 나. 보안대책 자원을획득하여사용한다음에는반드시자원을해제하여반환한다. 다. 코드예제 데이터베이스에연결된후에사용중예외가발생하면할당된데이터베이스 커넥션및 JDBC 자원이반환되지않는다. 169

안전하지않은코드의예 JAVA 1:...... 2: try { 3: Class.forName("com.mysql.jdbc.Driver"); 4: conn = DriverManager.getConnection(url); 5: conn.close(); 6: catch (ClassNotFoundException e) { 7: 예외상황이발생하여함수가종료될때예외의발생여부와상관없이 finally 블록에서할당받은자원을반환한다. 안전한코드의예 JAVA 1:...... 2: try { 3: Class.forName("com.mysql.jdbc.Driver"); 4: conn = DriverManager.getConnection(url); 5: stmt = conn.createstatement() ; 6: 7:...... 8: 9: catch (ClassNotFoundException e) { 10: System.err.print("error"); 11: catch (SQLException e) { 12: System.err.print("error"); 13: finally { 14: if(stmt!= null){ 15: try{ 16: stmt.close() ; 17: catch(sqlexception e){ 18:...... 19: 20: 21: if(conn!= null){ 22: try{ 23: conn.close() ; 24: catch(sqlexception e){ 25:...... 26: 27: 28: 라. 진단방법 170

자원( 파일기술자, 힙메모리, 소켓) 이선언되고, 선언된자원이할당된경우해제되는 지확인한다. 진단자는제어문, 예외처리문등의분기등에따라모든흐름 (control flow) 을판단하여자원해제여부를체크해야한다. 할당된자원이해제될 경우안전하다고판단하고자원이해제되지않는분기가존재할경우취약하 다. public void f() { String cmd = System.getProperty("cmd"); // cmd 가인지 null 체크하지않았다. cmd = cmd.trim(); System.out.println(cmd); ------------------------ ➀ 자원을얻어사용한후에는반드시자원을해제해야자원유출을막을수있다. 다음과같이 rs, pstmt, pstmt1, conn등자원을얻어서사용한후 try catch 문안에서해당자원을해제하면중간에예외가발생할경우자원해제가이루 어지지않은채예외처리구문인 일어난다. catch 구문으로건너뛰게되어자원유출이 try{ String sqlselect = "SELECT USER_ID, PWD FROM OSS_USER" String sqlupdate = "UPDATE OSS_USER SET PASSWD =? WHERE USER_ID =?" Class.forName("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.140.20:1521:ossdb", "oss", "OSS"); pstmt = conn.preparestatement(sqlselect); pstmt1 = conn.preparestatement(sqlupdate); rs = pstmt.executequery(); while( rs.next() ){ System.out.println("#################### user_id : " + 171

rs.getstring("user_id")); pstmt1.setstring(1, CryptUtils.encrypt(rs.getString("PWD"))); pstmt1.setstring(2, rs.getstring("user_id")); pstmt1.executeupdate(); pstmt1.clearparameters(); System.out.println("#################### ing01 : "); conn.commit(); rs.close(); pstmt.close(); pstmt1.close(); conn.close(); catch(exception e){ e.printstacktrace(); try{conn.rollback();catch(exception ex){ 다음과같은예제는 rs.close(), pstmt.close() 등자원해제가 try catch 문안에 서함께이루어지고있다. 이경우 rs.close() 문에서예외가발생하면 pstmt.close() 이하의자원해제는이루어지지않으므로자원유출이일어난다. try{ String sqlselect = "SELECT USER_ID, PWD FROM OSS_USER" String sqlupdate = "UPDATE OSS_USER SET PASSWD =? WHERE USER_ID =?" Class.forName("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.140.20:1521:ossdb", "oss", "OSS"); pstmt = conn.preparestatement(sqlselect); pstmt1 = conn.pr eparestatement(sqlupdate); rs = pstmt.executequery(); while( rs.next() ){ 172

System.out.println("#################### user_id : " + rs.getstring("user_id")); pstmt1.setstring(1, CryptUtils.encrypt(rs.getString("PWD"))); pstmt1.setstring(2, rs.getstring("user_id")); pstmt1.executeupdate(); pstmt1.clearparameters(); System.out.println("#################### ing01 : "); conn.commit(); catch(exception e){ e.printstacktrace(); try{conn.rollback();catch(exception ex){ finally { try{ rs.close(); pstmt.close(); pstmt1.close(); conn.close(); catch(exception e){ 다음의예제와같이 finally 절에서을 connection 해주 close 고있으므로자원 의부적절한반환이이루어지지않는다. try{ String sqlselect = "SELECT USER_ID, PWD FROM OSS_USER" String sqlupdate = "UPDATE OSS_USER SET PASSWD =? WHERE USER_ID =?" Class.forName("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.140.20:1521:ossdb", "oss", "OSS"); pstmt = conn.preparestatement(sqlselect); pstmt1 = conn.pr eparestatement(sqlupdate); 173

rs = pstmt.executequery(); while( rs.next() ){ System.out.println("#################### user_id : " + rs.getstring("user_id")); pstmt1.setstring(1, CryptUtils.encrypt(rs.getString("PWD"))); pstmt1.setstring(2, rs.getstring("user_id")); pstmt1.executeupdate(); pstmt1.clearparameters(); System.out.println("#################### ing01 : "); conn.commit(); catch(exception e){ e.printstacktrace(); try{conn.rollback();catch(exception ex){ finally { try{rs.close();catch(exception e){ try{pstmt.close();catch(exception e){ try{pstmt1.close();catch(exception e){ try{conn.close();catch(exception e){ 다음의예제는예외가발생하여절catch 에서 return 하고있다. 예외가발생 할경우에는 Socket 할당이이루어지지않으므로자원의부적절한반환이이루 어지지않는다. public void run() { Socket sock= null try { if (threadhost!= null ) { sock = new Socket(threadHost, threadport); else { sock = new Socket(threadInetAddr, threadport); 174

catch (IOException ioe) { threadexception = ioe; return threadsocket = sock; 클래스의멤버변수에자원을저장하고사용하기위한경우에는자원해제의 주체가함수내부가아닌함수외부이므로취약하지않다. public void run() { Socket sock = null try { if (threadhost!= null) { sock = new Socket(threadHost, threadport); else { sock = new Socket(threadInetAddr, threadport); catch (IOException ioe) { threadexception = ioe; threadsocket = sock; 함수의역할이자원을리턴하는경우에는자원의해제는함수를호출한쪽에 서담당하므로취약하지않다. public class DBUtil { public static Connection getconnection() { Connection conn = null; try { 175

Class.forName("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection(DbInfo.url,DbInfo.id, DbInfo.password); catch (SQLException e) { // TODO Auto-generated catch block e.printstacktrace(); catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printstacktrace(); return conn; 마. 참고문헌 [1] CWE-404 Improper Resource Shutdown or Release - http://cwe.mitre.org/data/definitions/404.html [2] OWASP Top 10 2004 - (OWASP 2004) A9 Application Denial of Service [3] SANS Top 25 2009 - (SANS 2009) Risky Resource Management 176

제6절캡슐화 중요한데이터또는기능성을불충분하게캡슐화함으로써인가되지않은사 용자에게데이터누출이가능해지는취약점 1. 잘못된세션에의한데이터노출 가. 개요 다중스레드환경에서는싱글톤 (singleton) 11) 객체필드에경쟁조건 (race condition) 이발생할수있다. 따라서다중스레드환경에서서블릿에 (servlet) 정보를저장하는멤버변수가포함되지않도록하여서로다른세션에서데이터를공유하지않도록해야한다. 나. 보안대책 싱글톤패턴을사용하는경우, 변수범위(Scope) 에주의를기울여야한다. 특 히 Java에서는 HttpServlet 클래스의하위클래스에서멤버필드를선언하지않도 록하고필요한경우지역변수를선언하여사용한다. 다. 코드예제 두사용자가거의동시에접속할시, 첫번째사용자를위한스레드가 out.println(...) 을수행하기전에두번째사용자의스레드가을 name =... 수행 하면첫번째사용자는두번째사용자의정보(name) 를보게된다. 안전하지않은코드의예 JAVA 1: public class U488 extends HttpServlet { 2: private String name; 11) Singleton Pattern : GOF 32 가지패턴중하나. 하나의프로그램내에서하나의인스턴스만을생성해야만하는패턴. Connection Pool, Thred Pool 과같이풀(Pool) 형태로관리되는클래스의경우프로그램내에서단하나의인스턴트로관리해야하는경우를말함 177

3: protected void dopost(httpservletrequest request, HttpServletResponse response) 4: throws ServletException, IOException { 5: name = request.getparameter("name"); 6: 7: out.println(name + ", thanks for visiting!"); 8: 9: 필요한경우지역변수를선언하여사용한다. 안전한코드의예 JAVA 1: public class S488 extends HttpServlet { 2: protected void dopost(httpservletrequest request, HttpServletResponse response) 3: throws ServletException, IOException { 4: // 지역변수로변경한다. 5: String name = request.getparameter("name"); 6: if (name == null "".equals(name)) return; 7: out.println(name + ", thanks for visiting!"); 8: 9: 라. 진단방법 HttpServlet의하위클래스에멤버필드가선언되어있고 final이아닌경우는취 약한것으로판단한다. public class U488 extends HttpServlet { private String name; protected void dopost(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { name = request.getparameter("name"); out.println(name + ", thanks for visiting!"); 서블릿(JSP 포함) 에서상수로사용하지않는멤버변수를사용하면취약하다. 178

<%@ page import="javax.xml.namespace.*" %> <%@ page import="gov.mogaha.ntis.web.frs.gis.cmm.util.*" %> <%@ page import="gov.mogaha.ntis.cmm.util.ssosessionutil"%> <%! String commonpath = "/"; String imagepath= commonpath +"img/"; String imagepath_gis = imagepath +"gis/cmm/btn/"; JSP 페이지나서블릿내에서의내부클래스사용은멤버필드가아니므로취약 하지않다. <%@ page language="java" import="java.io.*,java.text.*,java.util.*,java.net.*,com.inswave.system.config.*,com.inswave.system.exception.*"%> <%! private class CacheEntity { String name; String lastmodified; String expires; String etag; Final 필드의경우클래스내부에서상수로사용되는값이므로취약하지않 다. <%@ page import="java.util.*" %> <%@ page import="gov.mogaha.ntis.cmm.util.stringutil" %> <%@ page import="gov.mogaha.ntis.cmm.util.page" %> <%@ taglib uri="cmm.tld" prefix="cmm" %> <%! final String imagepath = "/img/"; String tr eeimagepath = imagepath+"gis/por/tr ee_sub.gif"; %> 179

Spring 프레임워크기반의 egov 프레임워크는 IoC 를사용하고있다. 이는 프레임워크에서인스턴스를관리하기때문에안전하다. @Resource(name = "EgovFileMngService") private EgovFileMngService fileservice; 마. 참고문헌 [1] CWE-488 Exposure of Data Element to Wrong Session - http://cwe.mitre.org/data/definitions/488.html 180

2. 제거되지않고남은디버그코드 가. 개요 디버깅목적으로삽입된코드는개발이완료되면제거해야한다. 디버그코 드는설정등의민감한정보를담거나시스템을제어하게허용하는부분을담 고있을수있다만. 일남겨진채로배포될경우공격자가식별과정을우회하 거나의도하지않은정보와제어정보가노출될수있다. < 그림 3-20> 제거되지않고남은디버그코드 나. 보안대책 디버그코드는삭제후운영서버에배포한다. 다. 코드예제 다음의예제는 main() 메소드내에화면에출력하는디버깅코드를포함하고 있다. J2EE의경우 main() 메소드사용이필요없으며, 개발자들이콘솔응용프 로그램으로화면에디버깅코드를사용하는경우가일반적이다. 안전하지않은코드의예 JAVA 181

1: public class U489 extends HttpServlet { 2: protected void doget(httpservletrequest request, ) throws { 3: protected void dopost(httpservletrequest request, ) throws { 4: // 테스트를위한 main() 함수나디버깅용로그출력문등이남아있다. 5: public static void main(string args[]) { 6: System.err.printf("Print debug code"); 7: 8: 이에따라 J2EE와같은응용프로그램에서 main() 메소드는삭제한다. J2EE의 main() 메소드의경우디버깅코드인경우가일반적이다. 안전한코드의예 JAVA 1: public class S489 extends HttpServlet { 2: protected void doget(httpservletrequest request, ) throws { 3: protected void dopost(httpservletrequest request, ) throws { 4: // 테스트용코드는제거해준다. 5: 라. 진단방법 J2EE 를제외하고디버그코드를정적도구만으로판단하기는쉽지않다. 개발중테스트목적으로남아있는디버그코드가존재하는지확인한다 ( ➀). J2EE 환경(J2EE Standard) 에서는 main 메소드작성을금하고있으며, 일반적으 로개발자들은디버그코드를 main으로작성하므로 main 메소드가사용되는 경우디버그코드인지확인하여야한다. public class U489 extends HttpServlet { protected void doget(httpservletrequest request, ) throws { protected void dopost(httpservletrequest request, ) throws { // 테스트를위한함수나 main() 디버깅용로그출력문등이남아있다. public static void main(string args[]) { System.err.printf("Print debug code"); ------------------------ ➀ 182

J2EE와같은응용프로그램에서디버깅용으로사용되는 한다. main() 메소드는삭제 1: public class U489 extends HttpServlet { 2: protected void doget(httpservletrequest request, ) throws { 3: protected void dopost(httpservletrequest request, ) throws { 4: // 테스트를위한 main() 함수나디버깅용로그출력문등이남아있다. 5: public static void main(string args[]) { 6: System.err.pri ntf("print debug code"); 7: 마. 참고문헌 [1] CWE-489 Leftover Debug Code - http://cwe.mitre.org/data/definitions/489.html [2] M. Howard and D. LeBlanc. "Writing Secure Code". Page 505. 2nd Edition. Microsoft. 2002 [3] https://www.owasp.org/index.php/leftover_debug_code 183

3. 시스템데이터정보노출 가. 개요 시스템의내부데이터나디버깅관련정보가공개되면, 이를통해공격자에 게아이디어를제공하는등공격의빌미가된다. < 그림 3-21> 시스템데이터정보노출 나. 보안대책 디버깅을위해작성한시스템정보출력코드를모두삭제한다. 다. 코드예제 예외발생시 getmessage() 를통해오류와관련된시스템에러정보등민감한 정보가유출될수있다. 안전하지않은코드의예 JAVA 1: 2: public void f() { 3: try { g(); 4: catch (IOException e) { 5: // 예외발생시 printf(e.getmessage()) 를통해오류메시지정보가유출된다. 6: System.err.printf(e.getMessage()); 7: 8: 9: private void g() throws IOException { 10: 184