Notice
Recent Posts
Recent Comments
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
07-27 15:49
Today
Total
관리 메뉴

해킹공주의 일상

다중 스레드 환경에서의 멤버변수의 위험성 | 06.01. 잘못된 세션에 의한 데이터 정보노출 본문

소스코드 진단

다중 스레드 환경에서의 멤버변수의 위험성 | 06.01. 잘못된 세션에 의한 데이터 정보노출

h4ckpr1n 2024. 4. 18. 17:13

개요

"잘못된 세션에 의한 데이터 정보노출'" 항목에 대해 정리해보고자 한다.

 

'잘못된 세션에 의한 데이터 정보노출' 이란

다중 스레드 환경에서 싱글톤 객체 필드의 경쟁 조건(Race Condiion)이 발생할 수 있다. 따라서 다중 스레드 환경인 Java의 서블릿 등에서는 정보를 저장하는 멤버 변수가 포함되지 않도록 하여 서로 다른 세션에서 데이터를 공유하지 않도록 해야한다.

 

일단 무슨말인지 이해하기 위해서 단어 하나하나 이해해보자.

 

다중 스레드 환경

다중 스레드 환경이란 말그대로 한번에 여러개의 스레드가 작동하는 환경을 말한다. 아래와 같이 하나의 과자공장에서 여러개의 과자를 만드는걸 의미한다.

 

다중 스레드 환경에서는 각 스레드가 공유된 자원(예: 메모리, 파일, 네트워크 연결 등)에 동시에 접근할 수 있다. 이로 인해 경쟁 조건(race condition)이 발생할 수 있으며, 이는 잘못된 동작이나 예기치 않은 결과를 초래할 수 있다. 이러한 이유로 다중 스레드 환경에서는 동기화(synchronization)가 필요하다. 

 

아래는 다중 스레드 환경의 예시이며, 공유자원 sharedResource 에 대해서 안전하게 동기화 하고 있다.

public class MultiThreadExample {
    // 공유 자원
    private static int sharedResource = 0;

    public static void main(String[] args) {
        // 첫 번째 스레드 생성
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                incrementSharedResource();
            }
        });

        // 두 번째 스레드 생성
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                incrementSharedResource();
            }
        });

        // 두 스레드 시작
        thread1.start();
        thread2.start();

        // 두 스레드가 종료될 때까지 대기
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 최종 공유 자원 값 출력
        System.out.println("Final shared resource value: " + sharedResource);
    }

    // 공유 자원을 안전하게 증가시키는 메서드 (동기화가 필요함)
    private static synchronized void incrementSharedResource() {
        sharedResource++;
    }
}

 

싱글톤 객체 필드

클래스의 인스턴스를 지칭하는 것으로, 특정 클래스의 객체가 싱글톤 패턴으로 생성되는 것을 의미한다. 여기서 싱글톤 패턴이라함은 단일한 객체를 참조하는 변수나 필드를 의미한다. 이 필드는 주로 정적(static)으로 선언되며, 클래스의 다른 메서드들이 이 객체를 공유하고 사용할 수 있도록 한다. 즉, 정적으로 선언된 필드는 싱글톤 객체 필드라고 할 수 있는 것이다.

 

서블릿 환경

서블릿(Servlet)은 자바 웹 애플리케이션 개발에서 사용되는 서버 측 프로그래밍 기술 중 하나다. 서블릿은 동적인 웹 페이지를 생성하거나, HTTP 요청에 대한 처리를 수행하며, 데이터베이스와의 상호 작용 등을 담당한다.

서블릿은 Java EE(Java Platform, Enterprise Edition) 스펙의 일부로서, HTTP 프로토콜을 기반으로 웹 애플리케이션 서버에서 실행된다. 클라이언트의 HTTP 요청에 따라 서블릿 컨테이너가 서블릿을 인스턴스화하고 실행하여 요청을 처리하고 응답을 생성한다

즉, 서블릿 환경은 웹페이지 동적으로 생성 + HTTP 요청수행 + 상호작용 등을 한번에 수행함 => 여러가지 일을 한번에 하는 다중 스레드 환경 라고 이해할 수 있겠다.

 

서블릿 환경 예시

1. HttpServlet을 종속하는 JAVA 환경

2. JSP 환경

JSP는 왜 다중 스레드 환경인가.
JSP(Java Server Pages)는 일반적으로 다중 스레드 환경에서 실행된다. 하지만 JSP 자체가 다중 스레드 환경인 것은 아니며, JSP가 실행되는 서블릿 컨테이너(예: Apache Tomcat, Jetty 등)가 다중 스레드 환경에서 JSP를 처리한다.
서블릿 컨테이너는 클라이언트의 요청에 대해 여러 스레드를 생성하여 JSP 페이지를 처리한다. 이렇게 생성된 각 스레드는 JSP 페이지의 인스턴스를 공유하고, 요청에 따라 해당 JSP 페이지를 실행한다. 따라서 동시에 여러 클라이언트 요청이 동일한 JSP 페이지에 접근할 수 있다.

 

