차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판이전 판
다음 판
이전 판
다음 판양쪽 다음 판
guide:java_개발_보안_가이드 [2013/12/24 04:17] 121.140.124.172guide:java_개발_보안_가이드 [2013/12/26 07:10] 121.140.124.172
줄 3368: 줄 3368:
 === 다. 예제 === === 다. 예제 ===
 <code | 안전하지 않는 코드 예제> <code | 안전하지 않는 코드 예제>
 +1: class FileMgmtThread extends Thread {
 +2:
 +3: private String manageType = "";
 +4:
 +5: public FileMgmtThread (String type) {
 +6: manageType = type;
 +7: }
 +8:
 +9: public void run() {
 +10: try {
 +11: if ( manageType.equals("READ") ) {
 +12: File f = new File("Test_367.txt");
 +13: if (f.exists()) { // 만약 파일이 존재하면 파일내용을 읽음
 +14: BufferedReader br = new BufferedReader(new FileReader(f));
 +15: br.close();
 +16: }
 +17: } else if ( manageType.equals("DELETE") ) {
 +18: File f = new File("Test_367.txt");
 +19: if (f.exists()) { // 만약 파일이 존재하면 파일을 삭제함
 +20: f.delete();
 +21: } else {
 +22: ;
 +23: }
 +24: }
 +25: } catch (IOException e) {
 +26: }
 +27: }
 +28: }
 +29:
 +30: public class CWE367 {
 +31: public static void main(String[] args) {
 +32: // 파일의 읽기와 파일을 삭제하는 것을 동시에 수행한다.
 +33: FileMgmtThread fileAccessThread = new FileMgmtThread("READ");
 +34: FileMgmtThread fileDeleteThread = new FileMgmtThread("DELETE");
 +35: fileAccessThread.start();
 +36: fileDeleteThread.start();
 +37: }
 +38: }
 +</code>
 +위 예제는 파일의 존재를 확인하는 부분과 실제로 파일을 사용하는 부분을 실행하는 과정에서 시간차가 발생하는 경우, 파일에 대한 삭제가 발생하여 프로그램이 예상하지 못하는 태로 수행될 수 있다. 또한 위 예제는 시간차를 이용하여 파일을 변경하는 등의 공격에 취약할 수 있다.
 +<code | 안전한 코드 예제>
 +1: class FileMgmtThread extends Thread {
 +2:
 +3: private static final String SYNC = "SYNC" ;
 +4:
 +5: private String manageType = "";
 +6:
 +7: public FileMgmtThread (String type) {
 +8: manageType = type;
 +9: }
 +10:
 +11: public void run() {
 +12: // synchronized 를 사용함으로써 지정된 객체에 lock이 걸려서
 +13: // 블록을 수행하는 동안 다른 Thread가 접근할 수 없다.
 +14: synchronized(SYNC) {
 +15: try {
 +16: if ( manageType.equals("READ") ) {
 +17: File f = new File("Test_367.txt");
 +18: if (f.exists()) { // 만약 파일이 존재하면 파일내용을 읽음
 +19: BufferedReader br = new BufferedReader(new FileReader(f));
 +20: br.close();
 +21: }
 +22: } else if ( manageType.equals("DELETE") ) {
 +23: File f = new File("Test_367.txt");
 +24: if (f.exists()) { // 만약 파일이 존재하면 파일을 삭제함
 +25: f.delete();
 +26: } else {
 +27: ;
 +28: }
 +29: }
 +30: } catch (IOException e) {
 +31: }
 +32: }
 +33: }
 +34: }
 +35:
 +36: public class CWE367 {
 +37: public static void main(String[] args) {
 +38: // 파일의 읽기와 파일을 삭제하는 것을 동시에 수행한다.
 +39: FileMgmtThread fileAccessThread = new FileMgmtThread("READ");
 +40: FileMgmtThread fileDeleteThread = new FileMgmtThread("DELETE");
 +41: fileAccessThread.start();
 +42: fileDeleteThread.start();
 +43: }
 +44: }
 +</code>
 +공유자원(예를 들어, 파일)을 여러 스레드가 접근하여 사용할 경우, 동기화 구문을 이용하여 한 번에 하나의 스레드만 접근 가능하도록 변경한다.
 +<code | 안전하지 않는 코드 예제>
 +1: public class MyServlet extends HttpServlet {
 +2: String name;
 +3: public void doPost ( HttpRequestRequest hreq, HttpResponceServlet hres ) {
 +4: name = hreq.getParameter("name");
 +5: …….
 +6: }
 +</code>
 +HttpServet을 상속받은 MyServlet이 멤버필드로 name을 사용하고 있다. name은 MyServlet을 동시에 사용하는 모든 사용자에게 정보가 노출된다.
 +<code | 안전한 코드 예제>
 +1: public class MyServlet extends HttpServlet {
 +2: public void doPost ( HttpRequestRequest hreq, HttpResponceServlet hres ) {
 +3: // 서블릿 프로그램의 멤버 변수는 공용으로 사용하기 때문에 로컬로 정의해서 사용한다.
 +4: String name = hreq.getParameter("name");
 +5: ...
 +6: }
 +</code>
 +name을 doPost 메소드에만 사용할 수 있도록 로컬로 정의하여 경쟁상태를 제거한다.
 +<code | 안전한 코드 예제>
 +1: public class MyClass {
 +2: String name;
 +3: public void doProcess (HttpRequestRequest hreq ) {
 +4: // 멤버변수 공유 시 동기화시킨다.
 +5: synchronized {
 +6: name = hreq.getParameter("name");
 +7: ...
 +8: }
 +9: ...
 +10: }
 +</code>
 +업무상 name을 여러 쓰레드 사이에서 공유해야 한다면, synchronized 문장을 사용하여,임계코드가 쓰레드들 간 동기화할 필요가 있다.
 +=== 라. 참고 문헌 ===
 +[1] CWE-367 경쟁 조건: 검사시점과 사용시점 - http://cwe.mitre.org/data/definitions/367.html
 +\\
 +[2] SANS Top 25 Most Dangerous Software Errors
 +\\
 +[3] Michael Howard, David LeBlanc and John Viega. "24 Deadly Sins of Software Security"."Sin 13: Race Conditions." Page 205. McGraw-Hill. 2010
 +\\
 +[4] Andrei Alexandrescu. "volatile - Multithreaded Programmer's Best Friend". Dr. Dobb's. 2008-02-01
 +\\
 +[5] Steven Devijver. "Thread-safe webapps using Spring" David Wheeler. "Prevent race conditions". 2007-10-04
 +\\
 +[6] Matt Bishop. "RaceConditions, Files, andSecurityFlaws; or the Tortoise andtheHareRedux". September 1995
 +\\
 +[7] Johannes Ullrich. "Top 25 Series - Rank 25 - Race Conditions". SANS Software Security Institute. 2010-03-26
 +==== 4. J2EE 잘못된 습관: 스레드의 직접 사용(J2EE Bad Practices: Direct Use of Threads) ====
 +
 +=== 가. 정의 ===
 +J2EE 표준은 웹 응용프로그램에서 스레드 사용을 금지하고 있다. 따라서 스레드를 직접 사용하는 것 대신에 해당 플랫폼에서 제공하는 병렬 실행을 위한 프레임워크를 사용해야 한다. 그렇지 않을 경우, 교착 상태, 경쟁 조건, 및 기타 동기화 오류 등이 발생한다.
 +=== 나. 안전한 코딩기법 ===
 +J2EE에서는 스레드를 사용하는 것 대신에 병렬 실행을 위한 프레임워크를 사용해야 한다.
 +=== 다. 예제 ===
 +<code | 안전하지 않는 코드 예제>
 +1: public class U383 extends HttpServlet {
 +2: protected void doGet(HttpServletRequest request, HttpServletResponse response)
 +throws ServletException, IOException {
 +3: // Thread를 만들고 background에서 작업을 수행한다.
 +4: Runnable r = new Runnable() {
 +5: public void run() {
 +6: System.err.println("do something");
 +7: }
 +8: };
 +9: new Thread(r).start();
 +10: }
 +11: }
 +</code>
 +J2EE 프로그램에서 스레드를 직접 생성하여 사용하면, 교착 상태, 경쟁 조건, 및 기타 동기화 오류 등이 발생할 수 있다.
 +<code | 안전한 코드 예제>
 +1: public class S383 extends HttpServlet {
 +2: protected void doGet(HttpServletRequest request, HttpServletResponse response)
 +throws ServletException, IOException {
 +3: // 수행할 Thread에 대해서 일반 자바 클래스를 만든다.
 +4: // New MyClass().main();
 +5:
 +6: // 만약 async로 병렬작업을 하기 위해서는 JAVA Runtime을
 +7: // 사용하여 async로 통신하는 게 좋다.
 +8: Runtime.getRuntime().exec("java AsyncClass" );
 +9: }
 +10: }
 +11:
 +12: class AsyncClass {
 +13: public static void main(String args[]) {
 +14: // Process and store request statistics.
 +15: // ……
 +16: System.err.println("do something");
 +17: }
 +18: }
 +</code>
 +스레드를 직접 사용하는 것 대신에 해당 플랫폼에서 제공하는 병렬 실행을 위한 프레임워크를 사용한다.
 +=== 라. 참고 문헌 ===
 +[1] CWE-383 J2EE 잘못된 습관: 스레드의 직접 사용 - http://cwe.mitre.org/data/definitions/383.html
 +\\
 +[2] Java 2 Platform Enterprise Edition Specification, v1.4, Sun Microsystems
 +==== 5. 심볼릭명이 정확한 대상에 매핑되어 있지 않음(Symbolic Name not Mapping to Correct Object) ====
 +
 +=== 가. 정의 ===
 +심볼릭명을 사용하여 특정 대상을 지정하는 경우 공격자는 심볼릭명이 가리키는 대상을 작하여 프로그램이 원래 의도했던 동작을 못하게 할 수 있다.
 +=== 나. 안전한 코딩기법 ===
 +클래스 객체가 필요할 때에는 클래스 생성자를 표준적인 방법으로만 호출해야 한다.
 +=== 다. 예제 ===
 +<code | 안전하지 않는 코드 예제>
 +1: ……
 +2: public void f() throws ClassNotFoundException, InstantiationException,
 +IllegalAccessException {
 +3: // Class.forName으로 클래스를 생성하고 있다.
 +4: Class c = Class.forName("testbed.unsafe.U386.Add");
 +5: Object obj = (Add)c.newInstance();
 +6: Add add = (Add) obj;
 +7: System.out.println(add.add(3, 5)); // 34
 +8:
 +9: Object obj2 = (Add)Class.forName(" testbed.unsafe.Add" ).newInstance();
 +10: Add add2 = (Add) obj2;
 +11: System.out.println(add2.add(3, 5)); // 8
 +12: }
 +13:
 +14: class Add {
 +15: int add(int x, int y) {
 +16: return x + y;
 +17: }
 +18: }
 +19: }
 +20:
 +21: class Add {
 +22: int add(int x, int y) { return (x*x + y*y); }
 +23: }
 +</code>
 +java.lang.Class.forName()은 인수 문자열을 기반으로 해당 클래스를 반환(return)하지만, 문자열이 "이름"으로서 지시하는 클래스가 언제나 동일한 메소드를 구현하고 있음을 보장하지 못한다.
 +<code | 안전한 코드 예제>
 +1: ……
 +2: public void f() throws ClassNotFoundException,
 +3: InstantiationException, IllegalAccessException {
 +4: // 객체의 생성은 직접 생성자를 호출하여 생성한다.
 +5: testbed.safe.S386.Add add = new testbed.safe.S386.Add();
 +6: System.out.println(add.add(3, 5));
 +7: testbed.safe.Add add2 = new testbed.safe.Add();
 +8: System.out.println(add2.add(3, 5));
 +9: }
 +10:
 +11: class Add {
 +12: private int add(int x, int y) {
 +13: return x + y;
 +14: }
 +15: }
 +16: }
 +17:
 +18: class Add {
 +19: int add(int x, int y) { return (x*x + y*y); }
 +20: }
 +</code>
 +java.lang.Class.forName 대신, 각 클래스의 생성자를 제대로 호출하여 객체를 생성한다.
 +=== 라. 참고 문헌 ===
 +[1] CWE-386 심볼릭명이 정확한 대상에 매핑되어 있지 않음 - http://cwe.mitre.org/data/definitions/386.html
 +==== 6. 중복 검사된 잠금(Double-Checked Locking) ====
 +
 +=== 가. 정의 ===
 +중복 검사된 잠금(double-checked locking)은 프로그램의 효율성을 높이기 위해 사용하지만, 의도한 대로 동작하지 않는다.
 +\\
 +동기화 비용을 줄이기 위해, 프로그래머는 하나의 객체만 할당될 수 있도록 코드를 작성하지만, 자바에서는 객체 참조 주소를 할당하고 생성자를 호출하므로 의도한 객체가 완전하게 초기화되지 않은 상태에서 사용되는 경우가 발생할 수 있다.
 +=== 나. 안전한 코딩기법 ===
 +특정 리소스가 (비)할당되었을 경우만 작업을 수행하도록 두 번에 걸쳐 검사를 시도해도 원하는 자원이 (비)할당되었는지 보장이 불가능하다.
 +\\
 +중복 검사된 잠금에 대한 완벽한 보장을 원할 경우 메소드를 동기화해야 한다.
 +=== 다. 예제 ===
 +<code | 안전하지 않는 코드 예제>
 +1: ……
 +2: Helper helper;
 +3:
 +4: public Helper MakeHelper() {
 +5: // helper 객체의 null 체크에 대해서는 동기화가 되지 않는다.
 +6: if (helper == null) {
 +7: synchronized (this) {
 +8: if (helper == null) {
 +9: helper = new Helper();
 +10: }
 +11: }
 +12: }
 +13: return helper;
 +14: }
 +15:
 +16: class Helper {
 +17: ……
 +18: }
 +19: }
 +</code>
 +위 예제는 하나의 helper 객체만 할당될 수 있도록 코드가 작성되었다. 이는 불필요한 동기화를 피하면서 스레드 안전성을 보장하는 것처럼 보인다. 그러나 자바에서는 객체 참조주소를 할당하고 생성자를 호출하므로, helper 객체가 완전하게 초기화되지 않은 상태에서 사용되는 경우가 발생할 수 있다.
 +<code | 안전한 코드 예제>
 +1: ……
 +2: Helper helper;
 +3:
 +4: // 메소드 전체에 대해 동기화를 하도록 설정함.
 +5: public synchronized Helper MakeHelper() {
 +6: if (helper == null) {
 +7: helper = new Helper();
 +8: }
 +9: return helper;
 +10: }
 +11: }
 +12:
 +13: class Helper {
 +14: ……
 +15: }
 +</code>
 +중복 검사된 잠금에 대한 완벽한 보장을 원할 경우, 메소드 전체에 대해 동기화를 하도록 설정한다.
 +=== 라. 참고 문헌 ===
 +[1] CWE-609 중복 검사된 잠금 - http://cwe.mitre.org/data/definitions/609.html
 +\\
 +[2] David Bacon et al.. "The "Double-Checked Locking is Broken" Declaration".
 +\\
 +http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
 +==== 7. 제대로 제어되지 않은 재귀(Uncontrolled Recursion) ====
 +
 +=== 가. 정의 ===
 +재귀의 순환횟수를 제어하지 못하여 할당된 메모리나 프로그램 스택 등의 자원을 과다하게 사용하면 위험하다. 대부분의 경우, 귀납 조건(base case)이 없는 재귀는 무한 재귀에 빠진다.
 +=== 나. 안전한 코딩기법 ===
 +무한 재귀를 방지하기 위하여 모든 재귀 호출을 조건문 블럭이나 반복문 블럭 안에서만 수행해야 한다.
 +=== 다. 예제 ===
 +<code | 안전하지 않는 코드 예제>
 +1: ……
 +2: public int factorial(int n) {
 +3: // 재귀 호출이 조건문/반복문 블럭 외부에서 일어나면 대부분 무한 재귀를 유발한다.
 +4: return n * factorial(n - 1);
 +5: }
 +</code>
 +재귀적으로 정의되는 함수의 경우, 재귀 호출이 조건문/반복문 블럭 외부에서 일어나면 대부분 무한 재귀를 유발한다.
 +<code | 안전한 코드 예제>
 +1: ……
 +2: public int factorial(int n) {
 +3: int i;
 +4: // 모든 재귀 호출은 조건문이나 반복문 블럭 안에서 이루어져야한다.
 +5: if (n == 1) {
 +6: i = 1;
 +7: } else {
 +8: i = n * factorial(n - 1);
 +9: }
 +10: return i;
 +11: }
 +</code>
 +모든 재귀 호출은 조건문이나 반복문 블럭 안에서 이루어져야한다.
 +=== 라. 참고 문헌 ===
 +[1] CWE-674 제대로 제어되지 않은 재귀 - http://cwe.mitre.org/data/definitions/674.html