CGIで特定のHTMLタグを許可するには

[上に] [前に] [次に]
Hyde 2000/03/05(日) 04:53:49
フォームからのデータでHTMLを以下の様にしてタグを無効化しますが,
$value =~ s/</&lt;/g;
$value =~ s/>/&gt;/g;
特定のタグ、例えば<br>だけし様可能で、それ以外はし様付加にするにはどの様に指定したら良いのでしょうか?

Ichi [HomePage] 2000/03/05(日) 07:51:17
私はこんな感じでしています。
途中変態な部分がありますが、頑張って解読してください。

#
# CheckTag('Text') : タグのチェック(開始と終了が対応しているか、許可タグか)
#
# 不正なタグならば、異常終了
#
sub CheckTag
{
local($_) = shift;
# チェック!
# <と>の非対応
if (tr/<// != tr/>//) {
# エラー終了
}
# ネスト<...<
if (m/<[^>]*</) {

# エラー終了
}
# 非対応タグの消去 & タグ対応チェック

# 特殊文字など削除だ
s/[\t\a]//g;
# < -> \t, > -> \a で処理しよう
tr/</\t/;
tr/>/\a/;
# 開始タグ置き換え、終了タグ置き換え、置き換え個数比較して違ってたらエラー(変態コード)
if (
# <p>は</p>を省略できるので、<p>の方が多くても良い
s/\tp([^\a]*)\a/<p$1>/gi < s/\t\/p([^\a]*)\a/<\/p$1>/gi ||
s/\tb([^\a]*)\a/<b$1>/gi != s/\t\/b([^\a]*)\a/<\/b$1>/gi ||
s/\ti([^\a]*)\a/<i$1>/gi != s/\t\/i([^\a]*)\a/<\/i$1>/gi ||
s/\tbig([^\a]*)\a/<big$1>/gi != s/\t\/big([^\a]*)\a/<\/big$1>/gi ||
s/\tsmall([^\a]*)\a/<small$1>/gi != s/\t\/small([^\a]*)\a/<\/small$1>/gi
) {
# エラー終了
}
# 終了タグ無しのタグ
s/\tbr([^\a]*)\a/<br$1>/gi;

s/\t/&lt;/g;
s/\a/&gt;/g;
}

Ichi 2000/03/05(日) 07:56:12
上のはperlの場合です。
<br> <b> <i> <big> <small> <p>を許可しています。

# もっとスマートな方法は無いものかなぁ

S-pore [HomePage] 2000/03/05(日) 12:17:06
> 特定のタグ、例えば<br>だけし様可能で、それ以外はし様付加にするにはどの様に指定したら良いのでしょうか?

私がC言語CGIで使ってる自作ライブラリのタグ処理ルーチンの一部をPerlで書き換えてみました。

@tag = ("br", "a", "font"); # 使用可能タグ
$value =~ s/&/&amp;/g; # 最初から &lt;br> と書いたのまで <br> になっちゃうとまずいので
$value =~ s/</&lt;/g; # とりあえず全部無効にする
foreach (@tag) # で,使用可能タグだけ有効にする
{ $value =~ s/&lt;(\/?$_( |>))/<$1/g; }
$value =~ s/&amp;/&/g; # &amp; を元に戻す

