본문 바로가기
홍TV 홍TV

“SQL 인젝션”, 로그인 화면 하나로 DB가 털릴 수 있습니다

홍TV 읽는 시간 약 9분
4.5
(300)

들어가며

웹 개발을 하다 보면 사용자가 입력한 값을 SQL에 넣어 조회하는 경우가 정말 많습니다.

로그인 아이디를 조회하거나, 게시글을 검색하거나, 특정 회원의 정보를 가져오는 기능 등이 대표적입니다.

문제는 사용자가 입력한 값을 SQL 문자열에 그대로 붙여서 사용하면 SQL 인젝션(SQL Injection) 취약점이 발생할 수 있다는 것입니다.

단순히 “이상한 문자를 입력하면 오류가 나는 문제” 정도로 생각하기 쉽지만, 상황에 따라서는 인증 우회나 데이터 노출, 데이터 변경 또는 삭제까지 이어질 수 있습니다.

그래서 SQL 인젝션은 웹 애플리케이션을 개발할 때 기본적으로 신경 써야 하는 보안 취약점 중 하나입니다.

SQL 인젝션이란?

SQL 인젝션은 사용자가 입력한 값이 SQL 명령문의 일부로 해석되도록 만들어 원래 개발자가 의도하지 않았던 SQL 동작이 발생하는 취약점입니다.

예를 들어 로그인 기능을 아주 단순하게 구현한다고 생각해 보겠습니다.

String sql =
    "SELECT * FROM users " +
    "WHERE user_id = '" + userId + "' " +
    "AND password = '" + password + "'";

정상적인 사용자가 아이디와 비밀번호를 입력하면 대략 다음과 같은 SQL이 만들어집니다.

SELECT *
FROM users
WHERE user_id = 'hong'
AND password = '1234';

겉으로 보기에는 별 문제가 없어 보입니다.

그런데 핵심적인 문제는 userIdpasswordSQL 문장과 데이터의 경계가 명확하게 분리되어 있지 않다는 것입니다.

사용자가 입력하는 값에 SQL에서 특별한 의미를 갖는 문자가 포함되면 개발자가 작성한 SQL의 구조 자체에 영향을 줄 가능성이 있습니다.

즉, 애플리케이션 입장에서는 단순한 문자열이라고 생각했던 입력값이 데이터베이스 입장에서는 SQL 명령의 일부로 해석될 수 있는 것입니다.

왜 발생할까?

가장 대표적인 원인은 문자열 연결 방식으로 SQL을 직접 만드는 것입니다.

예를 들어 검색 기능에서 다음과 같이 작성했다고 가정해 보겠습니다.

String sql =
    "SELECT * FROM board " +
    "WHERE title LIKE '%" + keyword + "%'";

사용자가 정상적으로 축구라고 입력하면 다음과 같이 만들어집니다.

SELECT *
FROM board
WHERE title LIKE '%축구%';

문제는 keyword가 사용자가 직접 입력하는 값이라는 것입니다.

애플리케이션에서 아무런 검증이나 처리를 하지 않고 SQL에 그대로 붙여 버리면 공격자가 SQL 문법에 영향을 줄 수 있는 입력을 시도할 수 있습니다.

따라서 SQL 인젝션의 핵심은 단순히 “특수문자를 입력한다”가 아닙니다.

신뢰할 수 없는 사용자 입력이 SQL 명령문과 섞이는 구조 자체가 문제라고 이해하는 것이 좋습니다.

PreparedStatement를 사용하는 이유

Java에서 SQL 인젝션을 방어할 때 대표적으로 사용하는 방법이 PreparedStatement입니다.

문자열을 직접 연결하는 대신 SQL 구조와 사용자 입력값을 분리합니다.

String sql =
    "SELECT * FROM users " +
    "WHERE user_id = ? " +
    "AND password = ?";

PreparedStatement pstmt = connection.prepareStatement(sql);

pstmt.setString(1, userId);
pstmt.setString(2, password);

ResultSet rs = pstmt.executeQuery();

여기서 중요한 부분은 ?입니다.

SQL 문장 자체에는 사용자 입력값이 들어가지 않고, 별도의 파라미터로 전달됩니다.

이렇게 하면 데이터베이스는 해당 값을 SQL 명령문으로 조합하는 것이 아니라 하나의 데이터 값으로 처리하도록 할 수 있습니다.

Spring이나 MyBatis 같은 프레임워크를 사용하는 경우에도 마찬가지입니다.

예를 들어 MyBatis에서는 다음과 같은 형태를 사용합니다.

<select id="findUser" resultType="User">
    SELECT *
    FROM users
    WHERE user_id = #{userId}
</select>

여기서 #{userId}와 같은 파라미터 바인딩을 사용하는 것이 중요합니다.

반대로 문자열 치환 방식으로 SQL을 직접 만들어 버리는 구조는 주의해야 합니다.

입력값 검증만 하면 안전할까?

여기서 흔히 하는 실수가 있습니다.

