식별
사용자가 본인의 신원정보를 밝히고 확인하는 행위 ( ID )
인증 (Authentication)
보호된 리소스에 접근하는 것을 허용하기 이전에 등록된 유저의 신원을 입증 (validating)하는 과정
---------------------------------------------------------------------------------------------------------------------------------------------
우선, 로그인에 관해 배울 때 처음 보게되는 ID = 'BOKYU' and PWD = '1111' 같은
지정형 로그인 로직은 패스하도록 하자 너무 간단하기도 하고 사실 분석할 건덕지가 없으니..!
1 ) 식별 / 인증 동시
SELECT * FROM user WHERE id = '$_POST["id"]' and pass = '$_POST["pwd"]'
WHERE ~ AND ~ 는 id와 password가 같아야만 질의문을 실행하게 된다.
연산자에서 or는 둘 중에 하나만 맞아도 조건이 참이 되지만
and 는 둘 다 맞아야만 참을 반환하는 일종의 인증 역할을 수행할 수 있다.
인증을 받아야만 DB에서 데이터를 꺼내는 식별이 가능하기 때문에 식별 / 인증이 동시에 진행된다고 할 수 있다.
<?php
//세션 시작
session_start();
//MySQL DB연결
$con = mysqli_connect('localhost','root','1234','test');
$input_id = $_POST['id'];
$input_pw = $_POST['pw'];
//아이디가 있는지 없는지 검사
//식별-인증 구간
$query = "SELECT * FROM user WHERE username='".$input_id."' AND password='".$input_pw."'";
$result = $con -> query($query); //쿼리문을 데이터베이스에 적용시키는 코드
$row = mysqli_fetch_assoc($result);
if($row != 0){
$_SESSION['is_login']=true;
$_SESSION['id']=$input_id;
// 이하 생략
}
}
2 ) 식별 / 인증 분리
처음 만들었던 로그인 형태가 바로 이 형태였다.
** SQL 질의문 형태
SELECT password FROM user WHERE username='';
SELECT username FROM user WHERE username='';
식별 과정에서 입력한 id 만을 이용해 DB를 불러온다.
password를 불러오는지, username을 불러오는지 두 가지 형태가 있을 수 있다.
전에 만들어놓은 login_proc.php의 코드와 똑같진 않지만
식별 / 인증 분리의 기본적인 형태로 한번 수정해보았다
login_proc.php
<?php
//세션 시작
session_start();
//MySQL DB연결
$con = mysqli_connect('localhost','root','1234','test');
$input_id = $_POST['id'];
$input_pw = $_POST['pw'];
//아이디가 있는지 없는지 검사
//식별 구간
$query = "SELECT password FROM user WHERE username='$input_id'";
$result = $con -> query($query); //쿼리문을 데이터베이스에 적용시키는 코드
if (mysqli_num_rows($result)>=1) {
$row = mysqli_fetch_assoc($result);
//인증 구간
if($row['id']==$input_id){
$_SESSION['is_login']=true;
$_SESSION['id']=$input_id;
// 이하 생략
}
}
mysqli_num_rows 를 통해 실행한 쿼리문이 값이 있는지를 체크하고 1이상이면 연관배열을 생성한다.
이걸 할 수 있는 함수는 3가지가 있습니다.
- mysql_fetch_assoc : 문자열 인덱스 배열로 변환
- mysql_fetch_row : 숫자형 인덱스 배열로 변환
- mysql_fetch_array : $result_type 의 결과에 따라 두 가지 타입의 배열을 반환하거나 동시에 반환할 수 있습니다.
ex ) mysql_fetch_array ($result MYSQL_BOTH) 이렇게 쓰면 두 가지 타입을 동시에 반환한다.
이 함수들은 또한 데이터를 읽은 후 함수 내부의 포인터를
증가시켜 다시 호출될 때 다음 행을 읽을 수 있도록 합니다.
3 ) 식별 / 인증 동시 + 개행
1번과 동일하지만 개행 처리가 되어 있어 SQL Injection 시에 주석 처리가 먹히지 않는다.
SELECT id,pass FROM user WHERE id = ''
\n AND pass = '';
4 ) 식별 / 인증 동시 + Hash
** SQL 질의문 형태
SELECT id,pass FROM user WHERE id = '' AND pass = md5('');
md5는 괄호 안의 문자열을 md5의 규칙에 따라 해시화하는 함수인데,
주로 쓰는 함수로 이것 말고도 SHA256도 있다.
SELECT id,pass FROM user WHERE id = '' AND pass = sha256('');
나머지는 해시함수가 들어가는 것 외에 1번과 동일하다.
login_proc.php
<?php
//세션 시작
session_start();
//MySQL DB연결
$con = mysqli_connect('localhost','root','1234','test');
$input_id = $_POST['id'];
$input_pw = $_POST['pw'];
//아이디가 있는지 없는지 검사
//식별-인증 구간
$query = "SELECT username,password FROM user WHERE username='".$input_id."' AND password='".md5($input_pw)."'";
$result = $con -> query($query); //쿼리문을 데이터베이스에 적용시키는 코드
$row = mysqli_fetch_assoc($result);
if($row != 0){
$_SESSION['is_login']=true;
$_SESSION['id']=$input_id;
// 이하 생략
}
}
5 ) 식별 / 인증 분리 + Hash
식별 / 인증 분리인 2번의 형태와 같지만 인증에서 해시함수가 들어가는 차이가 있다.
<?php
//세션 시작
session_start();
//MySQL DB연결
$con = mysqli_connect('localhost','root','1234','test');
$input_id = $_POST['id'];
$input_pw = $_POST['pw'];
//아이디가 있는지 없는지 검사
//식별 구간
$query = "SELECT username FROM user WHERE username='$input_id'";
$result = $con -> query($query); //쿼리문을 데이터베이스에 적용시키는 코드
if (mysqli_num_rows($result)>=1) {
$row = mysqli_fetch_assoc($result);
//인증 구간
if($row['password']==md5($input_pw)){
$_SESSION['is_login']=true;
$_SESSION['id']=$input_id;
// 이하 생략
}
}
-------------------------------------------------------------
각 CASE 별 우회법
만들어 놓은 서버에서 테스트해보았다.
1 ) 식별 - 인증 동시 CASE
bg5294' # 이나
' (작음 따옴표)
또는
bg5294' or 1=1 #
bg5294' or '1'='1 을 입력하면 로그인 우회가 가능하다.
2 ) 식별 - 인증 분리 CASE
식별 - 인증 분리 case에서는 SQL 문 뒤를 주석처리 해봤자 비밀번호 인증 부분이 해결되지 않으므로
union 을 쓰는 것이 좋다.
작은 따옴표로 짝을 맞추고 x' union select '1','2' # 이런 식으로 입력하면
각 db의 첫 인덱스에 1, 두번째 인덱스에 2 라는 글자가 새로 생성된다.
이를 이용해 id를 알고 있지만 비밀번호는 모른다고 할 때
x' union select 'bg5294','1234' # 이라고 입력하는 것이다.
그럼 순간적으로 bg5294 아이디에 1234 라는 비밀번호를 가진 계정으로 인식되어
로그인이 된다.
내 페이지에는 현재 어떤 계정으로 로그인되었는지 나타내는 기능을 추가해뒀는데,
union을 포함한 sql문이 통으로 아이디로 인식되어 있는 것을 볼 수 있다.
내가 입력한 코드가 id 변수에서는 x' union ~~ 이지만 sql문으로 들어갔을 때 질의문으로 바뀐다는 것을 알 수 있다.
근데 이건 앞 select 문과 뒤 select 문이 타입이 같게 맞춰놨기 때문이다.
이런 식으로 모든 컬럼을 불러오게 로직이 짜여져 있다면 컬럼의 수가 몇개인지, 어떤 타입으로 이루어져있는지,
존재는 하지만 출력되진 않는 컬럼은 없는지를 판단해야한다.
현재 내 DB는 첫번째 컬럼이 id, 두번째 컬럼이 username 이런 식으로 짜여있고
컬럼의 수는 총 6개이다.
이는 x' order by 7 # 구문을 이용하면 아무 창도 뜨지 않는 오류를 보고 판단할 수 있다.
6 # 까지는 에러 메시지가 뜨므로 조건문을 통과했다는 것을 알 수 있다.
그래서 순서에 맞게 x' union select '1','bg5294','1234','3','4','5' # 이라고 입력 후,
비밀번호에 1234를 입력했더니 로그인이 되었다.
3 ) 식별 - 인증 동시 + 개행
주석을 포함한 SQL Injection 구문은 로그인 우회가 안된다.
bg5294' or '1'='1 처럼 작음 따옴표로 짝은 맞춘 구문만이 로그인 우회가 가능하다.
위 코드가 sql 질의문이 들어갔다고 생각하면
select username, password from user WHERE id=bg5294' or '1'='1'
and password = '아무거나';
초록색으로 표시한 부분이 내가 입력한 부분인데,
연산자 or 와 and 중 and의 연산순위가 높기 때문에
select username, password from user WHERE id=bg5294' or ('1'='1'
and password = '아무거나') ;
이렇게 먼저 실행되고 비밀번호 부분이 false가 나온다. 하지만 그 앞에 or가 있기 때문에 최종 조건이
true가 된다.
4 ) 식별 - 인증 동시 + HASH
입력 란에 bg5294' # 을 입력해서 해시함수 부분을 주석 처리하면 비밀번호와 관계없이
인증을 우회할 수 있다.
SELECT username,password FROM user WHERE id = 'bg5294' #' AND pass = md5('1234')
5 ) 식별 - 인증 분리 + HASH
만약 DB가
SELECT username, password FROM user WHERE id='';
인 형태라고 해보자.
그럼 id에 밑 코드를 대입한다.
x' union SELECT 'bg5294','해시된 주입 비밀번호값' #
해시된 주입 비밀번호값은 내가 만약 1234로 로그인을 할거라면,
1234를 md5의 규칙대로 해시화한 값인 81DC9BDB52D04DC20036DBD8313ED055 가 들어가야 한다는 뜻이다.
그 값은 구글링을 통해 사이트에서 쉽게 얻어낼 수 있다.
그 후, 로그인 창에서 비밀번호 입력란에 1234를 입력하면 1234가 변수로 지정되고 그 값이 함수 안으로 들어가
순간적으로 데이터베이스에 저장된 해시값 비밀번호와 맞아떨어지면서 로그인 우회가 가능해진다.
직접 개발한 페이지에서 시험해보자
'개발 > 웹 개발' 카테고리의 다른 글
[3주차] 회원가입 페이지 구현 #2 - 아이디 중복 체크 (0) | 2023.04.22 |
---|---|
[4주차] 회원가입 페이지 구현 #1 - 주소 검색 기능 만들기 (3) | 2023.04.21 |
[2주차] 로그인 구현 #2 - 세션을 이용한 PHP 로그인 기능 구현 (0) | 2023.04.11 |
[2주차] 로그인 구현 #1 - 메인 페이지 /로그아웃 기능 구현 (0) | 2023.04.10 |
[1주차] GET / POST 메서드로 웹 서버에 데이터 보내기 (0) | 2023.04.05 |