이전 글들에서는 프로젝트 개요와 저의 느낀 점을
정리해 보았습니다.
이번에 개발을 진행하면서 간단한 라이브러리 사용으로도
사용자 입력란을 사용한 SQL 쿼리 공격과 같은
보안적인 측면까지 생각해야 한다는 것을 배웠습니다.
그래서 이번 프로젝트 진행하면서 새롭게 익힌 코드에
대해서 정리해보려고 합니다.
먼저 이번 프로젝트에서는 JDBC를 활용하여
Java 프로그램에서 프로젝트를 데이터베이스에 연결하고
SQL을 실행하였습니다.
JDBC (Java Database Connectivity)
JDBC(Java Database Connectivity)는 Java 프로그램에서
데이터베이스 연결, SQL 실행, 그리고 결과를 처리할 수
있게 해주는 라이브러리입니다.
MySQL, Oracle 등과 같은 다양한 Database에
Java코드로 접속하여 SELECT, INSERT 같은
SQL 코드를 실행하고 그 결과를 받아오는 기능을
제공해 줍니다.
JDBC를 통해서 구현가능한 기능들을
정리해 보겠습니다.
1. DB 연결 (Connection)
2. SQL 실행 (Statement, PreparedStatement)
3. 결과 조회 (ResultSet)
4. 트랜잭션 제어 (commit, rollback)
5. 연결 종료 (close)
위 기능들을 실행하기 위해 java.sql 패키지에서
제공하는 주요 클래스와 인터페이스들을 살펴보겠습니다.
1. DriverManager - DB 연결 관리
2. Connection - DB 연결 객체
3. Statement - 일반 SQL 실행용 객체
4. PreparedStatement - 파라미터를 지원하는 SQL 실행 객체
5. ResultSet - SQL 결과 조회용 객체
기본적으로 먼저 Database를 연결하고
Connection의 인스턴스를 활용하여 SQL 쿼리를 실행하고
ResultSet으로 결괏값을 가져오는 방식으로 작동합니다.
작동 예시들은 다음과 같습니다.
- Statement
// url: 자신의 데이터베이스 url
Connection conn = DriverManager.getConnection(url);
// username과 password를 설정해둔 경우 아래 코드 사용
// Connection conn = DriverManager.getConnection(url, user, pass);
Statement st = conn.createStatement();
String query = "SELECT * FROM User";
ResultSet rs = st.executeQuery(query);
- PreparedStatement
Connection conn = DriverManager.getConnection(url);
PreparedStatement pst = conn.prepareStatement("SELECT * FROM User WHERE id = ?");
pst.setInt(1, 123);
ResultSet rs = pst.executeQuery();
그럼 다음으로는 Statement와 PreparedStatement에
대해서 알아보겠습니다.
Prepared Statement
Prepared Statement는 Java의 SQL 패키지 속에
정의된 인터페이스(interface)입니다.
인터페이스로 정의되어 있는 이유는
각 데이터베이스의 드라이버가 실제 구현을
담당하도록 설계하여 다양한 데이터베이스와
연결을 가능하게 하기 위해서입니다.
PreparedStatement는 SQL 쿼리를 미리 준비해 두고
실행할 때 값만 바꿔서 재사용할 수 있도록 도와주는
기능입니다.
이때 쿼리 구조와 값을 분리해서 처리하기 때문에
입력값에 악의적인 SQL 코드가 섞여 있어도
실행되지 않습니다.
PrepareStatement를 사용하면 다음과 같은
SQL Injection을 방어하는데 장점이 있습니다.
- 사용자 로그인 우회
사용자 입력
username -> admin
password -> ' OR '1' ='1'
위와 같이 유저가 입력한다면
아래와 같은 SQL이 실행되면서
SELECT * FROM User WHERE username = 'admin' AND password = '' OR '1'='1';
모든 사용자 로그인을 우회할 수 있습니다.
- 테이블 삭제 시도
사용자 입력
username 항목
admin'; DROP TABLE User; --
실행되는 SQL
SELECT * FROM User WHERE username = 'admin'; DROP TABLE User; --';
위와 같은 SQL이 실행되면
세미콜론 뒤의 쿼리까지 실행되어
테이블이 삭제됩니다.
- 주석으로 조건 무력화시키기
사용자 입력
username: admin' --
실행되는 쿼리
SELECT * FROM User WHERE username = 'admin' -- ' AND password = 'abcd';
-- 이후로는 주석처리가 되어
비밀번호 조건이 무시됩니다!
- 조건 조작을 통해 사용자 데이터 조회
사용자 입력
ID : 1 OR 1=1
실행되는 쿼리
SELECT * FROM User WHERE id = 1 OR 1=1;
이렇게 조건을 조작하여
모든 사용자의 데이터를 조회할 가능성이
존재합니다.
PrepareStatement는 위와 같은 SQL 공격을
방어하는 역할에도 탁월하지만 성능 향상에도
이점이 있습니다.
같은 쿼리를 반복해서 실행하는 경우에 매번 파싱을
하는 Statement와 다르게 미리 컴파일이 되어있기
때문에 실행속도가 더 빠릅니다.
하지만 PreparedStatement에도 단점이 존재합니다.
바로 값만 바꿀 수 있고 칼럼명과 같은 쿼리 구조를
동적으로 바꾸는 것은 불가능하다는 점입니다.
따라서 동적으로 바꾸고 싶은 상황에서는
쿼리 구조는 문자열로 직접 조합하고
값만 PreparedStatement로 처리해야 합니다.
// 만약에 사용자가 선택한 것이 color항목이라고
// 가정한다면
String section = "color";
String color = "Red";
String sql = "SELECT * FROM User WHERE " + section + " = ?";
PreparedStatement pst = conn.prepareStatement(sql);
pst.setString(1, color);
// 위와 같은 방법으로 PreparedStatement를
// 사용하면서 쿼리 구조를
// 변경할 수 있습니다.
이렇게 PreparedStatement에 대해서 알아보았습니다.
PreparedStatement는 성능 향상에도 도움이 되고
보안 측면에서도 이점이 있는 유용한 기능이라고
생각합니다.
코드를 작성할 때 항상 SQL Injection과 같은 보안적인
측면과 공격의 가능성을 염두해둬야 한다는 것을
깨닫게 되었습니다.
이를 통해 앞으로 Database와 관련된 프로젝트를
진행할 때 많은 도움이 될 것 같습니다!
'웹 개발 프로젝트 리뷰 (Web Design)' 카테고리의 다른 글
웹 개발 프로젝트 리뷰 (5) - JSP & Servlet (0) | 2025.06.29 |
---|---|
웹 개발 프로젝트 리뷰 (4) - 세션 (Session) (0) | 2025.06.28 |
웹 개발 프로젝트 리뷰 (2) - 개인 기능 구현 (2) | 2025.06.26 |
웹 개발 프로젝트 리뷰 (1) - 프로젝트 개요 (4) | 2025.06.25 |