목차
- XML
1-1. storeToXML
1-2. loadFromXML - SQL Injection
2-1. Statement의 경우
2-2. PreparedStatement의 경우
1. XML
SQL 구문 작성은 길고 복잡해지게 마련이다. 일반 코드에서 쿼리문의 띄어쓰기를 구현하거나 하나의 문장으로 늘여 작성하는 것도 쉽지만은 않다. 이러한 한계를 고려해 별도 .xml 파일에 쿼리문을 저장하고 관리할 수 있다.
1-1. storeToXML
- 문서 타입 정의(DTD, Document Type Definition)로 생성된다.
- 상단의 DOCTYPE이 선언돼 있어야 실제 사용이 가능하다.
- <properties></properties>로 감싸져 있는 모양새를 가진다.
- entry는 key + value를 뜻한다.
- XML 파일에서 주석은 다음과 같이 작성한다: <!-- 텍스트 -->
-- .xml 생성 main 메소드 안에서
Properties prop = new Properties();
prop.setProperty("keyString", "valueString");
- 프로퍼티(Properties) 객체를 선언하고, setProperty() 메소드에 임시 key, value를 전달한다.
try {
prop.storeToXML(new FileOutputStream("src/com/reminder/preparedstatement/employee-query.xml"), "");
- storeToXML() 메소드 인자로 파일 경로 포함 OutputStream 및 주석 내용을 전달한다: public void storeToXML(OutputStream os, String comment)
- 이때 주석(comment)은 필요치 않은 경우 null로 두어도 된다. 두 번째 인자로서 값을 전달하면 위처럼 파일에 명시된다.
} catch (IOException e) {
e.printStackTrace();
}
- FileOutputStream 사용 시
IOException에 대해 catch블럭으로 처리해 주어야 한다.
-- menu-query.xml INSERT/UPDATE/DELETE
1-2. loadFromXML
try {
prop.loadFromXML(new FileInputStream("mapper/menu-query.xml"));
String query = prop.getProperty("insertMenu");
- loadFromXML() 메소드로 저장된 .xml 파일을 불러온다.
- getProperty() 메소드 인자에는 .xml 파일에 지정해둔 key 값을 작성해 찾아오는 구조이다: getProperty("key")
} catch (IOException e) {
e.printStackTrace();
}
- 이때도 역시 FileInputStream 사용에 있어
IOException에 대한 예외처리가 요구된다.
2. SQL Injection
- SQL 인젝션(SQL Injection)은 SQL문을 주입해 조작하는 해킹 기법의 하나이다.
- 시큐어 코딩(Secure Coding)에서 다루는 내용이나, 여기서는 개념과 대응에 대해서만 간략히 살펴보기로 하자.
2-1. Statement의 경우 방어 불가
-- ID와 이름으로 로그인하는 상황을 가정
private static String empId = "200";
private static String empName = "김구름";
-- main 메소드 안에 작성
String query = "SELECT * FROM EMPLOYEE WHERE EMP_ID = '" + empId + "' AND EMP_NAME = '" + empName + "'";
try {
stmt = con.createStatement();
rset = stmt.executeQuery(query):
- 필드에 empId, empName을 선언한 상황이다.
- main 메소드 안에서는 데이터 타입에 맞춰 작은따옴표로 감싸서 SELECT 쿼리문을 작성한다.
- try 블럭 안에서 쿼리 전송 인터페이스 Statement를 생성 및 실행한다.
- 정해진 필드값이 있으므로 번호나 이름이 다르면 정상 조회되지 않음을 확인할 수 있다.
private static String empId = "200";private static String empName = "' OR 1=1 AND EMP_ID = '200";
- 문제는 위처럼 의도적으로 SQL문을 주입했을 때이다.
- 결국 쿼리문은 AND, OR 논리연산자가 연속된 모양새를 갖는다: WHERE EMP_ID = '200' AND EMP_NAME = '' OR 1=1 AND EMP_ID = '200'
- 따라서 AND 조건이 먼저 false로 읽히고, 뒤따른 OR 조건이 true로 읽혀 전달값 조작만으로 정상 로그인된다.
- Statement는 SQL Injection을 방어할 수 없는 것이다.
2-2. PreparedStatement의 경우 방어 가능
String query = "SELECT * FROM EMPLOYEE WHERE EMP_ID = ? AND EMP_NAME = ?";
try {
pstmt = con.prepareStatement(query);
pstmt.setString(1, empId);
pstmt.setString(2, empName);
- 반대로 PreparedStatment는 위치홀더 자리마다 주어진 값이 알맞은지 확인을 거친다.
- 따라서 사용만으로 SQL Injection 해킹 기법에 자동 대응할 수 있다.
❗ PrepareStatement(위치홀더 포함)가 Statement보다 효율적이다.
✅ 위치홀더는 인수가 많아 특정 값을 바꿔가며 여러 번 실행해야 할 때 특히 유용하다.
✅ Statement는 SQL 문장을 매번 컴파일하지만, PreparedStatement는 딱 한 번 컴파일하므로 실행속도 또한 빠르다.
✅ 쿼리문 안에서 형식에 맞춰 문자를 작은따옴표로 감싸주던 번거로움도 위치홀더로 대체할 수 있다.
✅ 미리 형식을 준비해놓고 들어가는 값만 그때그때 다르게 처리하는 것이기 때문에 SQL Injection을 막을 수 있다.
'Database' 카테고리의 다른 글
[JDBC] DAO | MVC 패턴 | CRUD | Query (0) | 2022.02.04 |
---|---|
[JDBC] CRUD | INSERT | UPDATE | DELETE (0) | 2022.02.03 |
[Oracle/수업 과제 practice] DML (0) | 2022.01.30 |
[Oracle/수업 과제 practice] DDL(10~15번 문항) (0) | 2022.01.30 |
[JDBC] JDBC Driver | Connection | PreparedStatement (0) | 2022.01.29 |