“아이디에 특수문자가 들어오지 못하게 막으면 되겠네?”

입력값 검증은 분명 도움이 되지만 SQL 인젝션 방어의 핵심을 입력값 필터링 하나에만 맡기는 것은 적절하지 않습니다.

왜냐하면 애플리케이션마다 허용해야 하는 문자가 다르고, 예상하지 못한 입력 형태가 존재할 수 있기 때문입니다.

예를 들어 이름이나 검색어에는 특수문자가 정상적으로 포함될 수도 있습니다.

그래서 보안 관점에서는 다음과 같은 구조가 더 중요합니다.

사용자 입력 → 파라미터 바인딩 → SQL 실행

그리고 입력값 검증은 여기에 추가적인 방어 계층으로 사용하는 방식입니다.

ORM을 사용하면 SQL 인젝션은 없어질까?

JPA, Hibernate 같은 ORM을 사용한다고 해서 무조건 안전한 것은 아닙니다.

ORM을 사용하더라도 개발자가 직접 문자열을 조합해서 동적 SQL이나 쿼리를 만드는 부분에서는 여전히 주의해야 합니다.

예를 들어 애플리케이션의 구조가 편리해졌다고 해서 사용자 입력을 그대로 SQL 문자열에 넣는 방식이 자동으로 안전해지는 것은 아닙니다.

결국 중요한 것은 사용하는 프레임워크의 이름보다 사용자 입력을 어떻게 쿼리에 전달하고 있는가입니다.

SQL 인젝션을 예방하려면

실무에서는 몇 가지 원칙을 기본적으로 지키는 것이 좋습니다.

첫 번째는 PreparedStatement 또는 안전한 파라미터 바인딩을 사용하는 것입니다.

두 번째는 사용자 입력을 SQL 문자열에 직접 연결하지 않는 것입니다.

세 번째는 입력값 검증을 적절하게 적용하는 것입니다.

네 번째는 데이터베이스 계정의 권한을 최소화하는 것입니다.

웹 애플리케이션이 데이터를 조회하는 역할만 한다면 해당 DB 계정에 불필요한 삭제나 구조 변경 권한까지 줄 필요가 없습니다.

이렇게 하면 애플리케이션에 문제가 발생하더라도 피해 범위를 줄이는 데 도움이 됩니다.

다섯 번째는 오류 메시지를 사용자에게 그대로 노출하지 않는 것입니다.

SQL 오류가 화면에 그대로 출력되면 데이터베이스 종류나 테이블 구조 등 내부 정보가 외부에 노출될 가능성이 있습니다.

개발할 때 가장 먼저 볼 부분

기존 프로젝트에서 SQL 인젝션이 걱정된다면 무작정 모든 코드를 수정하기보다 다음과 같은 부분부터 찾아보는 것도 방법입니다.

"SELECT ... " + 변수
"UPDATE ... " + 변수
"DELETE ... " + 변수
"WHERE ... " + 변수

특히 검색, 로그인, 회원 조회, 게시판, 관리자 페이지처럼 사용자가 값을 입력할 수 있는 기능을 우선적으로 확인하는 것이 좋습니다.

그리고 SQL 문자열에 변수가 직접 연결되어 있다면 해당 부분이 파라미터 바인딩 구조로 되어 있는지 확인해 보는 것이 좋습니다.

마무리

SQL 인젝션은 오래전부터 알려진 공격 방법이지만, 그렇다고 해서 이제는 신경 쓰지 않아도 되는 취약점은 아닙니다.

오히려 기존 시스템을 유지보수하다 보면 오래된 코드에서 문자열 연결 방식으로 SQL을 생성하는 경우를 발견할 수 있습니다.

결국 가장 중요한 것은 하나입니다.

사용자가 입력한 값과 SQL 명령문을 섞지 않는 것.

PreparedStatement, 파라미터 바인딩, 적절한 입력값 검증, 최소 권한 원칙 등을 함께 적용하면 SQL 인젝션 위험을 크게 줄일 수 있습니다.

특히 신규 기능을 만들 때부터 “일단 동작하게 만들고 나중에 보안을 적용하자”는 식으로 접근하기보다는, 처음부터 사용자 입력과 SQL을 분리하는 습관을 들이는 것이 좋습니다.

SQL 인젝션은 복잡한 보안 기술이라기보다 잘못된 데이터 처리 방식에서 시작되는 문제라고 생각하면 이해하기 쉽습니다.

이 게시물이 얼마나 유용했나요?

별점을 클릭하여 평가하세요!

평균 평점 4.5 / 5. 투표 수: 300

아직 투표가 없습니다! 첫 번째로 평가해보세요.

홍TV

홍TV
함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.

error: Content is protected !!

광고 차단 알림

광고 클릭 제한을 초과하여 광고가 차단되었습니다.

단시간에 반복적인 광고 클릭은 시스템에 의해 감지되며, IP가 수집되어 사이트 관리자가 확인 가능합니다.