웹개발/PHP

[PHP] require, basename, htmlspecialchars, strip_tags by.생활코딩

ruming 2021. 1. 25. 11:58

 

이번 포스팅을 마지막으로 생활코딩의 php 강의를 마무리 할 것 같다.

 

이번 포스팅에서 알아볼 함수

#require

#require_once

#htmlspecialchars

#strip_tags

#basename

 

파일로 모듈화 - require

 

웹 페이지를 다 만든 후에는 계속해서 문제점을 찾고 수정해나가야 한다.

 

우리가 만든 파일들을 보면, 비효율적인 면이 보인다.

특히 index.php와 create.php, update.php에서 가장 윗부분에 있는 함수가 서로 같은데,

만약 함수의 특정 부분을 수정하면 이 세 파일을 모두 수정해야 하는 번거로움이 있다.

이렇게 파일을 수정하다가 어느 하나를 놓칠 수도 있다.

 

여기서 좋은 코드를 만드는 방법 한 가지는 무엇이냐면,

중복을 제거하라는 것이다.

 

그럼 이 코드에서 중복을 제거하려면 어떻게 해야 할까?

 

라이브러리[library]는 공통적으로 쓰이는 코드를 부품화한 것이다.

lib라는 폴더를 만들어서 코드를 따로 보관하면 나중에 꺼내 쓰기만 하면 되고,

수정하기에도 용이하다.

 

lib 폴더에 print.php 파일을 만들어서 함수를 따로 저장한다.

▽print.php▽

<?php
  function print_title(){
    if(isset($_GET['id'])){
      echo $_GET['id'];
    }else{
      echo "Welcome";
    }
  }
  function print_description(){
    if(isset($_GET['id'])){
      echo file_get_contents("data/".$_GET['id']);
    }else{
      echo "Hello, PHP";
    }
  }
  function print_list(){
    $list = scandir('./data');
    $i = 0;
    while($i < count($list)){
      if($list[$i]!='.'&&$list[$i]!='..'){
        echo "<li><a href=\"index.php?id=$list[$i]\">$list[$i]</li>";
      }
      $i = $i + 1;
    }
  }
?>

 

#require

코드를 저장했으니 이제 사용해야 한다.

require함수를 사용하면 lib 폴더 안의 함수들을 꺼낼 수 있다.

▽index.php

<?php  
  require('lib/print.php');	//lib에서 코드 불러옴
?>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>
      <?php
        print_title();
      ?>
    </title>
  </head>
  <body>
    <h1><a href="index.php">WEB</h1>
    <ol>
      <?php
        print_list();
      ?>
    </ol>
    <a href="create.php">create</a>
    <?php if(isset($_GET['id'])) { ?>
      <a href="update.php?id=<?= $_GET['id'] ?>">update</a>
      <form action="delete_process.php" method="post">
        <input type="hidden" name="id" value="<?=$_GET['id']?>">
        <input type="submit" value="delete">
      </form>
    <?php } ?>
    <h2>
      <?php
        print_title();
      ?>
    </h2>
    <?php
      print_description();
    ?>
  </body>
</html>

 

웹페이지는 그대로 잘 동작한다.

 

그리고 또 중복을 최소화하기 위해

view라는 폴더에 중복되는 부분을 저장할 것이다.

▽index.php

<?php
  require('lib/print.php');
?>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>
      <?php
        print_title();
      ?>
    </title>
  </head>
  <body>
    <h1><a href="index.php">WEB</h1>
    <ol>
      <?php
        print_list();
      ?>
    </ol>
    <a href="create.php">create</a>
    <?php if(isset($_GET['id'])) { ?>
      <a href="update.php?id=<?= $_GET['id'] ?>">update</a>
      <form action="delete_process.php" method="post">
        <input type="hidden" name="id" value="<?=$_GET['id']?>">
        <input type="submit" value="delete">
      </form>
    <?php } ?>
    <h2>
      <?php
        print_title();
      ?>
    </h2>
    <?php
      print_description();
    ?>
<?php require('view/bottom.php'); ?>	//view에서 코드 불러옴

 

가장 밑에는 두 줄밖에 없었기 때문에 줄이는 게 경제성은 없지만

나중에 뒤에 공통된 내용을 추가할 때 훨씬 경제적이다. 

 

윗부분도 중복을 없애보자.

▽index.php

<?php
  require_once('lib/print.php');
  require('view/top.php');	//view에서 코드 불러옴
