회사에서는 우분투, 집에서는 칼리로 계속 실습 중.
첫번째 주부터 여행으로 놓치니 초보인 내가 따라가기가 쉽지 않다.. 기록용으로 정리!
이 글은 “내가 왜 이렇게 썼는지”까지 다 적은 복습노트의 개념이다
만들 기능은 기본 로그인 페이지
- login.php
- 아이디/비밀번호를 입력할 수 있는 화면(폼)
- 사용자가 로그인 버튼을 누르면 데이터를 login_proc.php로 보냄
- 로그인 실패 시 에러 메시지를 화면에 띄움 (동적)
- login_proc.php
- 실제로 아이디/비밀번호를 받아서 검사하는 PHP 로직
- 정상 접근인지 확인하고 (POST만 허용)
- 성공이면 main.php로 보내고, 실패면 다시 login.php로 돌려보냄
- main.php
- 로그인 성공했을 때 가는 페이지 (힘들어서 예쁘게 안꾸밈...)
- style.css
- 화면을 예쁘게(배경 어둡게, 박스 가운데 놓고, 빨간 에러 메시지 등)
이 네 개 파일이 /var/www/html/weblogin 폴더 안에 있다는 전제
Apache가 돌아가고 있다면 브라우저에서
http://localhost/weblogin/login.php
로 접속해서 테스트할 수 있음
(https는 아직 안 된다. 그 얘기는 뒤에서 다시 다룰 것)
- 앞으로 작업 위치: /var/www/html
- 그 아래에 weblogin이라는 디렉토리를 하나 만들었다!
- 왜 sudo/root?
/var/www/html은 웹서버 문서 루트라 일반 유저 권한으로 수정 안 될 때가 많음..
저장이 안 되거나 "Permission denied" 또는 갑자기 코드 날아가면 멘탈 나감.. 걍 root 또는 sudo로 편하게 하자.. - Apache 기본 설정에서는 /var/www/html 아래에 있는 파일들이 http://localhost/... 로 감.
- vi/vim에서 한글 입력 미리 세팅해두는 게 정신건강에 이로움..(+VM, vim, 호스트OS 간 클립보드 허용도..)
(안 하면 한글이 입력 안되거나 깨짐. “vim 한글 입력” 검색해서 본인 환경에 맞게 설정)
최종적으로 이 디렉토리 안에 만들 파일은:
- login.php
- login_proc.php
- main.php
- style.css
1. login.php

이 파일은 "사용자에게 보여지는 화면"임
아이디/비밀번호를 입력하는 폼이 여기 있음
그리고 중요한 포인트: 에러 메시지가 "조건부로" 나타남
전체 코드는 이렇게 생김:
아래부터 줄별로 씹고 뜯고 맛봐보자.
- <!DOCTYPE html>: 그냥 HTML5 문서라고 선언하는 약속. 안 쓰면 일부 브라우저에서 예전 모드(호환 모드)로 랜더링할 수도 있어서 항상 넣는다고 보면 됨. 사실상 "그냥 제일 위에 항상 이거 쓰는 거"라고 외우자
- <html>: 페이지 전체를 감싸는 최상위 태그.
- lang="ko": 이 문서의 기본 언어는 한국어라고 알리는 속성.
- 화면에 직접 보이진 않지만 중요함.
- 스크린 리더(시각장애인용 읽기 도구)가 이걸 보고 한국어 발음으로 읽는다.
- 브라우저 번역기나 검색엔진도 이 정보를 사용한다.
즉 <html lang="ko"> = "이 페이지는 한국어 페이지야."
<head> 안은 실제 화면에 표시되는 내용은 아니고, 페이지 설정 정보들이다.
- <meta charset="UTF-8">
→ 이 문서는 UTF-8 인코딩이다!
→ 이걸 선언 안 하면 한글이 깨질 수도 있음.
→ 한글 쓰는 사이트는 이거 없으면 거의 100% 깨진다고 보면 된다고 함 - <meta name="viewport" content="width=device-width, initial-scale=1.0">
→ 모바일에서 페이지가 너무 확대/축소돼서 깨져 보이지 않게 해준다.(애플이 생기면서 생긴거라고..?)
→ "화면 너비를 기기 화면 너비에 맞춰줘" 이런 의미. - <title>로그인 페이지</title>
→ 브라우저 탭 제목에 보이는 글자. - <link rel="stylesheet" href="style.css">
→ CSS 파일 연결.
→ href="style.css" 라고 썼으니까 이 login.php와 같은 디렉토리(weblogin) 안의 style.css를 읽어온다.
여기서 rel="stylesheet" 는 "이 링크는 스타일시트(디자인 정보)다"라는 의미.
href="..." 는 "어디 파일에서 그걸 가져올지"를 알려준다.
- <body>: 이제부터 화면에 실제로 보일 내용이 시작된다는 뜻.
- class="login-body":
- body 태그에 클래스를 하나 달아준 것.
- CSS에서 .login-body { ... } 로 body 전체 스타일(배경색, 정렬 등)을 제어할 수 있다.
여기서 중요한 포인트
→ <body class="login-body"> 는 <body>랑 다르게 보이는 게 아니라, <body> 시작 + “추가 속성(class)”을 준 것.
HTML 태그는 <태그명 속성="값" 속성="값"> 이런 식으로 여는 게 기본 문법이다.
- <div> = "구역을 나눈다" / "박스를 하나 만든다" 정도로 이해하면 편함.
- class="login-container" 라고 이름 붙이면 CSS에서 이 부분만 따로 꾸밀 수 있다.
- 이 login-container는 화면 가운데 떠 있는 로그인 박스 그 자체다.
- width, 배경색, 보더, 그림자 등을 style.css에서 지정해줄 거다.
안쪽에 또 이런 게 들어있다:
- login-header 라는 또 다른 클래스로 감싸서
- 큰 제목(h1)
- 안내문(p)
를 묶어둔 것.
- 왜 이렇게 또 나눔?
- 제목 부분만 가운데 정렬하고, 글자 크기 다르게 하고, 아래쪽 여백 주고... 이런 스타일을 주고 싶기 때문.
- 구조를 쪼개두면 CSS에서 제어가 훨씬 깔끔해진다.

