개요
현재 진행중인 개인 프로젝트에서, mybatis를 이용해 쿼리문을 작성하고 있습니다.
@Insert("insert into TRADE (buyer_id, seller_id, product_id,transaction_date,transaction_status,transaction_product_quantity) VALUES (#{buyerId}, #{sellerId}, #{productId}, #{transactionDate},#{transactionStatus}, #{transactionProductQuantity})")
위와 같이 직접 sql 문을 작성하고 있지만, #{buyerId} 와 같이 입력 값을 설정하게 된다면, SQL injection 공격에 더 안전한 PreparedStatement를 파라미터 값에 바인딩 해주게 됩니다
위와 관련해, SQL injection은 무엇이며, 어떻게 방지해야할지, 그리고 과거에는 어떤 식으로 SQL문을 작성했기에 SQL injection과 관련된 문제가 많았는지, Mybatis 에서는 SQL injection을 어떻게 방지해주는지에 대해 알아보도록 하겠습니다.
SQL injection
SQL 인젝션 공격은 서비스를 사용하는 클라이언트가 입력한 데이터를 이용해 애플리케이션에 SQL 쿼리를 삽입하거나 인젝션 하는 방식으로 이루어집니다. 만약 악의를 가진 사용자가 SQL 인젝션에 성공하면 데이터베이스에서 중요한 데이터를 읽고(개인정보 등등), 중요한 데이터를 수정(삽입/업데이트/삭제)하고, 데이터베이스 관리 작업(DBMS 종료)을 실행하고, DBMS 파일 시스템에 있는 특정 파일의 내용을 복구하고, 심지어는 운영 체제에 명령을 실행할 수도 있습니다.
SQL 인젝션 공격은 애플리케이션 개발자가 미리 정의해놓은 SQL 구문에 자신이 원하는 SQL 명령을 주입하는 방식으로 진행됩니다.
SQL 인젝션 공격은 다음과 같은 경우에 발생합니다
- 신뢰할 수 없는 소스에서 의도치 않은 데이터가 프로그램에 주입되는 경우
- 데이터가 SQL 쿼리를 동적으로 생성할때
SQL Injection의 예시
예시1
SQL문 : select id, first_name, last_name from authors
입력 값: Firstname : evil'ex and Lastname: Newman
완성된 query string : select id, firstname, lastname from authors where firstname = 'evil'ex' and lastname ='newman'
SQL 실행 결과 : Incorrect syntax near il' as the database tried to execute evil.
SQL injection에 취약한 코드 예시:
String firstname = req.getParameter("firstname");
String lastname = req.getParameter("lastname");
// FIXME: do your own validation to detect attacks
String query = "SELECT id, firstname, lastname FROM authors WHERE firstname ="+firstname+" and lastname ="+lastname;
Statement stmt = connection.createStatement();
ResultSet results = stmt .executeQuery(query);
위와 같이 유저의 firstname과 lastname을 통해, 해당 유저의 개인 정보만 가져오는 쿼리가 존재한다고 가정했을 때, lastname에 SQL injection 값으로 newman or 1 = '1' -- 과 같이 작성하게 된다면
where 구문 이후 처음에 판별되는 firstname에 일치하는 값이 만약 데이터베이스에 존재해, true를 반환하게 되고, lastname에 존재하는 값이 데이터베이스에 존재해 true를 반환하게 된후에 1=’1’ 은 늘 true를 반환하기 때문에 firstname과 lastname에 일치하는 유저의 정보만 반환하는것이 아닌 서비스를 사용하는 모든 사용자의 정보를 조회할 수 있게 됩니다.
이러한 SQL injection 공격을 방지하기 위해서는 여러가지 방법이 있습니다.
- PreparedStatements 사용 ( Parameterized Query와 함께 사용)
Parameterized Query를 사용하면 SQL 쿼리에 상수 값 대신 매개변수를 넣을 수 있습니다. 매개변수는 쿼리가 실행될때만 값을 가져오기 때문에 쿼리를 다른 값과 다른 용도로 재사용 할 수 있습니다.
- 적절하게 생성된 Stored Procedure 사용
- 입력값에 대한 허용 목록 검사
- 유저가 입력한 값에 대한 특수문자에 대한 escaping 처리
여러가지 방법 중, PreparedStatements를 사용하는 방법에 대해 좀더 자세히 알아보도록 하겠습니다
PreparedStatement
PreparedStatement를 사용하면 SQL injection 공격을 시도하는 공격자가, SQL 명령을 삽입하더라도 공격자가 쿼리의 실행 의도를 변경할 수 없도록 보장합니다. 앞서 설명했던 예시에서 newman or 1 = '1' -- 과 같이 사용자의 lastname을 입력하더라도, parameterized Query는 SQL injection 공격에 취약하지 않게 'newman or 1 ='1' -- ' 과 같이 입력값 전체를 문자열로 인식하게 됩니다.(또한 매개변수로 입력받은 입력값은, SQL 문이 실행되기 전에 적절하게 이스케이프 및 필터링 처리 되도록 합니다.)
Mybatis 에서의 PreparedStatement
@Insert("insert into PRODUCT (category_id,title,price,product_status) values (#{categoryId},#{title},#{price},#{productStatus})")
void save(Product product);
MyBatis 에서는 #{categoryId} 와 같이 #{} 으로 작성해 준다면, 다음과 같이 실제 query가 작성되게 됩니다.
2023/04/05 16:50:43,113 [DEBUG] [ee2251b6-4501-4175-a87c-5fb06034d07e] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger] [http-nio-8080-exec-1]- ==> Preparing: insert into PRODUCT (category_id,title,price,product_status) values (?,?,?,?)
2023/04/05 16:50:43,341 [DEBUG] [ee2251b6-4501-4175-a87c-5fb06034d07e] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger] [http-nio-8080-exec-1]- ==> Parameters: 1(Integer), 상품1(String), 1000(Integer), TRADABLE(String)
2023/04/05 16:50:43,372 [DEBUG] [ee2251b6-4501-4175-a87c-5fb06034d07e] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger] [http-nio-8080-exec-1]- <== Updates: 1
#{} 자리에 ? 가 위치하게 되고 해당 위치에 적절한 사용자 입력 값이 바인딩 되게 됩니다.
참고 자료
https://owasp.org/www-community/attacks/SQL_Injection
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
'프로그래밍 > 프로젝트' 카테고리의 다른 글
JMeter를 통한 부하테스트 (1) | 2023.06.03 |
---|---|
테스트 시, Redis Session 으로 인해 생긴 문제 해결 (0) | 2023.04.19 |
Builder 패턴? (0) | 2023.04.05 |
테스트 커버리지를 70% 이상 유지하면서 느낀점 (0) | 2023.03.21 |
도커 컴포즈 사용 시 DB 초기화 문제 해결 과정 (0) | 2023.03.18 |