그래서 저 항목이 무슨 내용인데

 

다시 돌아가서 보자면, 

 

다중 스레드 환경에서 싱글톤 객체 필드의 경쟁 조건(Race Condiion)이 발생할 수 있다.

> 여러 스레드가 한번에 실행되는 환경에서 static 변수로 전역변수를 여기저기 스레드에서 가져다 쓰게되면 경쟁 조건이 발생하여 예기치 않은 문제가 발생할 수 있다.

 

따라서 다중 스레드 환경인 Java의 서블릿 등에서는 정보를 저장하는 멤버 변수가 포함되지 않도록 하여 서로 다른 세션에서 데이터를 공유하지 않도록 해야한다.

> 따라서 다중 스레드 환경인 Java의 서블릿 등에서는 전역변수를 사용하지 말고 필요한 경우 지역변수를 선언하여 사용하거나, final 한정자를 추가하여 사용하도록 한다.

 

static & final
1. static
 - static 키워드는 변수, 메서드 또는 블록을 정적(static)으로 정의 할때 사용한다.
 - 클래스 수준에 속하는 변수나 메서드에 static을 사용하면, 해당 멤버는 클래스의 모든 인스턴스에 대해 공유된다. 즉, 객체를 생성하지 않고도 클래스 이름으로 바로 접근할 수 있다.
2. final
 - final 키워드는 변수, 메서드 또는 클래스를 불변(immutable)하게 만드는 한정자.
 - final 변수는 한 번 초기화되면 값을 변경할 수 없다. 즉, 상수(constant)로 사용된다.
- fiinal 클래스는 상속될 수 없다. 즉, 다른 클래스가 이 클래스를 확장할 수 없다.

 

보안 대책 예시

1. JSP 환경

안전하지않은 코드

JSP 선언부(<%! 소스코드 %>)에 선언한 변수는 해당 JSP에 접근하는 모든 사용자에게 공유된다.(전역변수로 발동) 먼저 호출한 사용자가 값을 설정하고 사용하기 전에 다른 사용자의 호출이 발생하게 되면, 뒤에 호출한 사용자가 설정한 값이 모든 사용자에게 적용되게 된다.

<%@page import="javax.xml.namespace.*"%>
<%@page import="gov.mogaha.ntis.web.frs.gis.cmm.util.*" %>
<%!
// JSP에서 String 필드들이 멤버 변수로 선언됨
String username = "/";
String imagePath = commonPath + "img/";
String imagePath_gis = imagePath + "gis/cmm/btn/";
……
%>

 

안전한 코드

JSP의 스크립트릿(<% 소스코드 %>)에 정의한 변수는 _jspService 메소드의 지역변수로 선언되므로 공유가 발생하지 않아 안전하다.

<%@page import="javax.xml.namespace.*"%>
 <%@page import="gov.mogaha.ntis.web.frs.gis.cmm.util.*" %>
 <%
 // JSP에서 String 필드들이 로컬 변수로 선언됨
 String commonPath = "/";
 String imagePath = commonPath + "img/";
 String imagePath_gis = imagePath + "gis/cmm/btn/";
 ……
 %>

 

개인적인 의견이지만, 전역변수로 사용되어야하는 변수도 있기 때문에 불필요하게 전역변수로 선언부에 선언되어있는게 아니라면 양호처리해도 괜찮다는 의견이다.

 

2. JAVA 서블릿 환경

안전하지 않은 코드

HttpServlet에서 final로 선언되지 않은 static 전역 변수를 사용하는 간단한 예시 코드이다. 이 코드는 다중 스레드 환경에서 실행될 때 동기화 문제를 발생시킬 수 있다.

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyServlet extends HttpServlet {
    // 다중 스레드 환경에서 사용되는 static 전역 변수
    private static int count = 0;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 각 요청이 들어올 때마다 static 변수를 증가시킴
        count++;
        response.getWriter().println("현재 방문자 수: " + count);
    }
}

 

안전한 코드

아래와 같이 final 한정자를 사용해서 공유 자원을 상수화 시켜서 사용하면 안전하다. 

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

public class MyServlet extends HttpServlet {
    // AtomicInteger를 사용하여 다중 스레드 환경에서 안전하게 방문자 수를 추적
    private static final AtomicInteger count = new AtomicInteger(0);

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // AtomicInteger의 incrementAndGet 메서드를 사용하여 안전하게 증가시킴
        int visitorCount = count.incrementAndGet();
        response.getWriter().println("현재 방문자 수: " + visitorCount);
    }
}
Comments