웹기초_4주차 정리

2022. 7. 28. 17:0922-여름방학/웹 해킹(WEB HACKING)&웹 기초

생활코딩 WEB3 - PHP&MySQL 1강~10강

https://www.youtube.com/playlist?list=PLuHgQVnccGMA5836CvWfieEQy0T0ov6Jh 

 

WEB3 - PHP & MySQL

 

www.youtube.com


1. 수업소개

 

 하나의 글 안에 방문자, 댓글 등 다양한 정보를 저장하고 싶고, 이 정보들을 바탕으로 글 목록의 순서를 정렬하고 필터링 기능을 제공하고 파일의 검색을 가능하게 하기 위해 필요한 데이터베이스 기능을 이 수업에서 배울 것이다. 

파일에 직접 저장했던 데이터를 mysql에 저장하면 놀라운 성등의 웹애플리케이션을 만들 수 있다.


2. PHP와 MySQL의 연동 원리

 

점선의 왼쪽 : 웹브라우저가 설치되어 있는 컴퓨터

점선의 오른쪽 : 서버 컴퓨터

서버에는 웹서버, php, mysql의 3가지의 소프트웨어가 설치되어 있다.

php는 웹과 mysql을 접착하는 프로그램으로 그런 의미에서 미들웨어라고도 부른다.

 

모니터에서 mysql -hlocalhost -uroot -p명령어를 입력해서 mysql을 사용했던 명령어와 mysql_connect();명령어가 똑같은 일을 한다.

여기서 php는 mysql 서버에 대해서 mysql 클라이언트로서 동작한다.

 

글 목록 출력이 아래의 코드들로 가능하다.

사용자가 파일에 접속하면 웹서버에 요청이 가고 웹서버는 그 요청을 자신이 처리할 수 없기 때문에 php에게 전달한다. 그러면 php는 mysql 서버에서 여러 가지 sql문을 전송한다. 

-> php는 mysql 서버에 대해서 클라이언트로서 동작한다. php는 웹서버에 대해서 서버로서 동작한다.


3..1. 수업준비 (웹 쪽)

 

웹쪽으로 준비를 하기위해 index.php라는 새로운 파일을 만든다.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>WEB</title>
  </head>
  <body>
    <h1>WEB</h1>
    <ol>
      <li>HTML</li>
    </ol>
    <h2>Welcome</h2>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit
  </body>
</html>

3.2. 수업준비 (데이터베이스 쪽)

 

우리가 만들 웹애플리케이션의 테이블을 저장할 스키마(데이터베이스)를 만들어야 한다.

CREATE TABLE topic (   
   id int(11) NOT NULL AUTO_INCREMENT,   
   title varchar(45) NOT NULL,   
   description text,   
   created datetime NOT NULL,   
   PRIMARY KEY(id) 
) ENGINE=InnoDB;

위의 코드를 입력한다.

생성일은 datetime이라는 데이터타입을 쓴다.

주 키를 id로 하기 위해 프라이머리로 선언한다.

-> 똑같은 id값을 가지고 있는 행이 추가되려고 하면, 데이터베이스가 거절해서 중복되는 것을 방지할 수 있다.

내부적으로 강력한 색인을 걸기 때문에 id값을 기반으로 해서 데이터를 가져올 때 순식간에 가져올 수 있다.

InnoDB을 쓰면 mysql의 가장 핵심적인 부분이 교체 가능하다.

use opentutorials 명령어를 사용하면 이전에 썼던 명령어를 재입력할 수 있다. 


4. PHP Client로서 MySQL

 

mysql 서버에 실제 데이터가 저장되어 있다.
mysql 모니터에 직접 sql문을 입력하면 mysql 서버에 sql문이 요청된다. 그럼 그것에 대한 결과를 mysql 서버가 모니터에게 응답해준다.
즉, mysql 모니터는 mysql 서버에 대한 클라이언트이다.
mysql 서버에 대한 클라이언트에 mysql 모니터, php, 워크벤치 등이 있다.

5. MySQL API 찾기

 

PDO_MySQL을 쓰면, mysql을 제와한 다른 관계형 데이터베이스(오라클 등등..)를 사용할 때 php쪽의 코드를 바꾸지 않고도 데이터베이스를 교체할 수 있다는 장점이 있다. 그런데 이 방식은 객체를 이용하기 때문에 어렵다는 단점이 있어서 이 수업에서는 mysqli를 사용할 것이다. 

mysqli에서 i는 improved를 의미한다. mysqli는 함수 방식을 사용해서 데이터베이스를 제어한다.


6.1. mysqli_connect

 

