오늘 공부한 내용
<h5>오늘 공부한 내용:</h5>
<ul>
<li>
<pre>
{% set html_content %}
## 🛡️ XSS 공격 및 소스코드 취약점 분석 요약
---
## 1. Chapter 05: XSS 및 CSRF 공격
### A. 쿠키(Cookie)와 사용자 인증
* **개념**: 웹 서버가 사용자의 브라우저에 저장하는 4KB 이하의 텍스트 파일입니다.
* **용도**: 세션 유지(로그인 상태), 개인화 설정, 장바구니, 이용 행태 추적 등.
* **보안 중요성**: 쿠키에 세션 ID나 민감 정보가 담기므로, 공격자가 이를 탈취하면 사용자 권한을 도용할 수 있습니다.
### B. XSS (Cross-Site Scripting)
공격자가 웹 페이지에 **악성 스크립트를 삽입**하여, 해당 페이지를 열람하는 사용자의 브라우저에서 스크립트가 실행되게 하는 공격입니다.
| 유형 | 설명 | 주요 특징 |
| --- | --- | --- |
| **Stored XSS** | 게시판, 프로필 등 **DB에 스크립트가 저장**됨 | 게시물 열람 시 모든 사용자에게 영향 (파괴력 큼) |
| **Reflected XSS** | **URL 파라미터** 등에 포함된 스크립트가 즉시 반사됨 | 악성 링크 클릭을 유도해야 함 (1회성) |
### C. CSRF (Cross-Site Request Forgery)
사용자가 자신의 의지와 무관하게 **공격자가 의도한 행위(비밀번호 변경, 송금 등)**를 특정 웹사이트에 요청하게 만드는 공격입니다.
* **차이점**: XSS는 정보를 '탈취'하는 데 목적이 있다면, CSRF는 특정 '동작'을 수행하게 하는 데 목적이 있습니다.
---
## 2. Chapter 06: 소스코드 취약점 분석
애플리케이션의 보안성을 확보하기 위해 코드를 직접 분석하여 잠재적 취약점을 찾아내는 과정입니다.
### A. 분석 방법론
1. **Black Box Testing**: 소스코드 없이 외부 인터페이스를 통해 공격 시도 (해커의 관점).
2. **White Box Testing**: 소스코드 내부 로직을 직접 분석 (개발자/보안 진단원 관점).
3. **Gray Box Testing**: 외부 취약점 점검과 코드 분석을 혼합하여 효율성 극대화.
### B. 주요 취약점 분석 사례
* **입력값 검증 미비**: SQL 인젝션, XSS, 파일 업로드 취약점 등 대부분의 보안 사고 원인입니다. `getRawParameter`와 같이 필터링 없이 값을 받는 함수 사용을 주의해야 합니다.
* **세션 처리 및 접근 통제**: 쿠키 값(`getCookies`)만으로 권한을 판단하거나, 관리자 페이지에 세션 체크 코드가 누락된 경우(**강제 브라우징**) 발생합니다.
* **중요 정보 노출**:
* **하드 코딩**: 패스워드나 DB 접속 정보를 소스코드에 직접 기입하는 행위.
* **평문 전송**: HTTPS(SSL) 없이 데이터를 전송하여 스니핑에 노출됨.
* **주석 처리**: 개발 시 남겨둔 아이디/패스워드가 주석(`//`, `/* */`)에 남아있는 경우.
### C. 언어별 주요 보안 함수 (Java 기준)
* **입력값 처리**: `getParameter()`, `getQueryString()` 등.
* **세션/쿠키**: `getCookies()`, `getSession()` 등.
* **DB 연결**: `DriverManager.getConnection()` (하드코딩 주의).
---
<12. 22일>
(교제 외) command injection을 해보자.
deepseek.com 을 이용 : chatgpt 등은 보안 취약성 등에 대한 답을 안전상 이유로 잘 해주지 않음
질문 1 : 우분투 jsp 서버에서 ip를 입력받아, 목적지까지 ping 시스템콜을 호출하고, 그 결과를 웹페이지로 실시간 출력하는 jsp를 하나의 파일로 간단하게 작성하시오. 단, 보안에 취약하여 command injection이 발생 가능하도록 작성하시오.
질문 2: 위의 코드에서 스타일등 필수기능이 아닌 부분은 제외하고, 최대한 간단하게 작성하라. count도 기본으로 5번 수행하도록 하고, 입력은 IP만 받도록 하자.
보안상 이유로 톰캣9 까지만 아래 코드로 동작
<%@ page import="java.io.*" %>
<%
String ip = request.getParameter("ip");
if (ip != null && !ip.trim().isEmpty()) {
String[] command = {"/bin/bash", "-c", "ping -c 5 " + ip};
Process p = Runtime.getRuntime().exec(command);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
String s;
while ((s = stdInput.readLine()) != null) {
out.println(s + "<br>");
}
return;
}
%>
<html>
<body>
<form method="post">
IP: <input type="text" name="ip">
<input type="submit" value="Ping">
</form>
</body>
</html>
<경로 : /var/lib/tomcat10/webapps/ROOT/ping.jsp>
Tomcat10 보안 설정 관련
sudo setcap cap_net_raw+ep /usr/bin/ping
vi /usr/lib/systemd/system/tomcat10.service
[Service]
AmbientCapabilities=CAP_NET_RAW
NoNewPrivileges=false
sudo systemctl daemon-reexec
sudo systemctl restart tomcat10
** 소스코드 보안 관리 (jsp 기준)
: 소스코드의 제일 상위 디렉토리에서 시스템콜을 바로 호출하는 함수를 찾아서, 보안상 취약점을 검사 한다.
grep "getRuntime().exec" -r /var/lib/tomcat10/webapps/ROOT
XSS 취약성이 존재하는 게시판 만들기
deepseek.com
질문1 : jsp코드를 이용하여 xss공격이 가능한 익명게시판을 만들어라. 게시판은 글쓰기(이름, 제목, 내용), 목록(제목), 읽기 기능을 제공한다.
질문 2: java코드를 사용하지 않고, jsp만으로 코드를 최대한 간단하게 작성하시오. 스타일등은 생략하시오. 데이터베이스 생성부터 만드시오. 데이터베이스는 1.1.1.1의 mydb 계정을 사용하는 mariadb 입니다.
-- 데이터베이스 생성
CREATE DATABASE IF NOT EXISTS C;
USE cloud_db;
-- 게시판 테이블 생성
CREATE TABLE IF NOT EXISTS board (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
title VARCHAR(200) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
<mysql -u mydb -p -h 192.xxx.xxx.xxx>
초기 설정 파일 (config.jsp)
<%-- config.jsp --%>
<%@ page import="java.sql.*" %>
<%
// 데이터베이스 연결 정보
String dbUrl = "jdbc:mariadb://192.168.186.130:3306/cloud_db";
String dbUser = "mydb";
String dbPass = "abcd1234";
// 데이터베이스 드라이버 로드
Class.forName("org.mariadb.jdbc.Driver");
// 데이터베이스 연결 함수
public Connection getConnection() throws SQLException {
return DriverManager.getConnection(dbUrl, dbUser, dbPass);
}
%>
메인 페이지 (index.jsp)
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ page import="java.sql.*" %>
<!DOCTYPE html>
<html>
<head>
<title>XSS 게시판</title>
</head>
<body>
<h1>익명 게시판</h1>
<a href="write.jsp">글쓰기</a>
<hr>
<table border="1" width="100%">
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>날짜</th>
</tr>
<%
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("org.mariadb.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mariadb://192.168.186.130:3306/cloud_db", "mydb", "abcd1234");
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM board ORDER BY id DESC");
while(rs.next()) {
%>
<tr>
<td><%= rs.getInt("id") %></td>
<td><a href="read.jsp?id=<%= rs.getInt("id") %>">
<%= rs.getString("title") %></a></td>
<td><%= rs.getString("name") %></td>
<td><%= rs.getString("created_at") %></td>
</tr>
<%
}
} catch(Exception e) {
out.println("에러: " + e.getMessage());
} finally {
if(rs != null) try { rs.close(); } catch(Exception e) {}
if(stmt != null) try { stmt.close(); } catch(Exception e) {}
if(conn != null) try { conn.close(); } catch(Exception e) {}
}
%>
</table>
</body>
</html>
글쓰기 페이지 (write.jsp)
<%@ page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>글쓰기</title>
</head>
<body>
<h1>글쓰기</h1>
<form action="save.jsp" method="post">
이름: <input type="text" name="name" value="익명"><br><br>
제목: <input type="text" name="title" size="50"><br><br>
내용: <br>
<textarea name="content" rows="10" cols="50"></textarea><br><br>
<input type="submit" value="저장">
<a href="index.jsp">취소</a>
</form>
</body>
</html>
저장 처리 (save.jsp)
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ page import="java.sql.*" %>
<%
request.setCharacterEncoding("UTF-8");
String name = request.getParameter("name");
String title = request.getParameter("title");
String content = request.getParameter("content");
if(name == null || name.trim().equals("")) {
name = "익명";
}
Connection conn = null;
PreparedStatement pstmt = null;
try {
Class.forName("org.mariadb.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mariadb://192.168.186.130:3306/cloud_db", "mydb", "abcd1234");
String sql = "INSERT INTO board (name, title, content) VALUES (?, ?, ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
pstmt.setString(2, title);
pstmt.setString(3, content);
pstmt.executeUpdate();
response.sendRedirect("index.jsp");
} catch(Exception e) {
out.println("저장 실패: " + e.getMessage());
} finally {
if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}
if(conn != null) try { conn.close(); } catch(Exception e) {}
}
%>
글 읽기 페이지 (read.jsp)
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ page import="java.sql.*" %>
<%
String id = request.getParameter("id");
if(id == null) {
response.sendRedirect("index.jsp");
return;
}
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String name = "";
String title = "";
String content = "";
String date = "";
try {
Class.forName("org.mariadb.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mariadb://192.168.186.130:3306/cloud_db", "mydb", "abcd1234");
String sql = "SELECT * FROM board WHERE id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, Integer.parseInt(id));
rs = pstmt.executeQuery();
if(rs.next()) {
name = rs.getString("name");
title = rs.getString("title");
content = rs.getString("content");
date = rs.getString("created_at");
}
} catch(Exception e) {
out.println("에러: " + e.getMessage());
}
%>
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h1><%= title %></h1>
<div>작성자: <%= name %>