서버 동작 흐름 살펴 보기
사전 기반 지식
Dynamic Web Project - Java EE 스펙 중 Servlet, JSP 등 일부 기술을 활용해서 개발 해왔음(실제로 우리는 JAVA SE 버전을 사용)
웹 애플리케이션을 개발하기 위해 습득 내용
- Servlet - 클라이언트의 요청을 처리하고 응답을 생성하는 객체 만들기 위한 API(응용 프로그램 인터페이스)입니다.
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h2>Hello, Servlet!</h2>");
out.println("</body></html>");
}
}
- JSP (JavaServer Pages) - 동적 웹 페이지를 생성하는 기술입니다.
Servlet 이란?
서버에서 클라이언트의 요청을 처리하고 그 결과를 클라이언트에게 반환하는 역할을 합니다. Servlet은 HTTP 프로토콜에 따른 요청(GET, POST 등) 마다 별도의 메소드(doGet(), doPost() 등)가 있으며, 이들 메소드 내에서 비즈니스 로직을 구현합니다.
Servlet 객체는 HTTP 요청마다 새롭게 생성되는 것이 아니라, 처음 요청됐을 때 한 번만 생성되어 메모리에 올라가고 그 후 들어오는 동일한 경로의 HTTP 요청들은 이미 메모리에 올라간 해당 Servlet 객체를 재사용합니다. 이렇게 함으로써 성능 향상과 메모리 절약 등의 이점이 있습니다.
+------------------+
| Client |
+------------------+
| ^
v |
+------------------+
| Apache Web |
| Server |
+------------------+
| ^
v |
+------------------+ (동적 컨텐츠 생성시 동작)
| Tomcat | 서블릿 컨테이너의 생명주기 (WAS)
| (Servlet | 1.시작: 웹 서버(예: Tomcat)가 시작될 때, 서블릿 컨테이너도 함께 시작
| Container) | web.xml 파일을 읽어들여 초기화 작업을 수행
| | 2.운영: 서블릿 컨테이너는 웹 서버가 동작하는 동안 계속해서 클라이언트의
| *세션 스토리지* | 요청을 받아 처리합니다
| +------------+ | 클라이언트로부터 HTTP 요청을 받으면, 해당 요청에 맞는 서블릿
| | Filter | | 객체를 찾아 그 객체의 service() 메소드를 호출합니다.
| +------------+ | service() 메소드 내부에서는 HTTP 요청 방식(GET, POST 등)에 따라
| | 적절한 메소드(doGet(), doPost() 등)를 호출하여 실제 비즈니스
| | 로직을 처리하고 응답을 생성합니다.
| | 3.종료: 웹 서버가 종료되면, 그와 함께 연결된 모든 리소스와
| | 프로세스들도 종료
| *멀티스레딩* | destroy() 메소드를 호출하여 리소스 해제 및 종료 작업
| |
| | - 기본 동작 -
| | Servlet Life Cycle 존재
| | init() - Service() - HTTP의 메서드 유형(doGet, doPost) - destroy()
| | 서블릿 컨테이너가 시작될 때, 또는 최초의 요청이 들어올 때 설정에 따라
| | 서블릿 객체는 한 번만 생성됩니다
+------------------+
+------------------+
| Client |
+------------------+
|
v
+------------------+
| Apache Web |
| Server |
+------------------+
|
v (동적 컨텐츠 생성시 동작)
+------------------+
| Tomcat | WAS
| (Servlet |
| Container) |
| |
| *세션 스토리지* |
| +------------+ | - 스프링 컨테이너 기반 애플리케이션 -
| | Filter | | HttpServletRequest, HttpServletResponse 생성하고
| +------------+ | DispatcherServlet의 service 메서드가 호출됩니다
| |
| | * 동시에 여러 요청이 오면 서블릿 컨테이너의 멀티스레딩 능력 *
| | HTTP 요청을 처리하기 위해 스레드를 생성하거나 스레드 풀에서
| | 스레드를 할당하는 역할
| |
+-----------|------+
|
v
+----------------------------+ - **스프링 프레임워크의 핵심 부분 - IoC, DI, AOP ...**
| Spring | **Spring Container: 스프링 프레임워크의 핵심 부분으로 빈(Bean)의 생명**
| Container | **주기를 관리하며 스프링 기반 애플리케이션의 구동 환경입니다.**
| |
| +---------------------+ |
| | DispatcherServlet | |
| +---------------------+ |
| | |
| v |
| +------------------+ |
| | Interceptor | | *추상화된 API 와 인터페이스들을 통해 세션 데이터에 접근*
| +------------------+ |
| PreHandle |
| | |
| v |
| +------------------+ |
| | Controller | |
| +------------------+ |
| AOP | |
| Advice | |
| | |
| v |
| Interceptor |
| PostHandle |
| | |
| v |
| Interceptor |
| AfterCompletion |
| |
| |
+-----------|----------------+
|
v
+------------------+
| Tomcat |
| (Servlet |
| Container) |
| |
| |
| +------------+ |
| | Filter | |
| +------------+ |
| | |
| v |
+------------------+
|
v
+------------------+
| Response |
+------------------+
|
v
+------------------+
| Apache Web |
| Server |
+------------------+
|
v
+------------------+
| Client |
+------------------+
DispatcherServlet은 스프링 MVC 프레임워크의 핵심 구성 요소
스프링 부트 웹 애플리케이션은 내장형 서블릿 컨테이너를 사용합니다.
스프링 부트 애플리케이션 시작 시, 내장형 서블릿 컨테이너는 DispatcherServlet의 단일
인스턴스를 생성합니다. 이 DispatcherServlet 인스턴스는 웹 애플리케이션의
모든 HTTP 요청을 처리하게 됩니다.
DispatcherServlet은 웹 애플리케이션 컨텍스트(WebApplicationContext)와 연결되어
있습니다. 웹 애플리케이션 컨텍스트는 웹 관련 설정과 빈들을 포함합니다.
예를 들어, 웹 요청을 처리하는 컨트롤러(Controller), 서비스(Service), 데이터베이스
상호작용 등과 관련된 빈들이 여기에 포함될 수 있습니다.
따라서, HTTP 요청이 들어오면, DispatcherServlet은 웹 애플리케이션 컨텍스트에서 적절한
빈을 찾아 해당 요청을 처리합니다. 따라서 DispatcherServlet은 웹 요청의 시작점과 끝점,
즉 요청의 수신부터 응답의 전송까지 전체 웹 요청/응답 생명주기를 관리하는 중심적인 역할
을 합니다.
💡 스프링 부트가 시작될 때 주요하게 로드되는 컨텍스트(Context)에 대해 알아 보자.
[[ ApplicationContext 와 WebApplicationContext ]]
스프링 부트 애플리케이션이 시작될 때, ApplicationContext(Root ApplicationContext)가 메모리에 먼저 로드됩니다. 이는 데이터베이스 연결과 같은 핵심 서비스를 구성합니다. 그 다음, 웹 관련 빈들을 위한 WebApplicationContext가 생성됩니다. WebApplicationContext는 DispatcherServlet에 연결되며 웹 관련 설정을 관리합니다. WebApplicationContext는 ApplicationContext의 자식 컨텍스트로서, 웹 관련 빈이 정의됩니다. 만약 해당 빈을 찾지 못하면 부모 컨텍스트에서 검색합니다.
- Client: 사용자가 웹 브라우저나 모바일 앱 등 클라이언트를 통해 서버에 HTTP 요청을 보냅니다.
- Apache Web Server: 클라이언트로부터 받은 요청을 처리합니다. 정적 컨텐츠(예: HTML, CSS, JavaScript 파일 등) 요청의 경우 직접 응답을 제공하고, 동적 컨텐츠를 생성해야 하는 요청의 경우 백엔드 애플리케이션 서버(Tomcat)로 전달합니다.
- Tomcat (Servlet Container): 웹 서버로부터 받은 요청을 적절한 서블릿(DispatcherServlet)에게 전달합니다. 이 과정에서 필요한 필터(Filter)를 거치게 됩니다.
- Filter (Tomcat): HTTP 요청이나 응답에 대한 전/후 처리를 담당합니다. 예를 들어 인코딩 설정, CORS 설정 등과 같은 작업을 수행할 수 있습니다.
- Spring Container: 스프링 프레임워크의 핵심 부분으로 빈(Bean)의 생명 주기를 관리하며 스프링 기반 애플리케이션의 구동 환경입니다.
- DispatcherServlet (Spring Container): 들어온 요청을 적절한 컨트롤러 메소드에 매핑하고 그 결과를 다시 클라이언트에게 응답으로 반환하는 역할을 합니다.
- Interceptor (Spring Container): 컨트롤러가 요청을 처리하기 전과 후에 추가 작업(인증, 로깅 등)을 수행합니다.
- Controller (Spring Container): 실제 비즈니스 로직이 구현되어 있으며 클라이언트의 요청에 따른 응답 생성 코드가 포함되어 있습니다.
💡 CORS란?
CORS(Cross-Origin Resource Sharing)는 다른 출처(origin)에서 실행되는 웹 페이지로부터 요청된 리소스에 접근할 수 있게 허용하는 방법을 정의하는 웹 브라우저 기술입니다. 웹 페이지가 다른 도메인의 리소스에 접근하려고 할 때, 보안상의 이유로 브라우저는 동일 출처 정책(Same-Origin Policy)을 강제합니다. 이 정책은 스크립트 내에서 다른 출처의 리소스에 접근하는 것을 제한합니다.
예를 들어, domainA.com에서 로드된 웹 페이지가 domainB.com의 API에 AJAX 요청을 보내려고 하면 동일 출처 정책으로 인해 요청이 차단됩니다. 그러나 domainB.com의 서버가 적절한 CORS 헤더를 응답에 포함시키면 브라우저는 이 요청을 허용합니다.
Spring Boot에서는 @CrossOrigin 어노테이션을 사용하여 CORS를 허용할 수 있습니다.
// Controller 클래스
@RestController
@RequestMapping("/api")
public class MyController {
@CrossOrigin(origins = "<http://example.com>")
@GetMapping("/data")
public String getData() {
return "Hello, CORS!";
}
}
전역 설정
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("<http://example.com>")
.allowedMethods("GET", "POST", "PUT", "DELETE");
}
};
}
}
CSRF 와 XSS 의 차이
결론부터 말하자면,
XSS는 공격대상이 Client이고, CSRF는 Server이다.
XSS은 사용자가 특정 웹사이트를 신용하는 점을 노린 것이라면,
CSRF는 특정 웹사이트가 사용자의 웹 브라우저를 신용하는 상태를 노린 것이다.
따라서,
XSS는 사이트변조나 백도어를 통해 클라이언트에 대한 악성공격을 한다.
CSRF는 요청을 위조하여 사용자의 권한을 이용해 서버에 대한 악성공격을 한다.
XSS(사이트 간 스크립팅 : Cross-site Scripting)
웹사이트 관리자가 아닌 이가 웹 페이지에 악성 스크립트를 삽입할 수 있는 취약점으로 발생되는 공격이다.
- 웹 애플리케이션이 사용자로부터 입력 받은 값을 제대로 검사하지 않고 사용할 경우 나타난다.
- 주로 여러 사용자가 보게 되는 전자 게시판에 악성 스크립트가 담긴 글을 올리는 형태로 이루어진다.
- 이 취약점으로 해커가 사용자의 정보(쿠키, 세션 등)를 탈취하거나, 자동으로 비정상적인 기능을 수행하게 하거나 할 수 있다.
- 주로 다른 웹사이트와 정보를 교환하는 식으로 작동한다.
대응방안
- 필터 제작
- XSS 공격은 입력값에 대한 검증이 제대로 이루어지지 않아 발생하는 취약점이다. 따라서 모든 입력값에 대해 필터링해야 한다.
- script문장에 존재하는 특수문자를 메타캐릭터로 변환시킨다.
< ---- <
> ---- >
( ---- (
) ---- )
# ---- #
& ---- &
- BBCode 사용
- 따로 필터를 만들기 어려운 경우 사용할 수 있다. 만들기 편하고, 안정성도 높은 편이다.
- 쿠키의 보안 옵션 사용
- 쿠키 생성시 '보안 쿠키'라는 파라미터를 지정하면 TLS 상에서만 사용하게 할 수 있다.
- 콘텐츠 보안 정책(CSP) 사용
- 스크립트 실행에 대한 정책(조건)을 설정해 예방하는 방법
- 출처가 자기 서버인 스크립트만 실행될 수 있도록 한다.
CSRF(Cross-site Request Forgery)
사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하게 하는 공격을 말한다.
- 일단 사용자가 웹사이트에 로그인한 상태에서 사이트간 요청 위조 공격 코드가 삽입된 페이지를 열면,
공격 대상이 되는 웹사이트는 위조된 공격 명령이 믿을 수 있는 사용자로부터 발송된 것으로 판단하게 되어 공격에 노출된다.
대응 방안
- CSRF 토큰 사용
- 재인증 요구