<?php
$conn = mysqli_connect("localhost", "root", "111111", "opentutorials");
mysqli_query($conn, "
    INSERT INTO topic (
        title,
        description,
        created
    ) VALUES (
        'MySQL',
        'MySQL is ....',
        NOW()
    )");
?>

로 insert.php파일을 입력한다.

함수형으로 데이터베이스를 사용하기 위한 명령어가 mysqli_connect이다.

호스트에 데이터베이스 서버 주소를 적는다.

 

제네럴 로그를 키고 use opentutorials; 명령어를 입력하면 이전에 실행됐던 작업들이 나타나면서 sql 모니터와 데이터베이스 서버 사이에서 실제로 이루어지고 있었다는 것을 확인할 수 있다.

 

php 애플리케이션에서 리로드를 하면, php 애플리케이션이 mysql 서버에 대해서 클라이언트로서 동작하고 있기 때문에 mysql 모니터로 동작하는 것과 동일하거나 비슷한 작업을 내부적으로 실행하고 있어야 한다.


6.2. mysqli_query

 

데이터베이스에 데이터를 추가시키기 위해서 사용하는 절차식 형식(mysqli_query)의 첫번째 인자로 mysqli의 link가 들어간다.

두번째 인자로 query가 들어가는데 문자열(string)이다.

-> 두번째 인자로 sql문을 주면 php가 클라이언트가 되어서 데이터베이스 서버에 sql문이 실행될 수 있다.


6.3. mysqli_error

 

중간에 문제가 생길 경우 그 문제를 화면에 출력해서 정확히 파악하기 위해서 echo $sql;명령어를 사용한다.

echo mysquli_error($conn); 멸열어를 입력하면 어떤 에러가 있는지 데이터베이스 서버가 알려주는 정보를 php가 출력하는 결과를 볼 수 있다.

-> mysquli_error()를 이용하면 어떤 에러가 발생했을 때 그 에러가 어떤 에러인지에 대한 데이터베이스 서버의 진술을 들을 수 있다.

myquli_query()는 실패했을 때 리턴값이 false이고, show나 describe이나 explain 등 정보를 읽는 것과 관련된 쿼리를 했을 때는 리턴값이 mysquli_result이고, 그 외의 경우에는 true를 리턴한다.

실제로 프로그래밍을 라이브할 때에는 내부적인 시스템의 상황은 공격자에게 중요한 정보를 주지 않기 위해서 외부로 노출시키지 않는 것이 안전하다.


7. 활용-글 생성

 

create를 클릭하면 크레이트.php로 이동하도록 <a>태그를 만든다.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>WEB</title>
  </head>
  <body>
    <h1>WEB</h1>
    <ol>
      <li>HTML</li>
    </ol>
    <a href="create.php">create</a>
    <h2>Welcome</h2>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit
  </body>
</html>

접속하고 있는 데이터베이스 서버가 여러 개이기 때문에 어느 서버에 쿼리를 할 것인가를 적어 주기 위해서 mysqli_query()를 사용한다.

error가 뜨면 어떤 에러가 뜨는지 사용자에게 보여주기 위해서 if문을 사용하고 error_log()를 사용한다. 

insert로 mysqli api를 이용해서 데이터베이스에 데이터를 추가한다.

<?php
$conn = mysqli_connect(
  'localhost',
  'root',
  '111111',
  'opentutorials');
$sql = "
  INSERT INTO topic
    (title, description, created)
    VALUES(
        '{$_POST['title']}',
        '{$_POST['description']}',
        NOW()
    )
";
$result = mysqli_query($conn, $sql);
if($result === false){
  echo '저장하는 과정에서 문제가 생겼습니다. 관리자에게 문의해주세요';
  error_log(mysqli_error($conn));
} else {
  echo '성공했습니다. <a href="index.php">돌아가기</a>';
}
?>

8.1. SELECT 사용법 1

 

<?php
$conn = mysqli_connect(
  'localhost',
  'root',
  '111111',
  'opentutorials');
$sql = "SELECT * FROM topic";
$result = mysqli_query($conn, $sql);
var_dump($result->num_rows);

sql문 중에서 데이터 읽기와 관련된 게 select문이다.

데이터와 연결하기 위해서 connect를 한다.

글 목록을 가져오기 위해서 데이터를 입력한다.  

데이터를 가져오기 위해서 result 변수를 이용한다.

-> mysqli_query()를 통해서 select문을 데이터베이스 서버로 전송할 수 있다.


8.2. SELECT 사용법 2

 

mysqli_query()를 통해서 가져온 데이터를 php에서 사용할 수 있도록 전환해서 가져오기 위해 mysqli_fetch_array()를 사용한다.

<?php
$conn = mysqli_connect(
  'localhost',
  'root',
  '111111',
  'opentutorials');
$sql = "SELECT * FROM topic WHERE id = 19";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_array($result);
echo '<h1>'.$row['title'].'</h1>';
echo $row['description'];

var_dump()나 print_r()를 이용해서 안에 들어오는 변수가 무엇인지 출력해서 확인할 수 있다. 

컬럼의 이름을 통해서 데이터를 가져오는 형식의 배열을 연관 배열이라고 한다.

숫자(자릿수, 인덱스)를 통해서 가져오는 형태의 배열을 배열이라고 한다.

-> 연관 배열을 사용하면 자릿수를 몰라도 컬럼 이름으로 사용할 수 있다.


8.3. SELECT 사용법 3

 

<?php
$conn = mysqli_connect(
  'localhost',
  'root',
  '111111',
  'opentutorials');
echo "<h1>single row</h1>";
$sql = "SELECT * FROM topic WHERE id = 19";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_array($result);
echo '<h2>'.$row['title'].'</h2>';
echo $row['description'];
echo "<h1>multi row</h1>";
$sql = "SELECT * FROM topic";
$result = mysqli_query($conn, $sql);
while($row = mysqli_fetch_array($result)) {
  echo '<h2>'.$row['title'].'</h2>';
  echo $row['description'];
}

데이터를 가져오려고 했으나 더 이상 가져올 데이터가 없으면 mysqli_fetch_array()는 아무것도 하지 않는다.

코드에서 반복적인 내용은 while 반복문을 사용하는 게 좋다.

반복해서 실행했던 코드들을 지우고 반복문을 사용한다.

반복문을 이용하면 첫 번째 행에서 실행하고 row title을 실행한 뒤 그 다음 행으로 넘어가는 방법으로 실행된다.


9.1. 활용 - 글 읽기 1

 

글 목록에 있는 모든 데이터를 가져오기 위해 select * from topic 명령어를 사용한다.

저것을 데이터베이스 서버에 전송하는 api는 mysqli_query()이다.

반복 실행하기 위해서 while()문의 () 안에 $row= mysqli_fetch_array($result))를 넣는다.

 