?>	
    <a href="create.php">create</a>
    <?php if(isset($_GET['id'])) { ?>
      <a href="update.php?id=<?= $_GET['id'] ?>">update</a>
      <form action="delete_process.php" method="post">
        <input type="hidden" name="id" value="<?=$_GET['id']?>">
        <input type="submit" value="delete">
      </form>
    <?php } ?>
    <h2>
      <?php
        print_title();
      ?>
    </h2>
    <?php
      print_description();
    ?>
<?php require('view/bottom.php'); ?>

 

▽top.php

<?php require('lib/print.php'); ?>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>
      <?php
        print_title();
      ?>
    </title>
  </head>
  <body>
    <h1><a href="index.php">WEB</h1>
    <ol>
      <?php
        print_list();
      ?>
    </ol>

이렇게 줄이게 되면 top에서 print_title함수를 실행할 때 코드가 어디서 왔는지에 대한 암시가 되기 때문에

이상적인 코드라고 볼 수 있다.

그러나 이대로 실행하면 오류가 난다.

index.php에서 print.php와 top.php를 불러오는데 top.php에서 print.php를 또 불러오게 되어

함수가 중복으로 정의되기 때문이다.

 

php에서 한 번 정의한 함수는 다시 정의할 수 없다.

 

#require_once

여기서 require_once함수를 이용하면 오류를 방지할 수 있다.

require_once함수는 이미 require된 함수가 다시 require되려고 할 때 그것을 무시한다.

따라서 index.php와 top.php에서 require함수를 require_once함수로 바꾸면 오류가 생기지 않는다.

 


보안 XSS

 

#Cross-site Scripting

위 링크에는 지난번에 XSS에 대해 공부했던 포스팅을 걸어두었다.

 

cross site scripting, 줄여서 XSS는 웹페이지에 스크립트 태그를 삽입해

클라이언트를 대상으로 공격하는 공격 기법이다.

 

우리가 만든 페이지는 XSS에 대한 방어를 하나도 하지 않았기 때문에

스크립트 공격이 그대로 먹힐 것이다.

한번 실험해보자.

 

다음과 같이 새로운 글에 스크립트 태그를 넣고 본 블로그로 이동하도록 링크를 걸어보았다.

 

제출을 누르면 어떻게 될까?

블로그로 이동이 되었다!

 

만약에 링크가 아니라 alert을 사용하면 알림창을 띄울 수도 있다.

이것을 교묘히 숨겨 로그인을 대신 한다던지 하는 문제도 발생할 수 있는 아주 기초적이고 단순한 공격이다.

 

php에서는 xss를 방지하기 위한 함수 두 개가 있다.

 

#htmlspecialchars

이 함수를 이용하면 스크립트 태그가 그대로 출력된다.

그런데 이 함수를 사용하면 이미지나 줄바꿈 등의 필수적인 것들을 못할 수 있다.

 

이 함수를 사용해보자.

<?php
  function print_title(){
    if(isset($_GET['id'])){
      echo htmlspecialchars($_GET['id']);
    }else{
      echo "Welcome";
    }
  }
  function print_description(){
    if(isset($_GET['id'])){
      echo htmlspecialchars(file_get_contents("data/".$_GET['id']));
    }else{
      echo "Hello, PHP";
    }
  }
  function print_list(){
    $list = scandir('./data');
    $i = 0;
    while($i < count($list)){
      $title = htmlspecialchars($list[$i]);
      if($list[$i]!='.'&&$list[$i]!='..'){
        echo "<li><a href=\"index.php?id=$title\">$title</li>";
      }
      $i = $i + 1;
    }
  }
?>
<?php require('view/bottom.php'); ?>

입력이 들어가는 모든 곳에 함수를 쓰고 아까 했던 공격을 다시 해보겠다.

 

위와 같이 그대로 출력되어 나오는 것을 알 수 있다.

 

#strip_tags

이 함수는 html 태그와 php 태그를 제거하는 함수다.

allowable_tags로 제거하지 않을 태그를 정할 수 있다.

 

참고

 

XSS를 막으려면 사용자가 입력한 정보를 불신해야 한다.

 


보안 파일 경로 보호

 

만약 당신이 중요한 정보를 저장한 파일이 있다면,

공격자가 그 파일이 들어있는 폴더를 유추해 그 파일을 열어볼 수 있다.

 

이 폴더를 유추하지 못하도록 막는 함수가 있다.

 

#basename

이 함수는 파일의 경로에서 파일명만 추출해주는 함수이다.

 

실제로 사용해보자.

우선 password.txt 파일을 만들고 거기에 값을 저장해보자.

