../
Wikiページにhtmlタグを埋め込む†
PukiWiki公式ページで自作プラグインofficial:自作プラグイン/include_template.inc.php、official:自作プラグイン/include_module.inc.phpを発表していますkahataと申します。
wikiページへのhtmlタグの埋め込み許可については脆弱性/securityの点で議論のあるところですが、その議論を別にしてページにちょっとしたhtmlタグの埋め込みができると小回りが利いて大変便利です。
現にMediaWikiではhtmlタグの埋め込みを可能にする設定が可能で、またextensionと呼ばれる拡張機能はhtml(xml)タグで搭載する仕様になっています。さらにrunphpという、php生コードを頁に埋め込む拡張機能まであります。そこで、pukiwikiでhtmlタグを埋め込む拡張を試みました。(プラグインは色々あるようですが....)
こうした拡張は本来html.php、convert_html.phpでやるべきですが、codingが難しく大幅な改造になるのでここではちょっとしたフックで行っています。
原理は非常に簡単で、lib/pukiwiki.phpの convert_html(get_source($base)); の直前でタグ部分をhtml変換に影響されない識別子に一旦置き換え、convert_html後にタグ部分をしかるべきコードに書き戻すものです。
pluginも作成していますが、何分出来たてのほやほやで初心者が<html><php>を多用した場合や悪用された場合どんな危険があるかわからず、また公式サイトではフクロダタキに会うのが目に見えていますのでこちらのサイトで紹介させて頂きます。
開発者の方々の忌憚ないご意見を頂戴できれば幸いです。
インストール 以下のファイルを適当な場所(例えばlib)に置く†
tag_Hook.class_0.007.zip (以下のコメントのように変更しました)
変更箇所†
- pukiwiki.ini.php(以下を追加)
if (! defined('PKWKEXP_ENABLE_USE_TAG'))
define('PKWKEXP_ENABLE_USE_TAG', 1); // 1 = enabled
if (PKWKEXP_ENABLE_USE_TAG){
$tagHooks = array( // グローバル変数: タグ名 => コールバック関数
'html' => 'output_html',
'php' => 'output_php',
'plugin' => 'output_plugin'
);
require_once(LIB_DIR.'tag_Hook.class.php');
$taghook = new tag_Hook;
}
これで全てのページでhtmlタグの使用とphpコードの埋め込みが可能になります。
- htmlタグは <html>~</html>の間に埋め込みます
- phpコードは <php>~</php>の間に埋め込みます( <?php、?>は書かない。また書いても無視)
尚htmlタグ,phpコードの埋め込みが危険な場合は、上記$tagHook変数で必要なtagのみに限定することができます。
この方法で色々な<tag>の機能の拡張が可能ですが、ここでは実験的にブロック型,インライン型プラグイン埋め込みをやっています。
- 書式: <plugin [type="inline"] name="プラグイン名" option="引数">[~]</plugin>または<plugin name="プラグイン名" option="引数" />*1
コメント†
コメントをお待ちしています。
- どんな危険があるかわからない、って時点でBugTrack 行きな気がしますが・・・ --
- どんな危険があるかわからないと書いたのは、初心者が十分知識がないまま<html><php>を乱用した場合や悪用された場合の事です。またできたてのほやほやでと書いたのは十分な安全対策を実装してないからです。開発者の方にはむしろこうしたHookを設ける考え方についてご意見をお聞きしたいです。 -- kahata
- もし本サイト管理者のほうでここに載せること自体問題ありと判断された場合は、削除またはBugTrackに移して頂いて結構です。 kahata --
- kahataです。 -- kahata
class _tag_HookのtoStringを以下のようにすれば、プラグインを作成しなくても行頭に//#tag_Hookを埋め込んだ*2ページだけこの機能を発動することが出来ます。
ほかに安全対策として、ページを凍結しなければ作動しないようにするとか色々考えられると思います。
public function toString($body){
$count = 0;
str_ireplace('#tag_Hook','', $body, $count);
if($count) {
$retstr = $this->convert($body);
$retstr = convert_html($retstr);
$output = $this->retrieve($retstr);
}
else {
$output = convert_html($body);
}
return $output;
}
また、一般から見えないページに<html>コード,<php>コードのtemplateを作成しておいて拙作official:自作プラグイン/include_template.inc.phpを用いてページにincludeするような方法もあります。*3
- str_ireplace() でチェックしているって事は、文中のどこに出てきても(それこそ、整形済みテキストで
#tag_Hook
使用例
のような使い方をしても)発動するって事ですか?
それから、$count は参照渡しするパラメータなので、あらかじめ初期化しておかないとエラーがでるかもです。(エラーの内容を外してたらゴメンナサイ) --
- 正直なところページ内容を識別する手軽な方法としてstr_ireplace()を使いました。この辺もう少し深く考えて厳密にプログラムする必要があるかもしれません。ページ限定の仕様はお好みで変えてください。$count初期化の件了解しました。ありがとうございます。
- 例えば、str_ireplace()に代えて以下のようにするとプラグイン同様の仕様になります。
public function toString($body){
$count = 0;
- str_ireplace('#tag_Hook','', $body, $count);
+ $body = preg_replace('/^(?:#tag_Hook(?!\w)\s*)+/im','',$body,$count);
if($count) {
(以下略)
- また特定の正規表現にマッチするページだけ作動するようページ名で識別することも可能です。
- 安全を期す場合は、diff.inc.phpに以下のpatchを当てて、PKWK_SAFE_MODEで使用してください。*4 -- kahata
backup.inc.phpも同様のpatchを当てることが好ましいです。
function plugin_diff_action()
{
global $vars;
+ if (PKWK_SAFE_MODE) die_message('PKWK_SAFE_MODE prohibits this');
- 但し、上のような対策をとってもセキュリティーは万全ではありません。新規ページ作成のtempalateの読み込みで、PKWK_SAFE_MODEで凍結したページでも簡単に読み込めてしまうことが判りました。この辺はdeveloper teamでなんとかしてほしいところ。 -- kahata
- 補足です。さらにユーザ閲覧制限をかけたページも上記新規ページ作成のtempalate読み込みで簡単に読み込めました。
これは明らかにセキュリティー・ホールだと思います
- BugTrack/598 --
- linkありがとうございます。まだ完了にはなってませんが何とかなりそうなので少し安心しました。 -- kahata
template入力ボックスを取ってしまうのが一番手っ取り早いですが・・・・
- この改造が「PHP 5 以降必須」である事を、一応インストールの説明に書いておいたほうがいいのでは?(クラス関数を、public やprivate を使って宣言してますし)
あと、include プラグインで他ページを取り込んだ時や、edit プラグインの編集プレビュー時とかに、この機能を動かさないのは安全対策 or 仕様ですか?(プレビュー時に、タグ閉じ忘れとかPHP エラーとかで、「ページの更新」などのボタンが表示されなかったら泣きですしね・・・) --
- 冒頭書きのとおり本格的なhtml.php/convert_html.phpの改造ではなく最終convert_html直前のフックなので、プラグイン内部など、すなわち#includeするページ内やプレビューでこの機能は働きません*5*6。このことを理解した上でお使い頂くということになるのでしょうか・・・「PHP 5 以降必須」の件了解しました。
蛇足ながら<html>,<php>の中身までエラートラップ出来ませんので、当然自己責任でお願いします。*7
- revision 0.006 -- 以下の点を変更追加しました。 -- kahata
- convert()関数の修正 -- 同じページに<tag />と<tag>~</tag>が混在する場合の不具合修正
- convert()関数の改良 -- <tag>~</tag>内部の~($input)前後の改行空白削除
- retrieve()関数の改良 -- 置換順序を「先入れ後出し」方式に変更
- tagの追加・改良(必要に応じて $tagHooks に追加してお使いください)
- <comment>コメント(行)</comment> -- コメント(行)をコメントアウト(複数行サポート)
- <wiki>テキスト</wiki> -- テキストをconvert_htmlして出力 ( <wiki style="foo"> <wiki class="bar">が使用可能)
- <span>インライン</span> -- インラインのhtmlを出力 (ブロック型の<div>~</div>は<html>~</html>の内部に配置すること)
- <plugin>~</plugin> -- インライン型追加(type="inline"を指定)、ブロック型の複数行引数サポート(タグ内部に複数行引数を配置)
- ページ毎のスキン指定 -- kahata
official:質問箱4/502に関連してページ毎にスキンを切り替えるタグを考えました。
各ファイルを修正して*8以下の<skin />をページに埋め込むとスキンの切替が出来ると思います。
- 書式: <skin file="
DATA_HOMEからスキンファイルの相対パスSKIN_DIRにあるスキンファイルのパス" /> (例: <skin file="pukiwiki.skin.php" />)
- default.ini.phpの改造
/////////////////////////////////////////////////
// Skin file
if (defined('TDIARY_THEME')) {
define('SKIN_FILE', DATA_HOME . SKIN_DIR . 'tdiary.skin.php');
} else {
define('SKIN_FILE', DATA_HOME . SKIN_DIR . 'pukiwiki.skin.php');
}
+ $skin_file = SKIN_FILE;
- lib/html.phpの改造
function catbody($title, $page, $body)
{
+ global $skin_file;
global $script, $vars, $arg, $defaultpage, $whatsnew, $help_page, $hr;
.......(中略).......
- require(SKIN_FILE);
+ require($skin_file);
}
- tag_Hook.class.php(revision 0.006) のclass tag_Hook extends _tag_Hook に以下のコールバック関数を追加*9
public function output_skin($tag_name,$element,$input,$argv){
global $skin_file;
if(preg_match("#^\.\./|/\.\./#", $argv['file'])) {
return "You must not specify upper directory. ";
}
if(file_exists(DATA_HOME . SKIN_DIR . $argv['file'])) {
$skin_file = DATA_HOME . SKIN_DIR . $argv['file'];
return NULL;
}
else return 'no such skin file '.$argv['file']. '! <br />';
}
- 一応、関連先をリンク: BugTrack/634 --
- 試さずに書くので間違っているかもしれませんが、wiki ディレクトリの・・・と指定すればユーザー認証で制限しているファイルを覗けたりしませんか?コレ --
- 「wiki ディレクトリの・・・と指定すれば」の意味がよく理解できていませんが、書式のfile="DATA_HOMEからスキンファイルの相対パス"の意味であれば、例えばskinディレクトリにある*.skin.phpに限定して上位ディレクトリ(../,/../)を覗けないようにすることは可能だと思います。*10
違っていたらもう少し詳しい説明をお願いします。上記の件修正しました。 -- kahata
- Menubarに適用させるためにDLさせていただきました。pukiwiki.ini.phpとlib/pukiwiki.php(123行目あたり) に指定されている箇所を変更し、行頭に「//#tag_Hook」を埋め込んだのですが、「Menubar」のみの表示だと、うまく表示できるのですが、TOPページなど全体表示になると、タグが丸見え状態になってしまいます。 -- ery
- revision 0.007 -- MediaWiki(ウィキペディア)と同様の拡張関数機能とテンプレート機能を搭載しました。 --
これらの機能の詳細についてはウィキペディア(Wikipedia):条件文、[ウィキペディア(Wikipedia):マジックワードおよびウィキメディア(wikimedia):テンプレートを参照してください。
基本的に拡張関数は自作願います。
- 書式1: <{#拡張関数名:引数1[|引数2|.....]...}>
wikiテキストを引数としてwikiテキストを出力する。出力結果はhtmlに変換して表示される。(convert_htmlを通す)
(例)<{#test:&color(red){Hello World !};}> → 出力結果: <{ }>書式--Hello World !
- 書式2: <[#拡張関数名:引数1[|引数2|.....]...]>
htmlコードを引数としてhtmlコード出力する。出力結果(html)がそのまま表示される。(convert_htmlを通さない)
(例)<[#test:<font color=blue>Hello World !</font>]> → 出力結果: <[ ]>書式--Hello World !
- 書式3: <{テンプレートページ名[|キー1=値1|キー2=値2|.....]}>
指定したページをテンプレートにして、パラメータリストのデータを表示する。(MediaWikiのtemplateを模したもの)
official:自作プラグイン/include_template.inc.phpの組み込みが必要です。