데이터베이스를 제어하는 코드와 표현하는 것과 관련되어 있는 코드가 섞여있으면 보기에 좋지 않기 때문에 코드를 분리하는 게 좋다. (단지 보기 좋게 하기 위해서이기 때문에 안해도 괜찮음)

 

<ol>

   <?=$list?>

</ol>

로 표현하면 echo로 표현한 것과 똑같은 축약형이 된다.

효과는 똑같지만 조금 더 보기 좋아진다.


9.2. 활용 - 글 읽기 2

 

단 하나의 행만을 가져올 때는 반복문을 가져올 필요가 없다. 그렇기 때문에 반복문을 사용하지 않는 $article에 하나의 정보만 담는다. 

'description' => $row['description']이라고 하면 키 값이 숫자가 아니라 문자인데, 문자인 배열을 연관 배열이라고 한다.

링크를 클릭했을 때 어떤 글자를 나오게 하려면, 우선 링크를 만들어야 한다.

그리고 조건문을 통해서 id값이 존재할 경우 새로운 값을 출력하도록 작성한다.

$article = array(
  'title'=>'Welcome',
  'description'=>'Hello, web'
);
if(isset($_GET['id'])) {
  $sql = "SELECT * FROM topic WHERE id={$_GET['id']}";
  $result = mysqli_query($conn, $sql);
  $row = mysqli_fetch_array($result);
  $article['title'] = $row['title'];
  $article['description'] = $row['description'];
}

10.1. 보안 - filtering

 

사용자로부터 정보를 받는 순간부터 그 받은 정보는 오염될 가능성이 있다.

 

-보안과 관련해서 일어날 수 있는 2 가지의 사고와 해결책

1. 들어오는 정보가 문제가 있을 경우 (입력) -> filtering

2. 문제가 있는 정보가 이미 들어와 있는 상태에서 그 정보가 사용자들에게 노출되는 경우 (출력) -> escaping

 

보안에서 사용자가 입력한 정보는 철저히 불신해야 한다.

불신하는 방법이 filtering이다.

filtering은 $filtered_id와 같은 방법으로 쓴다. 이렇게 사용해야 애플리케이션이 훨씬 더 안전해지기 때문에 꼭 이렇게 써야 한다.

 