이 부분이 솔직히 제일 헷갈렸는데, 이제 이해했다.
이게 하는 일은 딱 하나다:
로그인에 실패해서 다시 돌아온 경우에만 에러 메시지를 보여줘.
자세히 보면:
- $_GET['error']
→ URL의 쿼리스트링(?error=1)에서 넘어온 값.
예: login.php?error=1 이라고 접속하면 $_GET['error'] 는 1이 된다. - isset($_GET['error'])
→ error라는 값이 URL에 실제로 존재하는지 체크.
값이 없는데 바로 $_GET['error']를 읽으면 경고 뜰 수 있으니까 방어용으로 씀. - $_GET['error'] == '1'
→ 그 값이 '1'인지 확인. - &&
→ 그리고 (AND).
→ 즉 둘 다 참이어야 전체가 참.
그러니까
if (isset($_GET['error']) && $_GET['error'] == '1') {
라는 건 이런 뜻이다:
"URL에 error=1 이라고 붙어서 이 페이지에 들어왔다면,
아래 HTML(error-message div)을 출력하자."
그럼 <div class="error-message">아이디 또는 비밀번호가 올바르지 않습니다.</div> 이 부분이 실제로 페이지에 찍혀서 빨간 경고 박스로 나타난다.
만약 그냥 처음 접속해서 login.php만 열었다면 ?error=1이 없으니까 저 div 자체가 아예 출력 안 된다.
= 깔끔한 화면.
여기서 가장 헷갈렸던 문법:
<?php if (...) { ?>
<div>...</div>
<?php } ?>
왜 PHP 열었다 닫았다 다시 열었다 닫냐?
- <?php if (...) { ?>
- PHP 모드로 들어가서 if문을 연다. { 까지 쓴다.
- 그리고 ?> 로 PHP를 잠깐 “일시정지”한다.
- 그 다음에 일반 HTML을 쓴다.
- 마지막에 <?php } ?>
- 다시 PHP 모드로 돌아와서 if문의 }를 닫는다.
즉 "조건부로 HTML 블럭을 넣고 싶어서" PHP를 잠깐 끊고 다시 켠 거다.
중요한 포인트:
- <form ... method="POST" action="login_proc.php">
→ 사용자가 로그인 버튼을 누르면
이 <form> 안의 값들을
POST 방식으로
login_proc.php한테 보낸다. - <input ... name="username">
여기서 name="username" 이 핵심
서버(login_proc.php)에서는 이 값이 $_POST['username'] 으로 들어온다.
즉 "username이라는 이름의 상자에 담아서 보내겠다"라고 이해하면 편함. - <input type="password">
브라우저 화면에서 입력한 글자를 ****처럼 가려준다.
(서버로 갈 때는 실제 값이 그대로 간다. 단순히 화면 표시용 마스킹이다.) - <button type="submit">로그인</button>
이 버튼을 누르면 form이 전송된다.
여기까지가 login.php.
말하자면 “로그인 폼 + (실패 시) 에러메시지 표시용 페이지”다.

2. login_proc.php(얘는 실제 보이는 화면이 아님)

"로그인 시도 결과를 판단하고, 어딜로 보낼지 결정하는 애"다.
즉 직접 보는 페이지가 아니라, 중간 심판 같은 역할.
전체 코드는 이렇게 썼다:
한 줄씩 해부해보자.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- $_SERVER 는 PHP가 자동으로 채워주는 "서버/요청 정보" 모음(슈퍼글로벌).
- $_SERVER['REQUEST_METHOD'] 는 이 파일이 어떤 방식으로 호출되었는지 알려준다.
예: GET, POST 등. - 우리는 <form method="POST" ...> 로 login_proc.php를 호출할 예정.
→ 정상 루트라면 무조건 POST여야 한다. - 그래서 === 'POST'
→ "정확하게 POST일 때만" 이라는 의미.
=== 는 "값 + 타입까지 완전히 같다"는 엄격 비교 연산자.
결국 이 줄은:
"이 파일은 POST로 불린 경우(폼 제출을 통해 정상적으로 온 경우)에만 처리 계속하겠다.
만약 누가 주소창으로 직접 login_proc.php를 치면 GET이니까 else로 빠질 거다."
이걸로 이상한 접근(우회 접근)을 1차 차단한다.
$username = isset($_POST['username']) ? trim($_POST['username']) : '';
$password = isset($_POST['password']) ? $_POST['password'] : '';
여기가 중요하고 어려움..
- $_POST 는 <form method="POST"> 로 넘어온 값들이 들어 있는 전역 배열이다.
- $_POST['username'] → 로그인 폼의 아이디 입력값
- $_POST['password'] → 로그인 폼의 비밀번호 입력값
- isset($_POST['username'])
→ 저 값이 실제로 존재하는지 확인.
(안 그러면 값이 없을 때 바로 꺼내다가 "undefined index" 같은 경고 날 수 있다.) - 삼항 연산자 조건 ? A : B
- 형식은 X ? Y : Z
- 의미: X가 참이면 Y, 거짓이면 Z를 결과로 써라.
- 여기선 “username이 존재하면 trim(….) 해서 넣고, 아니면 빈 문자열 넣어라”.
- trim($_POST['username'])
- 아이디 앞뒤 공백 제거.
예: " admin " → "admin" - 사용자가 실수로 스페이스 넣은 거 때문에 로그인이 안 되는 경우를 줄여준다.
- 아이디 앞뒤 공백 제거.
- password 쪽은 trim()을 안 했다.
이유: 비밀번호는 공백 자체도 실제 값으로 취급하고 싶을 수 있기 때문.
"1234" 와 "1234 " 는 다르다고 보고 싶은 경우가 많다.
정리하면:
- $username에는 "아이디 입력값(앞뒤 공백 제거 버전)"이 들어간다.
- $password에는 "비번 입력값(있는 그대로)"이 들어간다.
- 만약 뭔가 이상해서 값이 안 넘어왔다면 그냥 빈 문자열이 들어가게 해서 에러를 막는다.
if ($username === 'admin' && $password === '1234') {
- $username === 'admin'
- $password === '1234'
이 코드에서는 id, pw가 하드코딩되어 있다.
진짜 서비스라면 DB에서 사용자 정보를 가져와서 비교해야 한다.
하지만 이번 과제는 거기까진 아니었어..!
&& 는 AND (그리고).
둘 다 참이어야 if 안으로 들어간다.
즉:
아이디가 admin이고 비밀번호가 1234면 “로그인 성공”으로 인정해라.
header('Location: main.php');
exit();
- header('Location: main.php');
→ PHP가 브라우저에게 "main.php로 이동해"라고 HTTP 헤더를 보낸다.
즉 리다이렉트 시킨다. - exit();
→ 코드 실행을 여기서 끊는다.
이게 없으면 혹시 아래 이상한 코드가 더 실행될지도 모르니 !
보안적으로도 깔끔하게 끊는 게 좋다고 한다.

main.php는 로그인 성공 후에 보여줄 페이지다.(아직 안꾸밈 ㅜㅜ)
header('Location: login.php?error=1');
exit();
이 줄이 login.php와 완전히 연결된다.
- 실패하면 다시 login.php로 돌려보내는데, 그냥 보내지 않고 ?error=1 을 붙인다.
- 그래서 사용자는 사실상
http://localhost/weblogin/login.php?error=1
로 다시 오게 된다. - login.php 안에서는 $_GET['error'] == '1' 이면 빨간 에러 메시지 div를 찍도록 되어 있다.
즉 "아이디 또는 비밀번호가 올바르지 않습니다."가 뜬다.

→ 여기서 우리는 “정적인 HTML 페이지”가 아니라 “조건에 따라 다른 HTML을 보여주는 동적 페이지”를 만든 거다.
} else {
header('Location: login.php');
exit();
}
- 이건 아까 첫 if의 else에 해당한다.
- 즉, 만약 이 파일이 POST 방식으로 호출된 게 아니라면(=정상적인 폼 제출이 아님),
그냥 login.php로 돌려보낸다.
필요한 이유는
- 누가 브라우저 주소창에 http://localhost/weblogin/login_proc.php 라고 직접 치고 들어올 수도 있음
- 그건 우리가 의도한 흐름이 아님. (폼 안 거쳤으니까)
- 그런 경우를 여기서 걸러주는 것
이렇게 해서 login_proc.php는
"POST로 들어온 로그인 시도만 처리하고, 나머지는 다 튕겨내는 확인자 역할을 함!
참고사항!
1) HTTPS 안 되는 이유
- http://localhost/... 는 잘 되는데
https://localhost/... 는 기본 상태에선 안 된다. - 왜냐면 https는 "그냥 http + s"가 아니고:
- SSL/TLS 인증서
- 443번 포트
- Apache SSL 모듈 설정
이 3가지가 세트여야 한다.
- 우리는 지금 그냥 기본 Apache 설치로 80포트(http)만 열어둔 상태라서 https 요청은 실패하는 게 정상이다.
- 즉 "왜 https 안 돼요?" → 정상임. 아직 https 준비 안 한 거.
2) main.php에 그냥 바로 들어가는 문제
현재 상태에서는 브라우저 주소창에 그냥
http://localhost/weblogin/main.php
라고 치면, 로그인 안 해도 main.php가 열린다.
왜냐면 main.php는 지금 아무 검증이 없기 때문.
"이 사람이 실제로 로그인에 성공한 사용자냐?" 라는 체크가 없다.
그래서 등장하는 개념이 "세션" 인데 이건 아직 안배워서 다음에 다루겠다.
<기타> stlye.css

