🎈 SQL Injection이란?
SQL Injection 이란 악의적인 사용자가 보안상의 취약점을 이용하여,
임의의 SQL 문을 주입하고 실행되게 하여 데이터베이스가 비정상적인 동작을 하도록 조작하는 행위 입니다.
🎈 SQL Injection 공격 종류 및 방법
- 논리적 에러를 이용한 SQL Injection
위의 사진에서 보이는 쿼리문은 일반적으로 로그인 시 많이 사용되는 SQL 구문입니다.
해당 구문에서 입력값에 대한 검증이 없음을 확인하고, 악의적인 사용자가 임의의 SQL 구문을 주입하였습니다.
주입된 내용은 ‘ OR 1=1 -- 로 WHERE 절에 있는 싱글쿼터를 닫아주기 위한 싱글쿼터와
OR 1=1 라는 구문을 이용해 WHERE 절을 모두 참으로 만들고,
-- 를 넣어줌으로 뒤의 구문을 모두 주석 처리 해주었습니다.
결론적으로 Users 테이블에 있는 모든 정보를 조회하게 됨으로써
가장 먼저 만들어진 계정으로 로그인에 성공하게 됩니다.
보통은 관리자 계정을 맨 처음 만들기 때문에 관리자 계정에 로그인 할 수 있게 됩니다.
관리자 계정을 탈취한 악의적인 사용자는 관리자의 권한을 이용해 또 다른 2차피해를 발생 시킬 수 있게 됩니다.
- Union 명령어를 이용한 SQL Injection
SQL 에서 Union 키워드는 두 개의 쿼리문에 대한 결과를 통합해서 하나의 테이블로 보여주게 하는 키워드 입니다.
정상적인 쿼리문에 Union 키워드를 사용하여 인젝션에 성공하면, 원하는 쿼리문을 실행할 수 있게 됩니다.
Union Injection을 성공하기 위해서는 두 가지의 조건이 있습니다.
하나는 Union 하는 두 테이블의 컬럼 수가 같아야 하고, 데이터 형이 같아야 합니다.
위의 사진에서 보이는 쿼리문은 Board 라는 테이블에서 게시글을 검색하는 쿼리문입니다.
입력값을 title 과 contents 컬럼의 데이터랑 비교한 뒤 비슷한 글자가 있는 게시글을 출력합니다.
여기서 입력값으로 Union 키워드와 함께 컬럼 수를 맞춰서 SELECT 구문을 넣어주게 되면
두 쿼리문이 합쳐서서 하나의 테이블로 보여지게 됩니다.
현재 인젝션 한 구문은 사용자의 id와 passwd를 요청하는 쿼리문 입니다.
인젝션이 성공하게 되면, 사용자의 개인정보가 게시글과 함께 화면에 보여지게 됩니다.
물론 패스워드를 평문으로 데이터베이스에 저장하지는 않겠지만 인젝션이 가능하다는 점에서
이미 그 이상의 보안위험에 노출되어 있습니다.
이 공격도 역시 입력값에 대한 검증이 없기 때문에 발생하게 되었습니다.
- Boolean based SQL
위의 그림은 Blind Injection을 이용하여 데이터베이스의 테이블 명을 알아내는 방법입니다. (MySQL)
인젝션이 가능한 로그인 폼을 통하여 악의적인 사용자는 임의로 가입한 abc123 이라는 아이디와 함께
abc123’ and ASCII(SUBSTR(SELECT name From information_schema.tables WHERE table_type=’base table’ limit 0,1)1,1)) > 100 -- 이라는 구문을 주입합니다.
해당구문은 MySQL 에서 테이블 명을 조회하는 구문으로 limit 키워드를 통해 하나의 테이블만 조회하고,
SUBSTR 함수로 첫 글자만, 그리고 마지막으로 ASCII 를 통해서 ascii 값으로 변환해줍니다.
만약에 조회되는 테이블 명이 Users 라면 ‘U’ 자가 ascii 값으로 조회가 될 것이고,
뒤의 100 이라는 숫자 값과 비교를 하게 됩니다.
거짓이면 로그인 실패가 될 것이고, 참이 될 때까지 뒤의 100이라는 숫자를 변경해 가면서 비교를 하면 됩니다.
공격자는 이 프로세스를 자동화 스크립트를 통하여 단기간 내에 테이블 명을 알아 낼 수 있습니다.
🎈 Statement와 Prepared Statement 가 뭘까?
- SQL문을 실행할 수 있는 객체를 의미함
- 둘의 가장 큰 차이점은 캐시 사용 여부
🎈 Statement
Statement는 쿼리를 실행할 때마다 아래 1~5단계를 수행해야 한다.
- 구문 분석 및 정규화 단계
- 쿼리 문법이 제대로 작성되었는지 확인하고 해당 테이블과 칼럼이 데이터베이스에 존재하는지 확인한다.
- 컴파일 단계
- 쿼리를 컴파일한다.
- 쿼리 최적화 계획
- 쿼리를 실행할 수 있는 방법의 수와 쿼리를 실행하는 각 방법의 비용을 알아내 최적의 계획을 선택한다.
- 캐시
- 쿼리 최적화 계획에서 선택된 계획은 캐시에 저장되므로 동일한 쿼리가 들어올때마다 1, 2, 3단계를 다시 실행하지 않고 다음에 동일한 쿼리가 들어오면 Cache 찾아 실행한다.
- 실행 단계
- 쿼리가 실행되고 데이터가 ResultSet 객체로 사용자에게 반환된다.
🎈 PreparedStatement
PreparedStatement는 완전한 SQL 쿼리가 아니고
SQL 쿼리의 틀을 미리 생성해 놓고 물음표를 대체할 값을 나중에 지정한다.
따라서 PreparedStatement가 처음 실행될 때 위의 1~3단계를 수행 후 사전 컴파일 되어 캐시에 저장된다.
이후에 Placeholder Replacement 라는 추가 단계가 있으며
런타임시에 사용자가 입력한 데이터로 set메서드를 사용해 ?를 대체한다.
?가 사용자가 입력한 데이터로 바뀐 후에는 최종 쿼리가 다시 구문 분석하거나 컴파일하지 않는다.
🎈 PreparedStatement 와 Statement 차이
특징 비교
Statement | PreparedStatement |
어떤 SQL문을 사용하는지 파악하기 쉽다. executeQuery() 나 executeUpdate() 를 실행하는 시점에 파라미터로 SQL문을 전달 |
어떤 SQL문을 사용하는지 파악하기 어렵다. 보통 조건절과 함께 사용되며 재사용이 되는데, ? 부분에만 변화를 주어 지속적으로 SQL을 수행하기 때문 |
매번 쿼리를 할때마다 컴파일을 하기 때문에 성능상의 이슈가 있을 가능성이 있다. | 미리 Statement를 컴파일 실행하기 때문에 성능상 이점이 있다. |
정적 SQL 문을 실행해, 작성된 결과를 돌려주기 위한 오브젝트이다. 디폴트에서는 Statement 오브젝트 마다 1개의 ResultSet 오브젝트 만이 동시에 오픈할 수 있다. |
Statement를 상속 받음. SQL문은 프리 컴파일 되어 PreparedStatement 오브젝트에 저장된다. 이 오브젝트는 이 문장을 여러 차례 효율적으로 목적으로 사용할 수 있다. |
🎈 SQL Injection 을 막는 방법 중 하나는 PreparedStatement !
PreparedStatement는 SQL Injection 공격을 방지하기 때문에 보안 측면에서도 좋다.
SELECT * FROM users WHERE username='admin' and password='password' OR 1=1 --'
PreparedStatement에서는 쿼리가 컴파일 되어 캐시된 이후에
Placeholder Replacement단계에서 사용자의 데이터로 대체되기 때문에
이미 컴파일된 최종 쿼리는 다시 컴파일 과정을 거치지 않는다.
따라서 사용자의 데이터는 항상 간단한 문자열이여야 하며 쿼리의 원래 논리를 수정할 수 없다.
따라서 PreparedStatement를 사용한 쿼리는 SQL 주입 공격에 대한 영향을 받지 않는다.
출처
'Programming > 프로그래밍 내용 정리' 카테고리의 다른 글
[Linux] top과 jstat -gc 명령어로 서버와 GC 살펴보기(with 챗지피티) (0) | 2023.06.21 |
---|---|
[AWS야 제발 친해지자] VPC / 서브넷 / 인터넷 게이트웨이 / 라우팅 테이블 / 로드밸런싱 (0) | 2023.02.03 |
왜 프로그래밍 언어에서 0.1 + 0.2 는 0.3이 아닐까요? (0) | 2022.08.16 |
[네트워크] OSI7계층이 뭔가요? (0) | 2022.08.15 |
[네크워크] TCP와 UDP의 차이점이 뭔가요? 소켓 프로그래밍이 뭔가요? (0) | 2022.08.14 |