MyBatis에서 안전한 데이터 처리 : #{변수}와 ${변수}의 차이점

LeeYulhee·2024년 1월 3일
0

👉 ${변수} : 리터럴 값으로의 직접 치환과 그 위험성


  • 변수가 있는 그 자리에 변수의 값이 문자 그대로 치환
  • 쿼리에 ${tableName}이라고 쓰고, tableName 변수에 users라는 값이 있는 경우
    • ⇒ 쿼리는 SELECT * FROM users가 됨
  • 직접 치환되기 때문에, 외부에서 입력 받은 값에는 사용하면 안 됨
    • ⇒ SQL 인젝션 같은 보안 문제가 생길 수 있음



👉 #{변수} : 보안을 강화하는 안전한 매개변수 바인딩


  • 변수를 쿼리의 일부로 안전하게 삽입
  • 변수의 값을 직접 쿼리에 쓰는 대신, '?' 같은 표시자를 사용하고, 그 값은 나중에 안전하게 삽입
  • 쿼리에서 #{userId}라고 쓰면, 실제 쿼리는 SELECT * FROM users WHERE id = ?가 되고, userId 변수의 값은 별도로 쿼리에 삽입
  • SQL 인젝션 공격으로부터 안전하기 때문에 외부에서 입력 받은 값도 안전하게 쿼리에 사용할 수 있음



👉 SQL 인젝션 방지 : #{변수}의 효과적인 바인딩 방식


  • SQL 인젝션 예시
    String query = "SELECT * FROM users WHERE username = '" + username + "'";
    • username 변수에 "admin' --"와 같은 값이 들어간다면, 최종 쿼리는 "SELECT * FROM users WHERE username = 'admin' --'"가 되어 SQL 주석을 이용한 인젝션 공격이 가능
  • PreparedStatement의 매개변수로 처리하는 방식
    • 먼저, MyBatis는 SQL 쿼리를 준비함
      • 이때 #{변수}? (placeholder)로 대체
      • 예를 들어, "SELECT * FROM users WHERE id = #{userId}"라는 쿼리는 "SELECT * FROM users WHERE id = ?"로 변환
    • 쿼리가 준비되고 나면, MyBatis는 각 #{변수}에 해당하는 자리에 실제 변수의 값을 바인딩함
      • 이 과정에서 PreparedStatement를 사용하여 안전하게 수행
      • PreparedStatement는 SQL 쿼리에 값을 바인딩할 때 적절한 타입 변환을 수행하고, 필요한 경우 값을 이스케이프(escape)하여 SQL 인젝션 공격을 방지
    • 이렇게 준비된 쿼리는 데이터베이스에서 실행됨
      • 이때, ? 자리에는 안전하게 바인딩된 실제 값이 들어 감
  • PreparedStatement를 사용하는 경우 예시
    PreparedStatement pstmt = connection.prepareStatement("SELECT * FROM users WHERE username = ?");
    pstmt.setString(1, username);
    • 여기서 username"admin' --"가 들어가도, 이 값은 문자열 데이터로 처리되어 쿼리의 구조를 변경할 수 없음
    • ⇒ SQL 인젝션 공격 방지됨
profile
끝없이 성장하고자 하는 백엔드 개발자입니다.

0개의 댓글