이 파일이 실제로 화면을 예쁘게 만들어준다.
login.php에서 <link rel="stylesheet" href="style.css"> 로 가져온다.
/var/www/html/weblogin/style.css:
이 CSS에서 중요한 포인트들:
- .login-body
- 배경색을 어둡게 깐다
- display: flex; justify-content: center; align-items: center; min-height: 100vh;
→ 화면 전체에서 가로세로 가운데 정렬 - 즉 로그인 박스가 화면 정중앙에 뜨도록 만드는 핵심
- .login-container
- 실제 로그인 박스(카드)
- 둥근 모서리, 어두운 배경, 그림자
- 폭 고정 (320px)
- .error-message
- 빨간 느낌으로 경고창처럼 표시
- margin-bottom으로 아래 폼과 간격 확보
- .form-group
- label + input 묶음 한 줄
- column 방향으로 세로 정렬
- .login-btn
- 파란색 버튼
- hover시 살짝 색/그림자 변화
이제 할 수 있는 것들 정리하면:
- HTML로 폼 만들고
- CSS로 화면 예쁘게 하고(피티햄이 도와줌..)
- PHP로 POST 데이터를 받고
- PHP에서 조건(if)으로 판정해서
- header('Location: ...') 로 다음 화면을 결정하고
- URL 파라미터에 따라 에러 메시지를 "필요할 때만" 띄운다
다음 과제
- 하드코딩된 ID/PW 대신 DB 연동해서 진짜 계정 확인하기
'해킹스터디' 카테고리의 다른 글
| [Normaltic 웹해킹 입문] 2강 관련 복습&리뷰-②(DB) (0) | 2025.10.29 |
|---|---|
| [Normaltic 웹해킹 입문] 2강 관련 복습&리뷰-① (3) | 2025.10.29 |
| [Normaltic 웹해킹 입문] 1강 주요 과제-APM 웹 개발 환경 세팅(VBox 이용) (1) | 2025.10.24 |
| [Normaltic 웹해킹 입문] 1강 관련 복습&리뷰-② (0) | 2025.10.24 |
| [Normaltic 웹해킹 입문] 1강 관련 복습&리뷰-① (0) | 2025.10.21 |