※ 주의 : 보안 관련 학습은 자유이지만, 학습 내용을 사용한 경우 법적 책임은 행위자에게 있습니다. (Note: Security-related learning is allowed, but the individual is solely responsible for any legal consequences arising from the use of the acquired knowledge.)
※ 공부 기록용 포스트이며, 여기서 사용하는 리눅스 이미지는 실습을 하기 위한 기본적인 패키지 및 파일이 설치되어 있으므로, 따라 하셔도 안 되는 부분이 있습니다. ( 이미지 파일 제공하지 않음)
※ 보안에 매우 취약한 코드이므로 사용하시면 안 됩니다. (SQL인젝션 등..)
[보안 공부] 2주 차 - 데이터베이스 및 SQL, 회원가입 페이지 만들고 DB연동
1. 개념
● 데이터베이스(Database, DB)
→ 단어 그대로 데이터 저장소. DB가 개념이라면, DBMS(Database Management system)는 소프트웨어로, 대표적인 DBMS는 OracleDB, MSSQL, MySQL, MariaDB, MongoDB 등이 있다.
→ 왜 DB를 쓰는가?에 대해 의문이 생긴다면, 상반되는 개념인 파일 처리 시스템(file processing system)과 비교하면 된다. 필자도 자세히 아는 것은 아니라 간단하게 설명하면, 파일 처리 시스템의 데이터는 프로그램에 종속적이다.(핵심) 무슨 말인가 하면 면, 상품 이름, 정보, 가격 등을 적은 데이터 리스트가 있다고 할 때, 나는 엑셀(.xlsx)로 저장했는데, 상대방에게는 엑셀이 없다. 그러면 데이터를 열 수가 없다. 이것이 데이터가 응용프로그램(여기서는 엑셀)에 종속적인 예이다. 프로그램에 종속적이라면 데이터가 중복이 될 수도 있고, 데이터의 일관성, 무결성 유지가 어렵다. 그래서 DB개념이 나오게 된 것이다.(하지만, DB도 만능은 아니다.)
→ '데이터 베이스 '라는 용어는 1963년 미국 SDC사의 제1차 심포지엄에서 '공식적으로' 처음 사용 하였다.
→ 최초의 관계형 데이터베이스 관리 시스템은? System R이다. IBM의 San Jose 연구소에서 1974년에 개발된 프로토타입이었다.
● DB용어(기초)
→ 데이터베이스 스키마(Database Schema) : 데이터베이스의 자료구조, 표현 방법, 자료 간의 관계 형식을 정의한 것 데이터베이스의 설계도 정도로 이해하면 좋을 것 같다. 설계단에서 꼭 필요함.
→ 테이블(Table), 행(Row), 열(Column), 릴레이션(relation), 튜플(Tuple), 속성(attibute), Cardinality 등.. 그림 한 번에 정리
→ 행(Row), 열(Column)은 가로,세로 느낌이라면, 튜플은 관계형 데이터베이스에서 행을 의미한다.
→ 테이블(Table)은 가로 세로가 있는 2차원 표라고 생각하면 되고, 관계형 데이터베이스에서는 릴레이션으로 표현한다.
데이터베이스 테이블(릴레이션) 관련 용어
● SQL(Structured Query Language)
→ 관계형 데이터베이스에서(현존하는 DB는 대부분 관계형 데이터베이스이며, 관계형 데이터베이스가 아닌 DB로는 대표적으로 mongoDB가 있다.) , 데이터를 추출하기 위한 질의 문 언어(문법).
→ 기능으로 분류하면 일반적으로 DDL, DML, DCL, TCL로 분류한다. → DDL (Data Definition Language) : 예) CREATE, ALTER, DROP 등
→ DML (Data Manipulation Language) : 예) SELECT, INSERT, UPDATE, DELETE 등
→ DCL (Data Control Language) : 예) GRANT, REVOKE 등
→ TCL (Transaction Control Language) : 예) COMMIT, ROLLBACK, SAVEPOINT 등
2. 준비
1주 차에 했었던 것에서 이어집니다.
● 프론트앤드를.. 준비해와야 합니다.. ▶ 알아서 공부해오기.. 저도 잘 몰라서 GPT 도움을 많이 받고 있습니다..
3. 실행
● phpmyadmin 실행, DB와 Table생성
→ 도커에 설치되어 있으므로 http://192.168.248.128:1018/phpmyadmin/ 로 접속하면 됩니다.
→ ID : admin PW: student1234
phpMyAdmin 로그인
→ 새로운 DB를 만듭니다.
→ 구조는, 'DB' 밑 단에 여러 '테이블'이 존재합니다.
→ 수업내용에 따라 student라는 DB를 만들고 score라는 테이블을 만들겠습니다.
새로운 DB 생성
→ 만약 잘못 만들었다면 SQL문 DROP으로 DB를 삭제할 수 있습니다.
→ DROP은 DDL으로, DB나 Table을 삭제하는 명령어입니다.
→ Table안의 튜플을 삭제하는 것은 DELETE(DML)입니다.
→ DROP과 DELETE를 착각하면 위험한 결과를 초래할 수 있으니 구분을 해야 합니다.
→ ' 집' 안에 가구 하나를 삭제하는 것과, '집'을 삭제하는 것은 매우 큰 차이입니다.
DB를 삭제하는 방법
student DB 아래에 score이라는 테이블을 만듭니다.
→ 만들고 싶은 칼럼 (속성(Attribute))의 이름과 종류, 값, 기본키 여부, AI(Auto Increment - 자동증가) 체크를 아래와 같이 설정합니다. 수업에서는 score가 varchar타입이었지만 점수라서 int형이 더 어울려 보여서 int로 설정하였습니다.
→ 데이터 삽입하기
→ insert into score (name, score, pass) values ('kim', 62, 'yes');
→ SQL 명 령어는 대소문자(심지어 섞어 써도) 상관이 없다. 데이터 타입이 varchar인 경우 따옴표 ' '를 사용해야 한다. idx속성 같은 경우에는 A.I(Auto Increment) 옵션을 선택했기 때문에 입력하지 않으면 자동으로 넣어준다.
SQL insert로 데이터 삽입하기.
성공적으로 데이터를 삽입 했다고 나온다.
이렇게 테이블 형태로도 확인이 가능하다.
→ 이제 Select문을 사용해 보겠습니다.
→ select * from score where pass='yes';
→ 의미는 score 테이블에서 pass 속성에 yes로 되어있는 모든 튜플을 가져오라는 뜻입니다.
select 실행 결과 pass='yes'인 튜플만 잘 나온다.
→ where 절에는 and, or 사용이 가능합니다.
→ select * from score where pass='yes' and name='kim';
→ pass 속성이 yes이면서(and) name속성이 kim인 튜플을 불러올 수 있습니다.
● WAS와 DB연동하기 (php로 DB호출하기)
→ WAS와 DB를 연동시키는 작업입니다.
→ php코드에 DB관리자 계정 ID와 PW를 넣어주어야 합니다.
→ dbloginx.php라는 페이지를 새로 만들겠습니다.
dbloginx.php 코드
더보기
<!-- dbloginx.php -->
<?php
$db_conn = mysqli_connect ( 'localhost' , 'admin' , 'student1234' , 'student' );
//db연결 결과를 변수로 담기. db로그인 id, 암호, db명
if ( $db_conn ){
echo "DB Connect success" ;
//echo $db_conn; 무슨 값인지 보려고 했으나 표시가 잘 안됨.
}
else {
echo "DB Connect fail" ;
}
$sql = "select * from score" ;
//실행할 sql문을 변수에 넣기
$result = mysqli_query ( $db_conn , $sql );
//mysqli_query 함수 이용하여 sql문 실행
$row = mysqli_fetch_array ( $result );
//맨 위 1줄만 가져온다.
var_dump ( $result );
//sql 결과를 표시
? >
→ 실행결과
kim이 맨 윗줄에 있었나 보다.
● 과제1 - DB에 있는 doldol의 점수를 출력하기 (URL에 드러나는 GET방식 사용)
→ PHP기초가 부족해서 힘들었습니다.
→ dbtest.php와 그다음 페이지인 detest2.php를 작성했습니다.
dbtest.php
더보기
<!-- dbtest.php -->
<!DOCTYPE html >
<html lang = "ko" >
<head>
<meta charset = "UTF-8" >
<meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
<title> 사용자 점수 확인 </title>
<link href = "./style.css" rel = "stylesheet" type = "text/css" />
</head>
<body>
<div class = "contents-container" style = " width:300px; background-color:#ddfcff;" >
<h2> 사용자 점수 확인 페이지 </h2>
<form action = "dbtest2.php?user_name= <?php echo $_GET [ 'user_name' ]; ? > " >
<label for = "user_name" > 확인 할 사용자 이름 입력: </label>
<br> <p> ex. kim, lee, park, doldol </p>
<input type = "text" id = "user_name" name = "user_name" required ><br><br>
<button type = "submit" > 확인 </button>
</form>
</div>
</body>
detest2.php
더보기
<!--dbtest2.php-->
<?php
$db_conn = mysqli_connect ( 'localhost' , 'admin' , 'student1234' , 'student' );
//db연결 결과를 변수로 담기. db로그인 id, 암호, db명
if ( $db_conn ){
echo '<script>alert("DB로그인 성공!");</script>' ;
//echo $db_conn; 무슨 값인지 보려고 했으나 표시가 잘 안됨.
//header("Location: dblogin3.php");
}
else {
echo '<script>alert("DB로그인 실패!");</script>' ;
//echo "DB Connect fail";
//header("location:dblogin.php");
exit ;
}
$user_name = $_GET [ 'user_name' ];
$sql = "select * from score where name=' $user_name '" ;
$result = mysqli_query ( $db_conn , $sql );
//mysqli_query 함수를 이용하여 sql문 실행
//이렇게 실행하면 객체로 반환하기 때문에 행만 출력하려면 밑에 mysqli_fetch_arry()함수가 필요하다.
$row = mysqli_fetch_array ( $result );
//맨 위 1줄만 가져온다.
? >
<html>
<heda>
<meta charset = "UTF-8" >
<meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
<title> DB결과 페이지 </title>
<link href = "./style.css" rel = "stylesheet" type = "text/css" />
</head>
<body>
<div class = "contents-container" style = " width:300px; background-color:#ddfcff;" >
<h2> DB 결과 출력 </h2>
<?php
echo " $user_name 의 score값 : " . $row [ 'score' ]; // score 값만 표시
var_dump ( $row ); // 전체 표시
? >
</div>
</body>
</html>
style.css
더보기
/* CSS 스타일링 */
.login-container {
width : 300px ;
margin : 100px auto ;
padding : 20px ;
border : 1px solid #ccc ;
border-radius : 5px ;
text-align : center ;
background-color : #f9f9f9 ;
}
h2 {
margin-bottom : 20px ;
}
label {
display : block ;
text-align : left ;
}
input [ type = "text" ],
input [ type = "password" ] {
width : 100% ;
padding : 8px ;
margin-bottom : 10px ;
border : 1px solid #ccc ;
border-radius : 4px ;
box-sizing : border-box ;
}
button {
width : 100% ;
background-color : #4caf50 ;
color : white ;
padding : 10px 0 ;
margin-top : 10px ;
border : none ;
border-radius : 4px ;
cursor : pointer ;
}
button.red {
width : 100% ;
background-color : hsl ( 0 , 100% , 50% );
color : white ;
padding : 10px 0 ;
margin-top : 10px ;
border : none ;
border-radius : 4px ;
cursor : pointer ;
}
button.small {
width : 35% ;
background-color : hwb ( 199 19% 0% );
color : white ;
padding : 10px 0 ;
margin-top : 10px ;
border : none ;
border-radius : 4px ;
cursor : pointer ;
}
button:hover {
background-color : #45a049 ;
}
button.small:hover {
background-color : #00aefffd ;
}
button :hover_red {
background-color : hsla ( 0 , 100% , 72% , 0.884 );
}
.links {
margin-top : 20px ;
font-size : 14px ;
}
.sign_in-container {
width : 400px ;
margin : 100px auto ;
padding : 35px ;
border : 2px solid #ccc ;
border-radius : 5px ;
text-align : center ;
background-color : #e9f9f9 ;
}
.contents-container {
width : 600px ;
margin : 100px auto ;
padding : 20px ;
border : 1px solid #ccc ;
border-radius : 5px ;
text-align : center ;
background-color : hwb ( 135 71% 0% );
}
.container {
width : 400px ;
margin : 100px auto ;
padding : 20px ;
border : 1px solid #ccc ;
border-radius : 5px ;
text-align : center ;
background-color : #dfdfdf ;
}
doldol을 입력해 줍니다.
doldol의 score 값이 나왔습니다!
● 과제2 - 로그인 후 비밀번호 변경 기능 넣기 (미완성입니다. --> 해결 완료.)
→ 일단 점점 기능을 붙이다보니 만든 저도 헷갈리기 시작해서 순서도를 만들어 보았습니다.
순서도
→ 코드
login5.php
더보기
<!-- login5.php 로그인 DB연동 구현-->
<!DOCTYPE html >
<html lang = "ko" >
<head>
<meta charset = "UTF-8" >
<meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
<title> 로그인 페이지 </title>
<link href = "./style.css" rel = "stylesheet" type = "text/css" />
</head>
<body>
<div class = "login-container" >
<h2> Login 5 </h2>
<form action = " <?php echo htmlspecialchars ( $_SERVER [ "PHP_SELF" ]); ? > " method = "post" >
<label for = "userid" > ID: </label>
<input type = "text" id = "userid" name = "userid" required ><br><br>
<label for = "password" > PW: </label>
<input type = "password" id = "password" name = "password" required ><br><br>
<button type = "submit" > 로그인 </button>
</form>
<div class = "links" >
<button class = "small" onclick = " alert ( '해당 기능은 구현되지 않았습니다.' )" > 아이디 찾기 </button>
<button class = "small" onclick = " alert ( '해당 기능은 구현되지 않았습니다.' )" > 비밀번호 찾기 </button>
<button class = "small" onclick = " window . location . href = 'sign_up.php' " style = " width: 71%;" > 회원 가입 </button> <!--회원가입 페이지 연결 해야 함.-->
<button class = "small" onclick = " window . location . href = 'phpmyadmin' " style = " width: 50%; background-color: #ff8b5d; color: black;" onmouseover = " this. style . backgroundColor = '#ff6347' " onmouseout = " this. style . backgroundColor = '#ff8b5d' " > (관리자 전용)DB 연결 </button>
</div><br>
<div id = "server-time" ></div>
</div>
<?php
// DB 연결 정보
$servername = "localhost" ; // MySQL 서버 주소
$username = "admin" ; // MySQL 사용자 이름
$password = "student1234" ; // MySQL 비밀번호
$dbname = "user_db" ; // 사용할 데이터베이스 이름
// MySQL 서버에 연결
$conn = new mysqli ( $servername , $username , $password , $dbname );
// 연결 확인
if ( $conn -> connect_error ) {
die ( "DB 연결 실패: " . $conn -> connect_error );
}
// 로그인 처리
if ( $_SERVER [ "REQUEST_METHOD" ] == "POST" ) {
// 폼에서 제출된 사용자 이름과 비밀번호 가져오기
$userid = $_POST [ 'userid' ];
$password = $_POST [ 'password' ];
// 사용자 정보를 쿼리하여 가져오는 SQL 문
$sql = " SELECT * FROM user_table WHERE userid = ' $userid ' AND password = ' $password ' " ;
$result = $conn -> query ( $sql );
// 사용자 정보가 존재하는지 확인
if ( $result -> num_rows > 0 ) {
// 로그인 성공 시 다음 페이지로 리다이렉트
header ( "Location: login_success.php?userid=" . urlencode ( $username ));
exit (); // 리다이렉트 후 추가 코드 실행 방지
} else {
// 로그인 실패 시 JavaScript 코드를 출력하여 alert 창을 띄웁니다.
echo '<script>alert("로그인 실패! 사용자 이름 또는 비밀번호가 잘못되었습니다.");</script>' ;
}
}
// MySQL 연결 종료
$conn -> close ();
? >
<script>
// 실시간 서버 시간 표시
setInterval ( function () {
var currentTime = new Date ();
var year = currentTime . getFullYear (); // 년도
var month = currentTime . getMonth () + 1 ; // 월 (0부터 시작하기 때문에 +1)
var day = currentTime . getDate (); // 일
var hours = currentTime . getHours (); // 시
var minutes = currentTime . getMinutes (); // 분
var seconds = currentTime . getSeconds (); // 초
// 년, 월, 일, 시, 분, 초를 두 자리로 만들기 위해 10 미만인 경우 앞에 0을 추가
month = month < 10 ? '0' + month : month ;
day = day < 10 ? '0' + day : day ;
hours = hours < 10 ? '0' + hours : hours ;
minutes = minutes < 10 ? '0' + minutes : minutes ;
seconds = seconds < 10 ? '0' + seconds : seconds ;
var timeString = year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds ;
document . getElementById ( "server-time" ). innerHTML = "현재 서버 시간: " + timeString ;
}, 200 ); // 200ms 마다 업데이트
</script>
</body>
</html>
login_success.php
더보기
<!-- login_success.php -->
<!DOCTYPE html >
<html lang = "ko" >
<head>
<meta charset = "UTF-8" >
<meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
<title> 로그인 성공 후 페이지 </title>
<link href = "style.css" rel = "stylesheet" type = "text/css" />
</head>
<body>
<div class = "container" sytle = "width:400px" ; >
<h2> 로그인 성공 후 페이지 입니다. </h2>
<?php
// 만약 사용자의 아이디가 있으면 환영 메시지를 출력합니다.
if ( isset ( $_GET [ 'userid' ])){
$username = $_GET [ 'userid' ]; // 사용자의 아이디를 가져옵니다.
echo "<h3>환영합니다, $username 님</h3>" ; // 사용자의 아이디를 환영 메시지에 출력합니다.
}
? >
<h2> 내 정보 수정 </h2>
<li><a href = "./login_success_myaccount.php?userid= <?php echo $_GET [ 'userid' ]; ? > " > 내 정보 수정하기 </a></li>
</form>
</div>
</body>
</html>
login_success_myaccount.php (DB연동 되지 않던 문제 해결완료)
더보기
<!--login_success_myaccount.php-->
<!DOCTYPE html >
<html lang = "ko" >
<head>
<meta charset = "UTF-8" >
<meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
<title> 비밀번호 변경하기 </title>
<link href = "style.css" rel = "stylesheet" type = "text/css" />
</head>
<body>
<div class = "container" ; >
<h2> 비밀번호 변경 </h2>
<?php
// 만약 사용자의 아이디가 있으면 환영 메시지를 출력합니다.
if ( isset ( $_GET [ 'userid' ])){
$username = $_GET [ 'userid' ]; // 사용자의 아이디를 가져옵니다.
echo "<h3>환영합니다, $username 님</h3>" ; // 사용자의 아이디를 환영 메시지에 출력합니다.
}
? >
<form action = "changepw.php" method = "post" >
<input type = "hidden" name = "userid" value = " <?php echo $_GET [ 'userid' ]; ? > " > <!-- 사용자의 아이디를 전달하는 hidden input -->
<label for = "new_password" > 새로운 비밀번호: </label>
<input type = "password" id = "new_password" name = "new_password" required ><br><br>
<input type = "submit" name = "update" value = "정보 업데이트" >
</form>
</div>
</body>
</html>
changepw.php
더보기
<!--changepw.php-->
<?php
// DB 연결 정보
$servername = "localhost" ; // MySQL 서버 주소
$username = "admin" ; // MySQL 사용자 이름
$password = "student1234" ; // MySQL 비밀번호
$dbname = "user_db" ; // 사용할 데이터베이스 이름
// MySQL 서버에 연결
$conn = new mysqli ( $servername , $username , $password , $dbname );
// 연결 확인
if ( $conn -> connect_error ) {
die ( "DB 연결 실패: " . $conn -> connect_error );
}
$userid = $_POST [ 'userid' ];
// 사용자 정보 수정 처리
if ( $_SERVER [ "REQUEST_METHOD" ] == "POST" && isset ( $_POST [ 'update' ])) {
// 폼에서 제출된 사용자 정보 가져오기
$new_password = $_POST [ 'new_password' ];
// 추가적인 출력문 추가 변수를 제대로 받았는지 확인용
//echo "새로운 비밀번호: " . $new_password . "<br>";
//echo "사용자 ID: " . $userid . "<br>";
// 사용자 정보를 업데이트하는 SQL 문
$update_sql = " UPDATE `user_table` SET `password` = ' $new_password ' WHERE `userid` = ' $userid ' " ;
if ( $conn -> query ( $update_sql ) === TRUE ) {
// 정보가 성공적으로 업데이트되면 메시지를 출력합니다.
echo '<script>alert("정보가 성공적으로 업데이트되었습니다.");</script>' ;
header ( "refresh:2;url=login5.php" ); // 2초 뒤 로그인페이지로
} else {
// 업데이트 중 오류가 발생하면 오류 메시지를 출력합니다.
echo "오류: " . $conn -> error ;
}
}
sign_up.php
더보기
<!--sign_up.php-->
<!DOCTYPE html >
<html lang = "ko" >
<head>
<meta charset = "UTF-8" >
<meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
<title> 로그인 페이지 </title>
<link href = "./style.css" rel = "stylesheet" type = "text/css" />
</head>
<body>
<div class = "sign_in-container" >
<h2> Sign up 회원가입 </h2>
<h3> 테스트 페이지이므로 암호화 되지 않습니다. </h3>
<form action = "./sign_up_register.php" method = "post" accept-charset = "UTF-8" >
<label for = "username" > 이름: (한글 3~5글자) </label>
<input type = "text" id = "username" name = "username" required ><br><br>
<label for = "userid" > ID: (영문 10자 이하) </label>
<input type = "text" id = "userid" name = "userid" required ><br><br>
<label for = "password" > PW: </label>
<input type = "password" id = "password" name = "password" required ><br><br>
<label for = "password" > 휴대폰번호: 숫자만입력 ex)010123456789 </label>
<input type = "text" id = "userphone" name = "userphone" required ><br><br>
<button type = "submit" > submit 회원가입 신청 </button>
</form>
</div>
</body>
</html>
sign_up_register.php (받은 회원정보를 실제 DB에 넣는 작업 코드)
더보기
<!-- sign_up_register.php -->
<?php
// 데이터베이스 연결 정보
$servername = "localhost" ;
$username = "admin" ;
$password = "student1234" ;
$dbname = "user_db" ;
// 데이터베이스 연결
$conn = new mysqli ( $servername , $username , $password , $dbname );
$conn -> set_charset ( "utf8mb4" ); // DB에서 한글깨짐 현상 해결
// 연결 확인
if ( $conn -> connect_error ) {
die ( "연결 실패: " . $conn -> connect_error );
}
// 폼에서 받은 데이터 저장
$username = $_POST [ 'username' ];
$userid = $_POST [ 'userid' ];
$password = $_POST [ 'password' ];
$userphone = $_POST [ 'userphone' ];
// 중복 확인을 위한 SQL 쿼리 작성
$check_query = " SELECT * FROM user_table WHERE userid = ' $userid ' OR userphone = ' $userphone ' " ;
$result = $conn -> query ( $check_query );
// 결과 확인
if ( $result -> num_rows > 0 ) {
// 중복된 userid나 userphone이 존재할 경우
while ( $row = $result -> fetch_assoc ()) {
if ( $row [ 'userid' ] == $userid ) {
echo "이미 사용 중인 userid입니다." ;
}
if ( $row [ 'userphone' ] == $userphone ) {
echo "이미 사용 중인 휴대폰 번호입니다." ;
}
}
} else {
// 중복이 없을 경우, 데이터베이스에 저장할 수 있습니다.
$sql = " INSERT INTO user_table (username, userid, password , userphone) VALUES (' $username ', ' $userid ', ' $password ', ' $userphone ') " ;
if ( $conn -> query ( $sql ) === TRUE ) {
echo "회원가입 성공! 2초 뒤 로그인 페이지로 이동합니다." ;
header ( "refresh:2;url=login5.php" );
} else {
echo "에러:" . $sql . "<br>" . $conn -> error ;
}
}
// 데이터베이스 연결 종료
$conn -> close ();
? >
4. 평가
→ 여전히 프론트앤드에서 고전하고 있습니다! 아마.. 12시간은 넘게 한 것 같습니다.