이렇게 값을 저장하고

주소창에서 id 뒷부분을 ../password.txt로 바꿔주면 이 파일의 내용이 그대로 드러날 것이다.

 

예상대로 파일의 이름과 내용이 그대로 출력됐다!

이것은 아주 큰 문제다.

 

이제 함수를 사용해 이 문제를 해결해보자.

▽lib/print.php▽

<?php
  function print_title(){
    if(isset($_GET['id'])){
      echo htmlspecialchars($_GET['id']);
    }else{
      echo "Welcome";
    }
  }
  function print_description(){
    if(isset($_GET['id'])){
      $basename = basename($_GET['id']);	//변수 생성 - basename 함수 사용
      echo htmlspecialchars(file_get_contents("data/".$basename));
    }else{
      echo "Hello, PHP";
    }
  }
  function print_list(){
    $list = scandir('./data');
    $i = 0;
    while($i < count($list)){
      $title = htmlspecialchars($list[$i]);
      if($list[$i]!='.'&&$list[$i]!='..'){
        echo "<li><a href=\"index.php?id=$title\">$title</li>";
      }
      $i = $i + 1;
    }
  }
?>
<?php require('view/bottom.php'); ?>

basename이란 변수를 만들어 함수를 사용해주고 아래는 생성한 변수를 사용한다.

 

이제 내용이 나타나지 않는다.

 

 

또 다른 보안위협을 알아보자.

 

데이터를 삭제할 때, 서버와 어떤 방식으로 어떤 데이터를 주고받는지 알아낼 수가 있다.

검사 - 네트워크 - preserve log를 체크하는 방식으로 말이다.

 

delete_process.php를 호출하는 것을 알아낼 수 있다.

 

클릭하면 post방식으로 주고받는 것까지 알아낼 수 있다.

 

id값을 전달한다는 사실도 알아낼 수 있다.

 

이것을 악용해 ../index.php라고 하면 삭제될 수가 있다.

이것을 방지하기 위해 삭제대상을 data로 닫아놓을 필요가 있다.

▽delete_process.php

<?php
  unlink('data/'.basename($_POST['id']));
  header('Location: /index.php');
?>

 


UI API 그리고 공부방법

#UI = User Interface

#API = Application Programing Interface

 

UI란 무엇인가?

애플리케이션을 사용하는 사람들이 접하는 모든 것

예) 링크, 폼, 텍스트

사용자는 UI를 통해 시스템을 조작하고 시스템이 제공하는 정보를 볼 수 있다.

 

API란 무엇인가?

예) 명령, 함수, 태그

API가 부품이라면 언어의 문법은 결합방법이라고 할 수 있다.

 

어떻게 공부하는 게 좋을까?

최소한의 문법과 최소한의 API를 이용해 자신의 문제를 해결하라.

효율적이고 아름답지 않을수록 좋다.

 


수업을 마치며

 

php function reference

php는 객체 지향 언어인데, 객체 지향이 없을 때부터 존재했기 때문에

대체로 함수의 형태로 제공된다.

 

간단한 함수 몇가지를 간략하게 알아보자.

#Audio Formats Manipulation

mp3 파일의 재생 시간, 만든 이 등을 추출할 때 사용한다.

 

#Compresstion and Archive Extensions

압축된 파일을 php로 압축을 풀거나, 혹은 압축할 수 있다.

 

#Cryptography Extensions

암호/보안과 관련된 부분

 

 

#php composer

패키지 : 부품이 되는 소프트웨어

이 사이트에서 필요한 패키지를 다운받아 사용할 수 있다.

 

#php cookie

쿠키 기능을 이용해 웹 사이트의 사용자들을 식별할 수 있다.

보안 사항에 여러 가지 제약사항이 있어 session을 사용하는 것이 좋다.

 

#php session

사용자 인증 구현

 

#federation authentication [타사] php

 

현대의 애플리케이션은 사이트가 직접 회원관리를 하는 것이 아니라

거대 기업의 인증 시스템을 빌려 쓰는 경우가 많다.

예) 네이버로 로그인, 페이스북으로 로그인 등

 

장점

손쉽게 회원가입이 가능하다.

보안의 리스크를 덜 짊어질 수 있다.

 

단점

종속된다.

 

 

#php file upload

파일 업로드

 

 

이상으로 생활코딩 PHP 강의 공부를 마친다.

앞으로는 프로젝트를 준비하면서 필요한 것들, 혹은 개인적으로 궁금한 것에 대해 포스팅하도록 하겠다.