【追記】kses.phpのバグ(ではなかった)

学校Weblogシステムを開発している。
先日は別の中学校の先生からオファーがあり、是非導入したいと。
オープンソースである故に、自由にしていただいてもよいのだが、
いかんせん、日曜プログラマの開発したものであるため、私自身しかインストールをすることはできない気がした。
(一応Web上でインストールするためのプログラムを作ったのだが、どうもPHP上からパーミッションの設定ができない。
パーミッションを強引に設定するシェルスクリプトがあるのだが、きっとshexecは許可されていないだろう。)
で、導入をしに行った。

それで、久しぶりにWeblogシステムを開発するモチベーションが回復したので、
特定のタグを許可する機能が、開発サーバでは運用できていても、
運用サーバではうまくいかなかった機能を使えるように再設計した。

結構評判がよく、多くの場面で色を変えるのに使われている。

そこで、次はWYSWYGエディタを導入しようとする。
これをつかえば3分くらいで、導入できた。3分かかってないかも知れない。

http://ckeditor.com/demo

これまでのtextareaタグにclassを貼り付け関連づけるだけで導入できる。
ここで、「わーい。WYSWYGだからHTML Injectionから解放されたー!」
とか言うほどの馬鹿ではないつもりなので、特定タグをサニタイズすることは、
忘れないように、特定タグだけ許可する方針は変えないことにした。

(実はこのWYSWYGエディタはソース入力も可能である。さらに、POSTに強引に送られる心配もある。)


ところが、何度やっても文字色の変更に失敗する。
ところが、何度やっても背景色の変更に成功する。

つまりこうだ。

<span style="background-color:#FF0000">abc</span>

は常に成功するのに、

<span style="color:#ff0000">abc</span>

<span style="#ff0000">abc</span>

となるのだ。

styleタグがうまく機能しないとかならわかるが、、color:だけ消えるとか何なんですか。

何度自分のソースを追っていってもこんなよくわからない挙動は発見できなかったので、
タグ除去ライブラリである、kses.phpにバグがあると結論づけた。

そこで、kses.php バグとか調べると、あった。
kses.phpソースコードはさくっと見るのはしんどい程正規表現を使って居るので、結果だけを書くと、こうすれば直る。

#
# [kses strips evil scripts!]


function kses($string, $allowed_html, $allowed_protocols =
               array('http', 'https', 'ftp', 'news', 'nntp', 'telnet',
                     'gopher', 'mailto', 'color'))

という風に、arrayの中に、colorをプロトコルとしてallowさせればよい。
対症療法だが。

【追記】2014/11/02
774さんにコメントでご指摘いただいたように、
kses関数の第三引数は配列であり、コードを追っていくと、すべて、配列として処理をしているため、
外部からkses関数を呼ぶ際に、colorを含む配列を渡してやると、うまくいくはずですので、
よいですね。ご指摘ありがとうございます。
※ただ、既にkses関数はかなりのところでシステムとして使っているため、それをすべて修正するのは
大変ですので、また考えてみます。[sedとかawkとか使っていいんですが・・・最近使っていないため、いまいち自信が・・・(泣)]

別の方法としてkses.phpの配下に
filter.phpというものがあり、

include '../kses.php';

$allowed = array('b' => array(),
                 'i' => array(),
                 'a' => array('href'  => array('minlen' => 3, 'maxlen' => 50),
                              'title' => array('valueless' => 'n')),
                 'p' => array('align' => 1,
                              'dummy' => array('valueless' => 'y')),
                 'img' => array('src' => 1), # FIXME
                 'font' => array('size' =>
                                         array('minval' => 4, 'maxval' => 20)),
                 'br' => array());

$val = $_POST['val']

こんな感じで定義されているところに追加してもうまくいくかもしれません。
3年前のことで記憶が曖昧なのですが、たしかやってもうまく行かなくて困ったような気もしますが・・。