코드 블럭의 내용을 htmlpurifier에서 우회시키고 싶습니다.

코드 블럭의 내용을 htmlpurifier에서 우회시키고 싶습니다.

QA

코드 블럭의 내용을 htmlpurifier에서 우회시키고 싶습니다.

본문

1. 환경 : 우분투 20.04, PHP7.4.3, 그누보드 5.4.2.7

 

2. 테마 : 그누보드 순정에 Vditor 마크다운 에디터 적용 (https://sir.kr/g5_plugin/7337)

 

마크다운 에디터 적용했지만 결론적으론 순정에 가깝습니다.

 

3. 현상 : 

 

링크 : https://gnu.hubs.tk/bbs/board.php?bo_table=free&wr_id=2

 

링크에 있듯이 


```
<link rel="stylesheet" href="<?php echo G5_EDITOR_URL; ?>/vditor3/dist/index.css">
<script src="<?php echo G5_EDITOR_URL; ?>/vditor3/dist/index.min.js"></script>
<script src="<?php echo G5_EDITOR_URL; ?>/vditor3/editorOptions.js"></script>
```

 

위 내용을 넣으면 내용이 사라집니다.

 

마크다운 에디터 특성상 ```로 감싸면 

 


<pre><div class="vditor-copy"><textarea></textarea><span aria-label="복사" onmouseover="this.setAttribute('aria-label', '복사')" class="vditor-tooltipped vditor-tooltipped__w" onclick="this.previousElementSibling.select();document.execCommand('copy');this.setAttribute('aria-label', '복사완료')"><svg><use xlink:href="#vditor-icon-copy"></use></svg></span></div><code class="language-html hljs xml" style="max-height: 1407px;"><span class="php"><span class="hljs-meta"><?php</span> <span class="hljs-keyword">echo</span> get_view_thumbnail($view[<span class="hljs-string">'content'</span>]);<span class="hljs-meta">?></span></span>
</code></pre>

 

위와 같이 pre와 code로 감싸게 됩니다. 

 

그래서 htmlpurifier와 관계없는 안전한 구문은

 


<?php echo get_view_thumbnail(na_view($view)); // 글내용 출력 ?>

 

위와 같은 내용은 htmlpurifier로 걸러지지 않고 그대로 출력됩니다.

 

하지만 위에 있는 script 관련 부분은 전부 걸러져서 없어집니다.

 

4. 원하는 부분

 

그누보드 코어의 htmlpurifier를 수정해서라도 현상을 정상으로 고치고 싶습니다.

 

즉 script등 위험한 부분 등이 code 안에 있으면 htmlpurifier로 걸러지지 않고 그냥 text로 그대로 표시되게 하는 방법을 알고 싶습니다.

 

https://sir.kr/qa/370007

 

여기에 @BiHon 님께서 적어주셨는데 아직 초보라 어떻게 적용하는지 감이 오질 않습니다. ㅠㅠ

 

5. 해결되었을 때 기대되는 부분

 

이 문제가 해결되면 마크다운 에디터 끝판왕으로 그누보드에서 활용성이 매우 높을 것 같습니다.

 

https://gnu.hubs.tk/bbs/board.php?bo_table=free&wr_id=9

 

위와 같이 악보, 차트, 마인드맵 등 기존 에디터에서는 상상하기 힘든 내용들도 출력이 가능합니다.

방법을 알려주시면 그누보드에서도 잘 사용할 수 있게 가이드를 작성하겠습니다.

감사합니다.

이 질문에 댓글 쓰기 :

답변 8

예제 문자열을 포함한 짧은 코드인데… 안타깝네요.

질문을 기준으로 지정한 문자열 필터 제외에 초점을 맞춘 답변으로,

편의상 구분을 했던 부분을 줄여 필요한 코드만 남겨 등록합니다.

// 그누보드 5.4.2.7
function html_purifier($html)
{
    $f = file(G5_PLUGIN_PATH.'/htmlpurifier/safeiframe.txt');
    $domains = array();
    foreach($f as $domain){
        // 첫행이 # 이면 주석 처리
        if (!preg_match("/^#/", $domain)) {
            $domain = trim($domain);
            if ($domain)
                array_push($domains, $domain);
        }
    }
    // 내 도메인도 추가
    array_push($domains, $_SERVER['HTTP_HOST'].'/');
    $safeiframe = implode('|', $domains);
 
    include_once(G5_PLUGIN_PATH.'/htmlpurifier/HTMLPurifier.standalone.php');
    include_once(G5_PLUGIN_PATH.'/htmlpurifier/extend.video.php');
    $config = HTMLPurifier_Config::createDefault();
    // data/cache 디렉토리에 CSS, HTML, URI 디렉토리 등을 만든다.
    $config->set('Cache.SerializerPath', G5_DATA_PATH.'/cache');
    $config->set('HTML.SafeEmbed', false);
    $config->set('HTML.SafeObject', false);
    $config->set('Output.FlashCompat', false);
    $config->set('HTML.SafeIframe', true);
    if( (function_exists('check_html_link_nofollow') && check_html_link_nofollow('html_purifier')) ){
        $config->set('HTML.Nofollow', true);    // rel=nofollow 으로 스팸유입을 줄임
    }
    $config->set('URI.SafeIframeRegexp','%^(https?:)?//('.$safeiframe.')%');
    $config->set('Attr.AllowedFrameTargets', array('_blank'));
    //유튜브, 비메오 전체화면 가능하게 하기
    $config->set('Filter.Custom', array(new HTMLPurifier_Filter_Iframevideo()));
    $purifier = new HTMLPurifier($config);
    return $purifier->purify($html); // 여기에서 걸러집니다. 그럼 이 부분에 넣어야겠죠?
}
// ↓
function html_purifier($html)
{
    $f = file(G5_PLUGIN_PATH.'/htmlpurifier/safeiframe.txt');
    $domains = array();
    foreach($f as $domain){
        // 첫행이 # 이면 주석 처리
        if (!preg_match("/^#/", $domain)) {
            $domain = trim($domain);
            if ($domain)
                array_push($domains, $domain);
        }
    }
    // 내 도메인도 추가
    array_push($domains, $_SERVER['HTTP_HOST'].'/');
    $safeiframe = implode('|', $domains);
 
    include_once(G5_PLUGIN_PATH.'/htmlpurifier/HTMLPurifier.standalone.php');
    include_once(G5_PLUGIN_PATH.'/htmlpurifier/extend.video.php');
    $config = HTMLPurifier_Config::createDefault();
    // data/cache 디렉토리에 CSS, HTML, URI 디렉토리 등을 만든다.
    $config->set('Cache.SerializerPath', G5_DATA_PATH.'/cache');
    $config->set('HTML.SafeEmbed', false);
    $config->set('HTML.SafeObject', false);
    $config->set('Output.FlashCompat', false);
    $config->set('HTML.SafeIframe', true);
    if( (function_exists('check_html_link_nofollow') && check_html_link_nofollow('html_purifier')) ){
        $config->set('HTML.Nofollow', true);    // rel=nofollow 으로 스팸유입을 줄임
    }
    $config->set('URI.SafeIframeRegexp','%^(https?:)?//('.$safeiframe.')%');
    $config->set('Attr.AllowedFrameTargets', array('_blank'));
    //유튜브, 비메오 전체화면 가능하게 하기
    $config->set('Filter.Custom', array(new HTMLPurifier_Filter_Iframevideo()));
    $purifier = new HTMLPurifier($config);
    preg_match_all('#```.+?```#s', $html, $matches); // Markdown ```~``` 구간 구하기
    $change = []; // 백업↔복구용
    foreach ( $matches[0] as $k=>$v ) $change['^[CODE'.($k+1).']^'] = $v; // 내용에 사용되지 않을만한 문자열로 지정
    $html = strtr($html, array_flip($change)); // ```코드``` → ^[CODE0]^
    $html = $purifier->purify($html); // HTML Purifier 적용
    $html = strtr($html, $change); // ^[CODE0]^ → ```코드```
    return $html;
}
// 이렇게 하면, ```로 감싸인 문자열 부분은 HTML Purifier를 비껴갈 수 있습니다.

Vditor가 최신 버전인가요? 지금 보니 조금 이상하네요.

 

아래에 textarea가 들어가면 안되는데..

 



<pre><div class="vditor-copy"><textarea></textarea>

 

즉 HTML이기 때문에 아래와 같이  HTML입장으로 보면 textarea가 미리 끝나게 됩니다.


<textarea>
...

<pre><div class="vditor-copy"><textarea></textarea> ...

...
</textarea>
 

 

버전업 되면서 저 문장이 들어가는지 이전부터 있었는지 확인해봐야겠습니다.

 

 

필터링 되지 않은 code는


<code class="hljs xml" style="max-height: 1407px;"><span class="php"><span class="hljs-meta">&lt;?php</span> <span class="hljs-keyword">echo</span> get_view_thumbnail(na_view($view)); <span class="hljs-comment">// 글내용 출력 <span class="hljs-meta">?&gt;</span></span></span>
</code>


이렇게 정상적으로 출력됩니다.

제가 이것저것 다 해 봤는데,  그누보드에서는 두가지가 최선이라는 결론 때문에.

그누보드에 요청했던 것입니다.

1. https://github.com/gnuboard/gnuboard5/issues/55

2. https://github.com/gnuboard/gnuboard5/issues/56

 

두가지가 그리 어렵지 않은 것 같아서,  PHP보면서 고쳐서 사용해도 되겠지만, 나중에 업데이트에 문제가 되고.

 

답변에서 안된다고 하면,  Hook이라도 넣어달라고 할려고 기다리고 있는 중입니다.

 

그래서 그누보드 연동된 버전이 3.0.12로 남아 있는 이유입니다.

 

그누보드에서 어느정도 응답이 없고,  Vditor도 새로운 SV모드를 만들면서 거의 한달이상이 지나간 것입니다.

 

그래서 에디터 본연의 기능에 더 힘쓰고 있는중입니다.  

 

나머지는 시간이 지나면 다 해결이 됩니다. 

제 개인적인 생각은 그누보드에서 최대한으로 보수적인 태도를 취할 것 같다입니다.

아무튼 저도 기다려보겠습니다.


그래도 코드 블럭을 제외하면 나머지 기능은 100% 되니깐 정말 아쉬워서 그렇습니다.

분명히 방법이 있을 것 같은데 지식 부족으로 해결 못하는 것이 너무 아쉽습니다.

이미지 붙여넣기 및 마크다운 기능 100% 활용 가능한데 딱하나 htmlpurifier 이거 때문에

너무너무 아쉽습니다.

개인적으로 코드를 많이 써서... 코드 블럭이 필터링되면 쓸 수 없기 때문입니다.

https://gnu.hubs.tk/bbs/board.php?bo_table=free&wr_id=14

마젠토님이 올린 테마와 vditor3.0.12 그대로 올려서 테스트 해봤는데 전부 필터링 당합니다. ㅠㅠ

테스트 해보시려면 test01 / test01 입니다 ㅠㅠ

그냥 wr_option에 3번에 markdown 또는 text 옵션 넣어서 우회시키세요.

 

Vditor 최신 버전에는 sanitize: true 옵션이 디폴트여서 에디터 자체에서 xss는 걸러 줍니다.

 

/bbs/view.php

 


// $view['content'] = conv_content($view['wr_content'], $html);
$view['content'] = $view['wr_content'];

아 그냥 우회하는 것이군요.

저도 이렇게 해보았는데

크롬 개발자모드 콘솔에서


(index):519 GET https://aced.ga/free/nb-basic-%ED%85%8C%EB%A7%88-%EC%88%98%EC%A0%95%EC%97%86%EB%8A%94-vditor3-%EC%9E%91%EC%97%85-%EB%82%B4%EC%9A%A9%EC%9E%85%EB%8B%88%EB%8B%A4/%3C?php%20echo%20G5_EDITOR_URL;%20?%3E/vditor3/dist/index.css net::ERR_ABORTED 404
(index):520 GET https://aced.ga/free/nb-basic-%ED%85%8C%EB%A7%88-%EC%88%98%EC%A0%95%EC%97%86%EB%8A%94-vditor3-%EC%9E%91%EC%97%85-%EB%82%B4%EC%9A%A9%EC%9E%85%EB%8B%88%EB%8B%A4/%3C?php%20echo%20G5_EDITOR_URL;%20?%3E/vditor3/dist/index.min.js net::ERR_ABORTED 404
(index):521 GET https://aced.ga/free/nb-basic-%ED%85%8C%EB%A7%88-%EC%88%98%EC%A0%95%EC%97%86%EB%8A%94-vditor3-%EC%9E%91%EC%97%85-%EB%82%B4%EC%9A%A9%EC%9E%85%EB%8B%88%EB%8B%A4/%3C?php%20echo%20G5_EDITOR_URL;%20?%3E/vditor3/editorOptions.js net::ERR_ABORTED 404
(index):520 GET https://aced.ga/free/nb-basic-%ED%85%8C%EB%A7%88-%EC%88%98%EC%A0%95%EC%97%86%EB%8A%94-vditor3-%EC%9E%91%EC%97%85-%EB%82%B4%EC%9A%A9%EC%9E%85%EB%8B%88%EB%8B%A4/%3C?php%20echo%20G5_EDITOR_URL;%20?%3E/vditor3/dist/index.min.js net::ERR_ABORTED 404
(index):521 GET https://aced.ga/free/nb-basic-%ED%85%8C%EB%A7%88-%EC%88%98%EC%A0%95%EC%97%86%EB%8A%94-vditor3-%EC%9E%91%EC%97%85-%EB%82%B4%EC%9A%A9%EC%9E%85%EB%8B%88%EB%8B%A4/%3C?php%20echo%20G5_EDITOR_URL;%20?%3E/vditor3/editorOptions.js net::ERR_ABORTED 404
ant.js:1 Uncaught SyntaxError: Identifier 'svg' has already been declared


위와 같은 에러가 나오더라구요.

code 안의 내용이 전부 실행이 되더라구요 ㅠㅠ

이건 어떻게 막을 수 있을까요?

실행만 안되면 다 해결될 것 같거든요.

https://aced.ga/free/nb-basic-%ED%85%8C%EB%A7%88-%EC%88%98%EC%A0%95%EC%97%86%EB%8A%94-vditor3-%EC%9E%91%EC%97%85-%EB%82%B4%EC%9A%A9%EC%9E%85%EB%8B%88%EB%8B%A4/

위 링크는 나리야빌더 페이지입니다.

https://gnu.hubs.tk/bbs/board.php?bo_table=free&wr_id=2

위 링크는 마젠토님이 올려주신 테마입니다.

아... 이게 스크립트가 실행되는 조건이 따로 있나봅니다.

https://gnu.hubs.tk/bbs/board.php?bo_table=qa&wr_id=2

여기서는 단순히 스크립트 부분만 넣었는데 실행이 안됩니다.

다만

https://gnu.hubs.tk/bbs/board.php?bo_table=free&wr_id=2

여기서는 내용물이 첫번째 댓글인데...

이 경우는 스크립트가 실행이 되는군요.

그리고 이전에 알려주셨던 /textarea 부분이 역시나 문제가 되는군요..

그 이후에는 vditor에서 렌더링?을 하지 않네요.


// 질문 내용 기준으로, 특정 구간 필터 제외 방법의 한 예.
// https://sir.kr/qa/370007 ... 이전 질문에 남긴 댓글의 방법과 다를 바 없습니다.
// 필터 제외할 구간의 문자열을 단순한 문자열로 치환 > 필터링 > 재치환입니다.
 
preg_match_all('#```.+?```#s', $view, $matches); // ```로 감싸인 문자열 구하기
 
$change_a = []; // 1) A→B 치환용. 내용1```코드1```내용2```코드2```내용3 → 내용1^[CODE0]^내용2^[CODE1]^내용3
$change_b = []; // 2) B→A 치환용. 내용1^[CODE0]^내용2^[CODE1]^내용3 → 내용1```코드1```내용2```코드2```내용3
 
foreach ( $matches[0] as $k=>$v ) {
    $key = '^[CODE'.$k.']^'; // ^[CODE0]^, ^[CODE1]^, ... 내용에 사용되지 않을만한 구분자 사용
    $change_a[$v] = $key;
    $change_b[$key] = $v;
}
 
$view = strtr($view, $change_a); // 1)
$view = get_view_thumbnail(na_view($view)); // HTML Purifier 등등. 어차피 ^[CODE0]^ 문자열이라 그대로.
$view = strtr($view, $change_b); // 2)
echo $view; // 예전 질문에서, code-php를 language-php 처럼 치환해줘야 하면, 출력 전에 치환.
 
// 어때요? 참 쉽죠?

/lib/common.lib.php

581번째 줄부터 이렇게 바꿔줘봤는데요.


function html_purifier($html)
{
    $f = file(G5_PLUGIN_PATH.'/htmlpurifier/safeiframe.txt');
    $domains = array();
    foreach($f as $domain){
        // 첫행이 # 이면 주석 처리
        if (!preg_match("/^#/", $domain)) {
            $domain = trim($domain);
            if ($domain)
                array_push($domains, $domain);
        }
    }

    // 마크다운 ``` 코드 블럭 표시 부분에서 htmlpurifier 우회하기 // 시작
    preg_match_all('#```.+?```#s', $view, $matches); // ```로 감싸인 문자열 구하기
    
    $change_a = []; // 1) A→B 치환용. 내용1```코드1```내용2```코드2```내용3 → 내용1^[CODE0]^내용2^[CODE1]^내용3
    $change_b = []; // 2) B→A 치환용. 내용1^[CODE0]^내용2^[CODE1]^내용3 → 내용1```코드1```내용2```코드2```내용3
    
    foreach ( $matches[0] as $k=>$v ) {
        $key = '^[CODE'.$k.']^'; // ^[CODE0]^, ^[CODE1]^, ... 내용에 사용되지 않을만한 구분자 사용
        $change_a[$v] = $key;
        $change_b[$key] = $v;
    }
    
    $view = strtr($view, $change_a); // 1)
    $view = get_view_thumbnail($view['content']); // HTML Purifier 등등. 어차피 ^[CODE0]^ 문자열이라 그대로.
    $view = strtr($view, $change_b); // 2)
    // 마크다운 ``` 코드 블럭 표시 부분에서 htmlpurifier 우회하기 // 끝

    // 내 도메인도 추가
    array_push($domains, $_SERVER['HTTP_HOST'].'/');
    $safeiframe = implode('|', $domains);

    include_once(G5_PLUGIN_PATH.'/htmlpurifier/HTMLPurifier.standalone.php');
    include_once(G5_PLUGIN_PATH.'/htmlpurifier/extend.video.php');
    $config = HTMLPurifier_Config::createDefault();
    // data/cache 디렉토리에 CSS, HTML, URI 디렉토리 등을 만든다.
    $config->set('Cache.SerializerPath', G5_DATA_PATH.'/cache');
    $config->set('HTML.SafeEmbed', false);
    $config->set('HTML.SafeObject', false);
    $config->set('Output.FlashCompat', false);
    $config->set('HTML.SafeIframe', true);
    if( (function_exists('check_html_link_nofollow') && check_html_link_nofollow('html_purifier')) ){
        $config->set('HTML.Nofollow', true);    // rel=nofollow 으로 스팸유입을 줄임
    }
    $config->set('URI.SafeIframeRegexp','%^(https?:)?//('.$safeiframe.')%');
    $config->set('Attr.AllowedFrameTargets', array('_blank'));
    //유튜브, 비메오 전체화면 가능하게 하기
    $config->set('Filter.Custom', array(new HTMLPurifier_Filter_Iframevideo()));
    $purifier = new HTMLPurifier($config);
    return $purifier->purify($html);
}


아무런 영향이 없는데 ㅠㅠ

너무 몰라서 아쉽네요..

혹시 알려주신 내용을 view.skin.php에서 적용하는건가요?

정말 오랜만에 

질문은 이렇게 하는 것이다 하는 

질문의 정석과 같은 정성이 느껴지는 글입니다 

 

 

저는 무지하여 답을 드릴 수 없어 

안탑깝습니다 ㅜㅜ

 

답변을 작성하시기 전에 로그인 해주세요.
전체 818
QA 내용 검색
filter #DB ×

회원로그인

(주)에스아이알소프트 / 대표:홍석명 / (06211) 서울특별시 강남구 역삼동 707-34 한신인터밸리24 서관 1404호 / E-Mail: admin@sir.kr
사업자등록번호: 217-81-36347 / 통신판매업신고번호:2014-서울강남-02098호 / 개인정보보호책임자:김민섭(minsup@sir.kr)
© SIRSOFT