해당 페이지는 해킹 연습을 위한 모의 사이트이다.
일반적으로 아무 아이디와 페스워드를 작성하면
위와 같은 리턴 메세지를 확인 가능하다.
특수 기호 따옴표를 aaaa 뒤에 넣어 보도록 하겠습니다.
관계형 DB 를 사용하는 경우
아이디와 비밀번호 를 DB와 대조하기 위해서 이와 같은 SQL을 작성해 놓았을 것이다.
상세한 에러메세지는 헤커들에게 좋은 지표가 된다.
강제 로그인 되는지 확인해 봅시다 .
제가 tuser 라는 누군가의 아이디를 일단 알아 냈습니다.
이거는 실제 존재하는 id 인데
SELECT * FROM 사용자테이블
WHERE 아이디열 = '입력한아이디' AND 비밀번호열 = '입력한비밀번호';
tuser' --
아이디 인풋 창에 이와 같이 입력하면
SELECT * FROM 사용자테이블
WHERE 아이디열 = 'tuser' --' AND 비밀번호열 = '입력한비밀번호';
이와 같이 입력됩니다.
위의 SQL 만 봐도 알겠지만 -- 는 관계형 대부분 DB에서의 주석처리 코드입니다.
위의 비밀번호 부분은 주석처리가 됩니다.
이렇게 되면 아이디만 맞아도 로그인이 되는 쿼리라는 것을 해커는 확인할 수 있습니다.
비밀번호 공백 예외 처리는 해 놓았을 것이기 때문에
아무 숫자를 비밀번호 창에 입력해주면
로그인 성공 !
다음에는 admin 유저로 로그인 방법
대부분의 SQL 유저 테이블의 경우 admin 이 제일 첫 튜플일 경우가 크다
SELECT * FROM 사용자테이블
WHERE 아이디열 = 'tuser' OR 1=1 --' AND 비밀번호열 = '입력한비밀번호';
이와 같이 입력된다면??
위의 쿼리는 해당 유저 테이블의 전부를 출력할 것이다.
그래서 제일 윗 첫번째 튜플을 보여주는데 그게 admin 계정이 확율이 높은 부분을 치팅하는 방법이다.
어드민 유저 탈취 폼 미쳤다
다음은
해당 홈페이지에서도 테스트 가능하다.
http://testphp.vulnweb.com/artists.php?artist=2
해당 url 에서 숫자로 아티스트의 정보가 바뀌는 것을 알수 있다.
해당 쿼리를 사용해보자
UNION 은 2개의 쿼리를 이어 붙여서 실행해달라는 코드이다.
물론 해당 테이블의 행 갯수를 파악해야한다.
https://testphp.vulnweb.com/artists.php?artist=2%20UNION%20SELECT%201,uname,pass%20FROM%20users%20WHERE%20uname=%27test%27
여기서 핵심은
UNION 앞의 쿼리가 실행 되어도 값이 나올게 없어야 뒤의 값이 웹페이지에 등장한다.
그래서 값은 -1 을 주면 해당 아티스트에 대한 정보가 없기 때문에 이후의 쿼리문을 통해 값이 올라온다.
이와 같이 탈취한 아이디로 로그인이 가능하다.
그러면 어떻게 예방할 수 있을까 ?
1. parameterized query
Parameterized Query는 사용자 입력값을 직접 문자열로 구성하는 대신, 쿼리의 매개변수(파라미터)를 사용하여 안전하게 데이터베이스에 접근하는 방법입니다. 이를 통해 SQL 인젝션 공격을 방지할 수 있습니다.
import sqlite3
# 사용자로부터 입력받은 아이디와 비밀번호
input_username = '사용자가_입력한_아이디'
input_password = '사용자가_입력한_비밀번호'
# SQLite3 데이터베이스 연결
conn = sqlite3.connect('데이터베이스파일.db')
cursor = conn.cursor()
# parameterized query 작성 및 실행
query = 'SELECT * FROM 사용자테이블 WHERE 아이디열 = ? AND 비밀번호열 = ?;'
cursor.execute(query, (input_username, input_password))
# 결과 얻기
result = cursor.fetchall()
# 연결 종료
conn.close()
# 결과 확인
if result:
print('로그인 성공')
else:
print('로그인 실패')
위의 코드에서 중요한 부분은 ?를 사용하여 쿼리의 매개변수를 나타내었습니다. execute 메서드의 두 번째 매개변수로 튜플을 전달하여 매개변수에 값이 대입되게 됩니다. 이러한 방식으로 입력값이 문자열로 직접 쿼리에 삽입되는 것이 아니라, 안전하게 매개변수를 통해 전달되므로 SQL 인젝션 공격에 대한 안전성이 향상됩니다.
2. stored procedure
저장 프로시저(Stored Procedure)는 데이터베이스에 미리 저장되어 있는 일련의 SQL 명령문들을 수행하는 프로그램 유사한 형태의 코드입니다. 저장 프로시저는 일반적으로 특정 작업을 수행하거나 결과를 반환하기 위해 사용됩니다. 여러 개의 SQL 명령문을 그룹화하여 효율적으로 실행할 수 있도록 합니다.
여기서는 MySQL 데이터베이스를 기준으로 Spring Framework에서 저장 프로시저를 사용하는 예제를 보여드리겠습니다. 먼저, MySQL에 다음과 같은 저장 프로시저를 생성해보겠습니다.
DELIMITER //
CREATE PROCEDURE GetUserByUsername(IN p_username VARCHAR(255))
BEGIN
SELECT * FROM users WHERE username = p_username;
END //
DELIMITER ;
저장 프로시저를 사용하면 SQL 인젝션 공격에 대한 보호가 일부 제공됩니다. SQL 인젝션은 주로 사용자 입력을 통해 동적으로 쿼리를 생성할 때 발생하며, 저장 프로시저는 미리 정의된 프로시저를 호출하므로 동적으로 쿼리를 생성하지 않습니다. 그러나 여전히 몇 가지 주의사항이 있습니다.
파라미터화된 쿼리 사용: 저장 프로시저를 호출할 때도 파라미터를 사용하여 값을 전달하는 것이 중요합니다. 이렇게 하면 사용자의 입력이 직접 쿼리 문자열에 삽입되지 않고 안전하게 전달됩니다.
적절한 권한 설정: 저장 프로시저를 실행하는 사용자에게는 최소한의 권한만 부여해야 합니다. 필요한 테이블과 열에만 접근할 수 있도록 권한을 제한하여 데이터베이스 보안을 강화할 수 있습니다.
입력 값 검증 및 정제: 저장 프로시저 내에서 사용자 입력을 사용할 때도 입력 값을 검증하고 정제하는 것이 중요합니다. 예를 들어, 문자열 입력이 예상되는 경우에는 입력 값을 따옴표로 감싸거나 특수 문자를 이스케이프하여 SQL 인젝션을 방지할 수 있습니다.
저장 프로시저의 안전성 검토: 저장 프로시저 내에서도 안전성을 고려해야 합니다. 잘못된 로직이나 권한 부여 문제로 인해 보안에 취약해질 수 있습니다. 따라서 저장 프로시저의 코드를 주기적으로 검토하고 보완하는 것이 좋습니다.
SQL 인젝션은 다양한 형태로 발생할 수 있으며, 보안을 유지하기 위해서는 다채로운 방어책이 필요합니다. 저장 프로시저는 그 중 하나일 뿐이며, 다른 방어책과 함께 사용하여 전체적인 보안을 강화해야 합니다.