[SQL] SQL 삽입 공격(SQL Injection Attack) 차단/방지
[SQL 삽입 공격 차단/방지]
SQL 삽입 공격(SQL Injection Attack)이란?
: 웹 애플리케이션에서 가장 흔한 보안 취약점 중 하나로, 이 공격은 사용자 입력을 통해 SQL 쿼리에 악의적인 코드를 삽입하여 데이터베이스를 비정상적으로 조작하거나 민감한 정보를 노출시키는 것을 목표로 한다. SQL 삽입 공격은 데이터베이스와 직접적으로 상호작용하는 웹 애플리케이션에서 발생한다. (출처: chatGPT)
예시)
- 제목: aaa
- 내용: bbb’), (‘ccc’,’ccc’), (‘ddd’,’ddd
SQL문: insert into x_board(title, contents) values(‘%s’, ‘%s’)
최종적으로 생성된 SQL?
=> insert into x_board(title, contents) values (‘aaa’, ‘bbb’), (‘ccc’, ‘ccc’), (‘ddd’, ‘ddd’)
사용자가 입력한 값을 가지고 SQL문을 만들면 SQL문이 왜곡될 수 있다. 이런 식의 해킹 기법을 “SQL 삽입 공격”이라고 부른다.
방지책: “PreparedStatement”를 사용한다.
애초부터 분리해서 보내는 것이다.
PreparedStatement를 사용하여 mysql 폴더에 있던 DAOImpl가 받을 수 있는 SQL 삽입 공격을 막아보겠다. 기존에 Statement stmt = 으로 되어있던 부분을 일괄수정(MacOs: cmd + shift + f / Windows: ctrl + shift + r)로 PreparedStatement stmt로 바꿔준다.
방법:
우선, PreparedStatement를 임포트해준다.
import java.sql.PreparedStatement;
그러면 AssignmentDaoImpl라는 과제 파일을 예시로 CRUD에 어떻게 적용해야할지 알아보겠다.
=> preparedStatement를 적용하기 전 코드:
public class AssignmentDaoImpl implements AssignmentDao {
Connection con;
public AssignmentDaoImpl(Connection con) {
this.con = con;
}
@Override
public void add(Assignment assignment) {
try {
Statement stmt = con.createStatement();
stmt.executeUpdate(String.format(
"insert into assignments(title,content,deadline) values('%s','%s','%s')",
assignment.getTitle(), assignment.getContent(), assignment.getDeadline()));
} catch (Exception e) {
throw new DaoException("데이터 입력 오류", e);
}
}
@Override
public int delete(int no) {
try {
Statement stmt = con.createStatement();
return stmt.executeUpdate(
String.format("delete from assignments where assignment_no=%d", no));
} catch (Exception e) {
throw new DaoException("데이터 삭제 오류", e);
}
}
@Override
public List<Assignment> findAll() {
try {
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select * from assignments");
ArrayList<Assignment> list = new ArrayList<>();
while (rs.next()) {
Assignment assignment = new Assignment();
assignment.setNo(rs.getInt("assignment_no"));
assignment.setTitle(rs.getString("title"));
assignment.setContent(rs.getString("content"));
assignment.setDeadline(rs.getDate("deadline"));
list.add(assignment);
}
return list;
} catch (Exception e) {
throw new DaoException("데이터 가져오기 오류", e);
}
}
@Override
public Assignment findBy(int no) {
try {
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select * from assignments where assignment_no=" + no);
ArrayList<Assignment> list = new ArrayList<>();
if (rs.next()) {
Assignment assignment = new Assignment();
assignment.setNo(rs.getInt("assignment_no"));
assignment.setTitle(rs.getString("title"));
assignment.setContent(rs.getString("content"));
assignment.setDeadline(rs.getDate("deadline"));
return assignment;
}
return null;
} catch (Exception e) {
throw new DaoException("데이터 가져오기 오류", e);
}
}
@Override
public int update(Assignment assignment) {
try {
Statement stmt = con.createStatement();
return stmt.executeUpdate(String.format(
"update assignments set title='%s', content='%s', deadline='%s' where assignment_no=%d",
assignment.getTitle(), assignment.getContent(), assignment.getDeadline(),
assignment.getNo()));
} catch (Exception e) {
throw new DaoException("데이터 변경 오류", e);
}
}
}
- 참고: CRUD & SQL 문법
- Create = insert
- Retrive(Read) = select
- Update = update
- Delete = delete
(1) Add
@Override
public void add(Assignment assignment) {
try (PreparedStatement pstmt = con.prepareStatement(
"insert into assignments(title,content,deadline) values(?,?,?)")) {
pstmt.setString(1, assignment.getTitle());
pstmt.setString(2, assignment.getContent());
pstmt.setDate(3, assignment.getDeadline());
pstmt.executeUpdate();
} catch (Exception e) {
throw new DaoException("데이터 입력 오류", e);
}
}
(2) Delete
@Override
public int delete(int no) {
try (PreparedStatement pstmt = con.prepareStatement(
"delete from assignments where assignment_no=?")) {
pstmt.setInt(1, no);
return pstmt.executeUpdate();
} catch (Exception e) {
throw new DaoException("데이터 삭제 오류", e);
}
}
(3) FindAll
@Override
public List<Assignment> findAll() {
try (PreparedStatement pstmt = con.prepareStatement(
"select assignment_no, title, deadline from assignments order by assignment_no desc");
ResultSet rs = pstmt.executeQuery()) {
ArrayList<Assignment> list = new ArrayList<>();
while (rs.next()) {
Assignment assignment = new Assignment();
assignment.setNo(rs.getInt("assignment_no"));
assignment.setTitle(rs.getString("title"));
assignment.setDeadline(rs.getDate("deadline"));
list.add(assignment);
}
return list;
} catch (Exception e) {
throw new DaoException("데이터 가져오기 오류", e);
}
}
(4) FindBy
@Override
public Assignment findBy(int no) {
try (PreparedStatement pstmt = con.prepareStatement(
"select * from assignments where assignment_no=?")){
pstmt.setInt(1, no);
try(ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
Assignment assignment = new Assignment();
assignment.setNo(rs.getInt("assignment_no"));
assignment.setTitle(rs.getString("title"));
assignment.setContent(rs.getString("content"));
assignment.setDeadline(rs.getDate("deadline"));
return assignment;
}
return null;
}
} catch (Exception e) {
throw new DaoException("데이터 가져오기 오류", e);
}
}
(4) Update
@Override
public int update(Assignment assignment) {
try (PreparedStatement pstmt = con.prepareStatement(
"update assignments set title=?, content=?, deadline=? where assignment_no=?")) {
pstmt.setString(1, assignment.getTitle());
pstmt.setString(2, assignment.getContent());
pstmt.setDate(3, assignment.getDeadline());
pstmt.setInt(4, assignment.getNo());
return pstmt.executeUpdate();
} catch (Exception e) {
throw new DaoException("데이터 변경 오류", e);
}
}
🚨 주의: ‘?’ 물음표를 싱글 쿼테이션 안에 넣으면 일반 문자열로 물음표를 인식하게 된다.