これでどうでしょう? なんか馬鹿みたいな方法ですがわかりやすいと思います。(^^;

# ちなみに,私が使ってるC言語のタグ処理ルーチンは120行もあります。(自動タグ閉じやら自動リンク化やらフォントサイズ制限やらいろいろやってるので^^;)
## あ,今度Perlに移植する予定があるんで,これよりいい方法があったら教えてください。(を)

andi 2000/03/05(日) 12:23:45
TryTheHomePageさんのスクリプトが
S-Poreさんと同様の考え方で作成されているようです。
(一度全部タグを無効にしたあと必要なタグを許可する)
こちらはfontのsizeとかも考えられていたような・・・
記憶が曖昧ですが。

http://www2q.biglobe.ne.jp/~terra/cgi/

で探して下さい。

S-pore [HomePage] 2000/03/05(日) 12:45:39
> Ichiさん:
> # 開始タグ置き換え、終了タグ置き換え、置き換え個数比較して違ってたらエラー(変態コード)

なかなかおもしろい書き方だと思いますが,たとえば,
"</b><b>" とか打たれたときでも対応できますか?
余計な心配だったらすみません。

S-pore [HomePage] 2000/03/05(日) 13:36:33
私が書いたものに間違いがありました。

> { $value =~ s/&lt;(\/?$_( |>))/<$1/g; }

{ $value =~ s/&lt;(\/?$_( |>))/<$1/ig; }
に変更してください。(^^;

あと,私が書いたのには閉じタグチェックなどを入れてないので,
必要なら適当に入れてください。

Ichi 2000/03/06(月) 06:54:36
></b><b>" とか打たれたとき
まずいですね。アルゴリズムを根本的に変える以外上手い解決方法は見つかりません。
例えば<b>なら、前から順にみていって、<b>が出てきたらカウンタを増やし、
</b>が出てきたらカウンタを減らし、カウンタが負になったらエラー。
文字列の最後まで行った時点で、カウンタが0で無ければエラー。
という方法で。明日までにスクリプト書きますのでよろしく。
# 自分のスクリプトにも関係している...
# いや、もろ自分のスクリプトだからな↑

コウノトリ 2000/03/06(月) 11:37:43
私が使っている方法は以下の通り。
対象文字列中に一個でも禁止タグが出てくると全部無効にしてしまいますが。

--
$tag = "a/br/strong/b/sup/sub"; # 許可するタグ
$message = "<marquee>ほえほえ〜!!</marquee>";
$message = checkTag($message, $tag);
print $message . "\n";
exit;

sub checkTag
{
    local($str, $tag) = @_;
    if ($tag && $str =~ /</) {
        $tag =~ tr/A-Z/a-z/;
        @tags = split(/\//, $tag);
        foreach (@tags) { $TAG{$_} = 1; }
        while ($str =~ m/<([\w!\-]+)/g) {
            ($tag = $1) =~ tr/A-Z/a-z/;
            if (!$TAG{$tag}) {
                $str =~ s/&/&amp;/g;
                $str =~ s/"/&quot;/g;
                $str =~ s/</&lt;/g;
                $str =~ s/>/&gt;/g;
            }
        }
    } else {
        $str =~ s/&/&amp;/g;
        $str =~ s/"/&quot;/g;
        $str =~ s/</&lt;/g;
        $str =~ s/>/&gt;/g;
    }
    undef @tags;
    return $str;
}

Ichi 2000/03/07(火) 05:36:25
こんな感じになりました。でてきた開始タグをスタックに積んでいって、
終了タグが来たら、スタックのトップと比較し、違ってたらだめという
方法です。p,i,bを許可しています。
(上の奴は、/\tb([^\a]*)\a/<b$1>/giで<body>も引っ掛けるという
最悪な問題を抱えているので、使わないでください)

sub CheckTag
{
local($_) = shift;
local(@tags,$nest) = ((),0);
local($bak);

(中略)

# < -> \t, > -> \a で処理しよう
tr/</\t/;
tr/>/\a/;
# 許可タグを<...>に戻す
# <b>など、属性指定の無い場合
s/\t(|\/)(b|p|i)\a/<$1$2$3>/gi;
# 属性指定のある場合
s/\t(|\/)(b|p|i) ([^\a]*)\a/<$1$2$3>/gi;

# タグの対応、ネスト具合(</b><b>や、<b><i></b></i>を蹴る)
$bak = $_;
# 小文字にする
tr/[A-Z]/[a-z]/;
while (m/<(\/|)(\w*)/) {
# 終了タグの場合
if ($1 eq '/') {
$nest --;
# 開始タグが無いのに終了タグがきた
if ($nest < 0) {
#エラー
}
# 直前の開始タグと、現在の終了タグが対応しない
if ($tags[$nest] ne $2) {
# ただし、直前の開始タグが終了タグ省略可能なら、無かったことにする
if ($tags[$nest] eq 'p' || $tags[$nest] eq 'li') {
next;
}
#エラー
}
}
# 開始タグの場合
else {
$tags[$nest++] = $2;
}
$_ = $';
}
# 開始、終了タグ対応しているのか?
while ($nest > 0 && ($tags[$nest-1] eq 'p' || $tags[$nest-1] eq 'li')) {
$nest --;
}
if ($nest != 0) {
#エラー(対応していない!)
}
$_ = $bak;

# 終了タグ無しのタグ
s/\tbr([^\a]*)\a/<br$1>/gi;

s/\t/&lt;/g;
s/\a/&gt;/g;
s/&/&amp;/g;

$_;
}

Ichi 2000/03/07(火) 06:07:47
後は属性値の引用符中の">"ですかね。W3Cは&gt;と書くべきだと
主張していますが。

Ichi 2000/03/09(木) 06:23:38
# 許可タグを<...>に戻す
の辺りの修正。タグ名と、属性がくっつくバグがありました。

...
# <b>など、属性指定の無い場合
s/\t(|\/)(font|b|i|big|small|s|strike|cite|strong|p|div|center|table|tr|th|td|ul|dl|dt|dd|li)\a/<$1$2>/gi;
# 属性指定のある場合
s/\t(|\/)(font|b|i|big|small|s|strike|cite|strong|p|div|center|table|tr|th|td|ul|dl|dt|dd|li) ([^\a]*)\a/<$1$2 $3>/gi;
#$1$2 $3
#  ~スペースの追加です

# style, background, onXx属性の禁止(怪しいのも禁止)
(エラー) if m/<[^>]* style=[^>]*>/;
(エラー) if m/<[^>]* background=[^>]*>/;
(エラー) if m/<[^>]* on\w*=[^>]*>/;
...

Ichi [HomePage] 2000/03/12(日) 06:10:23
>(エラー) if m/<[^>]* style=[^>]*>/;
>(エラー) if m/<[^>]* background=[^>]*>/;
>(エラー) if m/<[^>]* on\w*=[^>]*>/;
(エラー)  if m/<[^>]* (style|background|on\w*)=[^>]*>/i;

に修正。

[上に] [前に] [次に]