mysqli_real_escape_string($conn, $_POST['title']를 주면 사용자로부터 입력을 받을 수 있다.

<?php
$conn = mysqli_connect(
  'localhost',
  'root',
  '111111',
  'opentutorials');
$filtered = array(
  'title'=>mysqli_real_escape_string($conn, $_POST['title']),
  'description'=>mysqli_real_escape_string($conn, $_POST['description'])
);
$sql = "
  INSERT INTO topic
    (title, description, created)
    VALUES(
        '{$filtered['title']}',
        '{$filtered['description']}',
        NOW()
    )
";
$result = mysqli_query($conn, $sql);
if($result === false){
  echo '저장하는 과정에서 문제가 생겼습니다. 관리자에게 문의해주세요';
  error_log(mysqli_error($conn));
} else {
  echo '성공했습니다. <a href="index.php">돌아가기</a>';
}
?>

10.2. 보안 sql injection의 원리

 

sql injection 공격에서 자주 사용되는 기술들을 알아볼 것이다.

sql문은 주석이 있는데, 이 주석에 의해서 처리된 데이터를 데이터베이스 서버는 무시한다.

'-- SELECT * FROM topic;이라고 입력하면 아무 일도 일어나지 않는다. 왜냐하면 '--'는 이 뒤에 오는 명령문을 무시하라는 내용의 명령어이기 때문이다.

이렇게 주석으로 인해서 뒤에 오는 내용을 무시할 수 있다.

 

NOW()를 sql injection을 이용하면 다르게 바꿀 수 있다. 

악의적인 사용자가 새치기를 하도록 입력할 수 있다.

-> 따옴표 앞에 \가 붙으면 공격의 의도가 있었던 데이터가 그대로 데이터베이스 안에 유입되게 할 수 있다.

또 잘 조작하면 데이터베이스에 있는 다른 테이블도 지울 수 있다.

 

sql injection 공격을 이용해서 새로운 유저를 만들고 그 새로운 유저에게 슈퍼유저의 권한을 주면 그 공격자는 슈퍼유저가 되서 데이터베이스 시스템을 점령할 수 있게 된다.

 

sql 서버는 sql 데이터베이스를 통해서 운영체제에 명령을 내릴 수 있기 때문에 그렇게 되면 데이터베이스 뿐만 아니라 그 데이터베이스가 사용되고 있는 서버 컴퓨터를  점령할 수 있게 된다.

 

sql 문을 쪼개서 공격하는 것이 중요한 공격 기법이기 때문에 mysqli_query()를 쓰는 것이 훨씬 더 안전한다.

-> 사용자가 입력하는 정보뿐만 아니라 사용자가 입력하는 파일, 네트워크를 통해서 제공되는 데이터 등 모든 것을 불신해야 한다. sql injection 공격은 위험하다.


10.3. escaping

 

오염된 정보가 들어왔을 때 그것이 사용자에게 노출되지 않도록 하는 방법 중에서 크로스 사이트 스크립트라고 하는 방법을 알아보자

 

프로그래머가 자기의 의도에 의해서 자바스크립트 코드를 짜는 것은 문제가 없는데 사용자가 데이터를 입력하면서 악의적인 목적으로 자바스크립트 코드를 주입할 수 있다.

크로스 사이트 스크립팅이라는 공격 기법을 사용하면 목록을 선택하면 다른 사이트로 가버리도록 할 수 있다. 

-> 스크립트 태그를 사용하면 그렇게 만들 수 있다. 

 

로그인되어 있는 사용자가 어떤 페이지로 들어왔을 때 크로스 사이트 스크립트가 동작하면서 은밀한 정보를 외부로 유출시키는 것이 가능하다. 그렇기 때문에 크로스 사이트 스크립트 공격은 상당히 위험한 공격이다.

 

<,>(꺽쇠)는 태그가 시작하고 끝날 때 사용하는 명령어이기 때문에 그냥은 출력이 되지 않는다. 그렇기 때문에 꺽쇠를 출력시키기 위해서는 &lt;를 앞에 붙이고 원하는 내용을 쓴 다음에 끝 부분에 &gt;를 쓰면 출력이 된다.

php에서는 htmlspecialchars()함수가 그런 역할을 한다.

이것을 이용하면 링크를 클릭하면 자바스크립트 코드가 그대로 들어오게 만들 수 있다.

'22-여름방학 > 웹 해킹(WEB HACKING)&웹 기초' 카테고리의 다른 글

웹기초_5주차 정리  (0) 2022.08.06
웹해킹_4주차 정리  (0) 2022.07.31
웹해킹_3주차 정리  (0) 2022.07.24
웹기초_3주차 정리  (0) 2022.07.24
웹해킹_2주차 정리  (0) 2022.07.17