728x90

PHP 위지윅 에디터를 이용하여 글쓰기를 할 경우 DB에 저장하는 코드 예시이다.

https://link2me.tistory.com/1142 게시글을 참조하면 도움된다.

 

 
<?php
if(!isset($_SESSION)){
    session_start();
}
// 데이터가 제대로 넘어오는지 검증 목적
//echo '<pre>';print_r($_POST);echo '</pre>';
//echo '<pre>';print_r($_FILES);echo '</pre>';
//exit;
if(isset($_POST&& $_SERVER['REQUEST_METHOD'== "POST"){
    @extract($_POST);
    require_once 'path.php';// root 폴더를 기준으로 상대경로 자동 구하기
    require_once $g['path_config'].'config.php';
    require_once $g['path_config'].'dbconnect.php';
    require_once $g['path_class'].'dbDataClass.php';
    require_once $g['path_class'].'dbconnect.php';
    require_once $g['path_class'].'adminClass.php';
    require_once $g['path_class'].'bbsClass.php';
 
    $a = new adminClass();
    $b = new bbsClass();
    $d = new LegacyDBClass;
 
    if($mode == 'write'){
        $subject = trim($subject);
        $subject = preg_replace("/\s{2,}/"" "$subject); // s(공백문자)가 2회 이상을 1번으로 변경
        $subject = $d->XSSFilter($subject);
        $content = trim($content);
        $content = $b->html_purifier($content);
 
        $filename = NULL;
        if ($_FILES['file']['name']) { // 파일이 첨부되어 있으면
            $allowed_ext = array('jpg','bmp','png','gif','zip','doc','xls''xlsx');
            $tmpname = $_FILES['file']['tmp_name'];  // 임시파일명
            $realname  = $_FILES['file']['name'];  // 실제 파일명
            $filesize  = $_FILES['file']['size']; // 파일 크기
            $filetype  = $_FILES['file']['type']; // 파일 형태
 
            if (!$_FILES['file']['error']) { // 오류 없이 파일 업로드 성공
 
                $fileExt = $b->getExt($realname);  // 파일 확장자 구하는 함수
                if(!in_array($fileExt$allowed_ext)) {
                    @unlink($tmpname);
                    echo "-1"// 허용되지 않는 확장자
                    exit;
                } else {
                    // 새로운 파일을 업로드하면 기존 파일은 삭제 처리
                    if($idx > 0) {
                        $R = $d->getDbData('bbs_data','idx='.$idx,'*');
                        if($R['filename'!= NULL) {
                            $oldfilename = $g['path_root'].'files/infile/' . $R['filename'];
                            @unlink($oldfilename);
                        }
                    }
 
                    // 신규 파일 등록
                    if (is_uploaded_file($tmpname)){  // 임시파일이 디렉토리에 존재하는 경우
                        $filename = date("Ymd").md5(uniqid($tmpname)) .round(microtime(true)).'.'.$fileExt;
                        $uploadFile = $g['path_root'].'files/infile/' . $filename//change this directory
                        if(move_uploaded_file($tmpname$uploadFile)){ // 임시 디렉토리에 있던 파일을 실제 파일의 이름으로 전송
                            @chmod($uploadFile,0606); // 리눅스에서 파일 권한 변경
                        }
                    }
                }
            }
        }
 
        $userID = $_SESSION['userID'];
        $userNM = $_SESSION['userNM'];
        $html = 1;
        $depth = 1;
        $notice = 0;
        date_default_timezone_set('Asia/Seoul');
 
        if($idx == 0){
            $d_regis = date('YmdHis');
            $access_ip=$d->getClientIP();
            $QKEY = "subject,content,html,depth,filename,notice,d_regis,userID,userNM,ip";
            $QVAL = "'$subject','$content',$html,$depth,'$filename',$notice,'$d_regis','$userID','$userNM','$access_ip'";
            $d->getDbInsert('bbs_data',$QKEY,$QVAL);
            echo 1;
        } else {
            $idx = $d->XSSFilter($idx);
            // 등록자 여부 체크
            $R = $d->getDbData('bbs_data','idx='.$idx,'*');
            if($R['userID'=== $_SESSION['userID']){ // 등록자만 수정 가능하며 관리자도 수정은 불가
                $QSET="subject='".$subject."',";
                $QSET.="content='".$content."',";
                $QSET.="filename='".$filename."',";
                $QSET.="html='".$html."'";
                $QVAL="idx='".$idx."'";
 
                $d->getDbUpdate('bbs_data',$QSET,$QVAL);
                echo 2;
            } else {
                echo -2;
            }
        }
    } else if($mode == 'delete'){
        $d->getDbDelete('bbs_data',"idx='".$idx."'");
        echo 3// 삭제
    }
else {
    echo -1;
}
 
?>
 

여기서 더 신경써야 할 사항은

realname 을 DB 칼럼에 추가로 저장하는 것이다.

그래야 파일 다운로드할 때 실제 서버에 저장된 파일명과 다운로드되는 파일명이 달라서 해킹 시도 자체를 방지하는 것이 된다.

 

 

 

최근에는 그누보드 소스코드를 분석해보니 html_purifier 를 이용하여 악성코드를 걸러내고 있는 거 같다.

아래 코드는 위에 사용하는 Class 내에 있는 함수들 중에서 일부 발췌하였다.

<?php
class bbsClass extends DBDataClass {
 
    function conv_content($content){
        $source = array();
        $target = array();
 
        $source[] = "//";
        $target[] = "";
 
            $source[] = "/\n/";
            $target[] = "<br/>";
 
        // 테이블 태그의 개수를 세어 테이블이 깨지지 않도록 한다.
        $table_begin_count = substr_count(strtolower($content), "<table");
        $table_end_count = substr_count(strtolower($content), "</table");
        for ($i=$table_end_count$i<$table_begin_count$i++)
        {
            $content .= "</table>";
        }
 
        $content = preg_replace($source$target$content);
 
        $content = $this->html_purifier($content);
 
        $content = str_replace('<A href=','<a target="_blank" href=',$content);
        $content = str_replace('<a href=','<a target="_blank" href=',$content);
 
        return $content;
    }
 
    function html_purifier($html){
        // 절대경로 지정
        //include_once('/home/httpd/htdocs/vendor/ezyang/htmlpurifier/library/HTMLPurifier.auto.php');
        // 상대경로 지정
        include_once('../vendor/ezyang/htmlpurifier/library/HTMLPurifier.auto.php');
        $config = HTMLPurifier_Config::createDefault();
        // data/cache 디렉토리에 CSS, HTML, URI 디렉토리 등을 만든다.
        $config->set('Attr.EnableID'false);
        $config->set('Attr.DefaultImageAlt''');
 
        // 인터넷 주소를 자동으로 링크로 바꿔주는 기능
        $config->set('AutoFormat.Linkify'true);
 
        // 이미지 크기 제한 해제 (한국에서 많이 쓰는 웹툰이나 짤방과 호환성 유지를 위해)
        $config->set('HTML.MaxImgLength'null);
        $config->set('CSS.MaxImgLength'null);
 
        $config->set('Core.Encoding''UTF-8'); // 인코딩
 
        $config->set('HTML.SafeEmbed'false);
        $config->set('HTML.SafeObject'false);
        $config->set('Output.FlashCompat'false);
        $config->set('HTML.SafeIframe'true);
 
        $config->set('Attr.AllowedFrameTargets'array('_blank'));
 
        $purifier = new HTMLPurifier($config);
        return $purifier->purify($html);
    }
 
 
    // 파일 확장자 추출
    function getExt($filename){
        $ext = substr(strrchr($filename,"."),1); // 공격방어 감지를 위해 콤마의 취지를 뒤에서부터 검색
        // strrchr(문자열, 찾을 문자) : 찾을 문자가 마지막으로 나온 위치부터 끝까지 반환 
        // strstr(문자열, 찾을 문자) : 찾을 문자열이 나온 처음 위치부터 끝까지 반환.
        $ext = strtolower($ext); // 확장자 소문자로 변환
        return $ext;
    }
 
}//end class boardClass
 

 

 

 

블로그 이미지

Link2Me

,