본문 바로가기

해킹/Web Hacking

File Upload 취약점 대응 방안 - BLOB / CLOB 구현

이번에는 File Upload 공격을 막을 수 있는 BLOB 저장 방식을 구현해보겠습니다.

 

File Upload 취약점이라는 건 서버에 공격자가 업로드한 파일이 저장되고, 그 파일을 실행시킬 수 있기 때문에

발생하는데요,

 

하지만 BLOB 형식인 긴 문자열의 데이터로 이미지나 파일을 저장시킨다면

이는 File Upload 공격에 대한 완벽한 대비책이 됩니다.


BLOB ( Binary Large Object )

JavaScript에서 Blob은 이미지, 사운드, 비디오 같은 멀티미디어 데이터를 다룰 때 사용합니다.

  • BLOB
  • TINYBLOB
  • MEDIUMBLOB
  • LONGBLOB

이 중, LONGBLOB은 4GB까지의 데이터를 저장가능합니다.

 

터미널을 열고 다음과 같은 코드를 입력해 blob 을 저장할 테이블을 하나 추가합니다.

 

CREATE TABLE `blob` (
image_id tinyint(3) NOT NULL AUTO_INCREMENT,
image_type varchar(25) NOT NULL,
image longblob NOT NULL,
image_size varchar(255) NOT NULL,
image_name varchar(50) NOT NULL,
KEY image_id (image_id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

 

Query OK라는 글자가 뜨면 성공한겁니다.

 

desc `blob`; 을 입력하니 예쁘게 잘 만들어진 것 같습니다.

 

테스트를 위해 blobtest.php라는 파일을 하나 생성합니다.

blobtest.php

<form enctype="multipart/form-data" action="uploadblob.php" method="post">
    <div><input name="myfile" type="file"/></div>
    <div><input type="submit" value="Submit"/></div>
</form>

 

화면에 이렇게 잘 구성되었습니다.

 

 

uploadblob.php 파일도 하나 생성해줍니다.

uploadblob.php

<?php
/*** check if a file was submitted ***/
if(!isset($_FILES['myfile']))
{
    echo '<p>Please select a file</p>';
}
else
{
    try    {
        upload();
        /*** give praise and thanks to the php gods ***/
        echo '<p>Thank you for submitting</p>';
    }
    catch(Exception $e)
    {
        echo '<h4>'.$e->getMessage().'</h4>';
    }
}

function upload(){
    /*** check if a file was uploaded ***/
    if(is_uploaded_file($_FILES['myfile']['tmp_name']) && getimagesize($_FILES['myfile']['tmp_name']) != false)
    {
        /***  get the image info. ***/
        $size = getimagesize($_FILES['myfile']['tmp_name']);
        /*** assign our variables ***/
        $type = $size['mime'];
        $imgfp = fopen($_FILES['myfile']['tmp_name'], 'rb');
        $size = $size[3];
        $name = $_FILES['myfile']['name'];
        $maxsize = 99999999;


        /***  check the file is less than the maximum file size ***/
        if($_FILES['myfile']['size'] < $maxsize )
        {
            $dbh = new PDO("mysql:host=localhost;dbname=test", 'root', 'root-password');

   			 /*** set the PDO error mode to exception ***/
    		$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            /*** our sql query ***/
            $stmt = $dbh->prepare("INSERT INTO testblob (image_type ,image, image_size, image_name) VALUES (? ,?, ?, ?)");

            /*** bind the params ***/
            $stmt->bindParam(1, $type);
            $stmt->bindParam(2, $imgfp, PDO::PARAM_LOB);
            $stmt->bindParam(3, $size);
            $stmt->bindParam(4, $name);

            /*** execute the query ***/
            $stmt->execute();
        }
        else
        {
            /*** throw an exception is image is not of type ***/
            throw new Exception("File Size Error");
        }
    }
    else
    {
        // if the file is not less than the maximum allowed, print an error
        throw new Exception("Unsupported Image Format!");
    }
}
?>

코드 출처 : https://salix97.tistory.com/181

 

 

그리고는 화면으로 돌아가 저번 게시판 구현 때 사용했던 우파루파 사진을 첨부해줍니다.

 

 

 에러가 떴습니다.

 

데이터베이스가 선택되지 않았다는 것 같은데... 뭐가 문제일까요

 


 

코드를 치다가 그만 host=localhost를 빼먹었군요 이걸 추가해주니 잘 나왔습니다

Thank you for submitting 이라고 업로드가 잘 된 것 같은 문구가 떴습니다.

 

 

 

터미널을 열고 MySQL 쿼리문을 입력해봤습니다.

 

SELECT * FROM `blob`;

 

그랬더니 귀여운 우파루파가 데이터 형식으로 DB에 저장된 것을 볼 수 있었습니다.

 

잘 저장된 것을 불러와서 보는 것도 가능한지 테스트해보겠습니다.

 

showblob.php를 생성해줍니다.

 

showblob.php

<?php

/*** assign the image id ***/
$image_id = 1;
try     {
    /*** connect to the database ***/
    $dbh = new PDO("mysql:host=localhost;dbname=test", 'root', 'root-password');

    /*** set the PDO error mode to exception ***/
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    /*** The sql statement ***/
    $sql = "SELECT image, image_type FROM `blob` WHERE image_id=$image_id";

    /*** prepare the sql ***/
    $stmt = $dbh->prepare($sql);

    /*** exceute the query ***/
    $stmt->execute();

    /*** set the fetch mode to associative array ***/
    $stmt->setFetchMode(PDO::FETCH_ASSOC);

    /*** set the header for the image ***/
    $array = $stmt->fetch();

    /*** check we have a single image and type ***/
    if(sizeof($array) == 2)
    {
        /*** set the headers and display the image ***/
        header("Content-type: ".$array['image_type']);

        /*** output the image ***/
        echo $array['image'];
    }
    else
    {
        throw new Exception("Out of bounds Error");
    }
}
catch(PDOException $e)
{
    echo $e->getMessage();
}
catch(Exception $e)
{
    echo $e->getMessage();
}
?>

 

URL 창에 showblob.php를 호출했더니 귀여운 우파루파가 잘 보여지고 있습니다.


소스 참조

https://heropy.blog/2019/02/28/blob/

https://salix97.tistory.com/181

 

MySQL 에 BLOB으로 이미지 저장하기

1. BLOB 란? Binary Large Object 의 약어이다. 이진 데이터를 저장하기 위한 MySQL 의 필드 유형이라고 한다. 이미지의 경우 이진 데이터로 이루어져 있기 때문에 BLOB 유형의 데이터로 만들어서, MySQL 데이

salix97.tistory.com