Search
Duplicate
📁

⭐ 면접에 자주 나오는 SQL injection

SQL injection

악의적인 SQL 코드를 데이터베이스 쿼리에 삽입하는 공격 기법이다.
이 공격을 통해 공격자는 데이터베이스를 조작하여 민감한 정보를 획득하거나 삭제, 변경하는 등의 행위를 할 수 있다.
SQL 인젝션은 웹 애플리케이션의 보안 취약점 중 하나로, 입력 값 검증이나 적절한 쿼리 구조의 사용 없이 사용자 입력을 데이터베이스 쿼리에 직접 포함시킬 때 발생한다.

공격 방법

사용자 입력을 통한 공격
사용자로부터 받은 입력값을 필터링 없이 SQL 쿼리에 포함시킬 때, 공격자는 악의적인 코드를 삽입하여 원하지 않는 쿼리를 실행시킬 수 있다.
로그인 폼 우회
공격자는 SQL 인젝션을 이용해 인증 과정을 우회하여 무단으로 시스템에 접근할 수 있다.
데이터 유출
민감한 정보가 저장된 데이터베이스에서 데이터를 추출할 수 있다.
데이터베이스 조작
데이터베이스에 저장된 데이터를 수정하거나 삭제할 수 있다.

해결 방법

사용자 입력 검증
서버 측에서 철저하게 검증.
PreparedStatement 사용
PreparedStatement를 사용해서 SQL 인젝션을 방지할 수 있다.
쿼리에 변수를 직접 삽입하는 대신, 미리 컴파일된 쿼리에 파라미터를 전달하는 방식.
String query = "SELECT * FROM users WHERE username = ? AND password = ?"; PreparedStatement pstmt = connection.prepareStatement(query); pstmt.setString(1, username); pstmt.setString(2, password); ResultSet results = pstmt.executeQuery();
Java
복사
코드 예시에서는 ?를 사용하여 사용자 입력 위치를 표시하고, setString 메소드를 통해 안전하게 값을 설정한다.
ORM 사용
ORM 라이브러리를 사용하면 SQL 쿼리를 직접 작성하는 대신 객체 기반의 인터페이스를 통해 데이터베이스를 조작할 수 있어, SQL 인젝션 위험을 줄일 수 있다.
웹 애플리케이션 방화벽(WAF) 사용
특정 패턴이나 의심스러운 트래픽을 감지하여 SQL 인젝션 공격을 차단할 수 있다.
SQL 인젝션은 심각한 보안 위협이지만, 적절한 방어 기법을 적용함으로써 효과적으로 방지할 수 있음.
사용자 입력 검증, 안전한 쿼리 작성 방법의 적용, 보안 도구의 사용은 SQL 인젝션 방지를 위한 필수 조치다.

PreparedStatement와 Statement

SQL 인젝션 공격에 대응하기 위한 방법으로 PreparedStatementStatement의 사용이 중요하다.
이 두 인터페이스는 자바의 JDBC(Java Database Connectivity) API에서 SQL 쿼리를 실행하는 데 사용된다.

Statement

Statement는 SQL 쿼리를 실행하기 위한 기본 인터페이스다.
사용자 입력을 쿼리 문자열에 직접 결합하여 SQL 쿼리를 생성한다.
동적인 쿼리 실행에 사용되며, SQL 인젝션 공격에 취약하다는 단점이 있다.
예시
Statement stmt = connection.createStatement(); String query = "SELECT * FROM users WHERE username = '" + username + "'"; ResultSet rs = stmt.executeQuery(query);
Java
복사
username 변수에 악의적인 SQL 코드가 포함될 경우 데이터베이스가 공격받을 수 있다.

PreparedStatement

PreparedStatementStatement를 상속받는 인터페이스로, 미리 컴파일된 SQL 쿼리를 사용한다.
쿼리의 구조가 미리 정해져 있고, 실행 시점에 파라미터를 전달하여 쿼리를 실행한다.
SQL 인젝션 공격에 대한 보호 기능을 제공한다.
사용자 입력이 쿼리의 일부로 결합되지 않음. 오로지 파라미터로 전달되기 때문에, 악의적인 SQL 코드가 쿼리를 변조할 수 없다.
성능 측면에서도 이점이 있다. 미리 컴파일된 쿼리를 여러 번 재사용할 수 있으므로, 쿼리 실행 속도가 향상될 수 있다.
예시
String query = "SELECT * FROM users WHERE username = ? AND password = ?"; PreparedStatement pstmt = connection.prepareStatement(query); pstmt.setString(1, username); pstmt.setString(2, password); ResultSet rs = pstmt.executeQuery();
Java
복사
이 방식에서는 ?를 통해 파라미터의 위치를 지정하고, setString 메소드를 사용하여 안전하게 파라미터 값을 설정한다.

차이점

보안
PreparedStatement는 SQL 인젝션 공격에 대해 안전하다.
사용자 입력이 쿼리에 파라미터로 전달되므로, 입력값이 쿼리를 변조할 수 없다.
반면, Statement는 사용자 입력을 쿼리에 직접 결합하기 때문에 SQL 인젝션 공격에 취약하다.
성능
PreparedStatement는 미리 컴파일된 SQL 쿼리를 사용하므로, 동일한 쿼리를 반복 실행할 경우 성능상의 이점이 있다.
Statement는 매번 쿼리를 컴파일해야 하므로, 같은 쿼리를 반복 실행할 경우 PreparedStatement보다 성능이 떨어질 수 있다.
용도
동적으로 변하는 쿼리를 실행할 경우 Statement를 사용할 수 있지만, 보안과 성능을 고려할 때 PreparedStatement의 사용이 권장된다.
결론적으로, SQL 인젝션 공격을 방지하고 데이터베이스 애플리케이션의 성능을 최적화하기 위해서는 PreparedStatement의 사용이 권장된다.

근데 민감한 정보를 어떻게 가져갈 수 있을까?

창의적인 방법이 여러개 있다.
원래 조건문으로만 사용해야 되는데 종결문을 포함한 쿼리를 새로 붙여 넣어서 이제 앞에 조건은 항상 통과해서 쿼리를 패스시킨다던가 아니면 뒤에 쿼리가 실행되게 해서 데이터를 끌어온다거나…
-- 원래 쿼리 SELECT * FROM users WHERE username = 'user' AND password = 'pass'; -- SQL 인젝션 공격 SELECT * FROM users WHERE username = 'user' OR 1=1 -- AND password = 'pass';
SQL
복사
예전에는 프로그래밍을 하면 ORM 개념이 많이 없어서 사실 이런 방법을 누구나 다 알고 있었다.
요즘에는 ORM이 너무 유명해져서 대부분의 프레임워크를 쓰면 사실 이걸 걱정할 일이 거의 없다.
성능 최적화를 하다 보면은 이제 직접 쿼리를 다루게 될 일이 있어서 모르고 있으면 큰 이슈가 발생할 수 있기 때문에 잘 알고 있어야 한다.