一部のHTMLタグを通すフィルタ
Posted feedbacks - Java
SGMLのタグは非常に多様な形式を持つようです。もちろん全部の形式に対応すれば良いのですが、必ずしもブラウザが対応しているとは限らないため、正しい形式のタグであっても万一ブラウザが対応していない事によってセキュリティホールになってしまっては意味がありません。そこでごく一般的な形式のタグしか想定しない事にします。
方針
間違っても、未処理のタグが残らないようにする(想定外であっても)。
想定外の構造のタグが誤って変換されてしまう事はやむを得ないとする。
この条件を満たすため、以下の方法で処理する
変換対象のタグを前処理する
全ての <, > を変換する
前処理したタグを元に戻す
&は題意から変換しないことにしました。尚、HTMLではURL中であっても文字参照は有効(ブラウザが解釈しなければならない)ため特別扱いはしていません。
方針
間違っても、未処理のタグが残らないようにする(想定外であっても)。
想定外の構造のタグが誤って変換されてしまう事はやむを得ないとする。
この条件を満たすため、以下の方法で処理する
変換対象のタグを前処理する
全ての <, > を変換する
前処理したタグを元に戻す
&は題意から変換しないことにしました。尚、HTMLではURL中であっても文字参照は有効(ブラウザが解釈しなければならない)ため特別扱いはしていません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | import java.util.regex.*;
public class Sample {
private static final Pattern TAG_FILTER = Pattern.compile
("<(¥¥w+)((¥¥s+¥¥w+(¥¥s*=¥¥s*(¥"[^¥"]*¥"|'[^']*'|[¥¥w-:]*))?)*)¥¥s*/?¥¥s*>");
private static final Pattern END_TAG_FILTER = Pattern.compile
("(?i)</(A|BR|STRONG)¥¥s*>");
private static final Pattern ATTR_FILTER = Pattern.compile
("(¥¥w+)¥¥s*=¥¥s*(¥"[^¥"]*¥"|'[^']*'|[¥¥w-:]*)");
public static String sanitizing(String fragment) {
fragment = fragment.replaceAll("[¥¥p{Cntrl}&&[^¥¥s]]", "");
Matcher m = TAG_FILTER.matcher(fragment);
StringBuffer sb = new StringBuffer();
while (m.find()) {
if ("A".equalsIgnoreCase(m.group(1))) {
String href = null, name = null;
Matcher m2 = ATTR_FILTER.matcher(m.group(2));
while (m2.find()) {
if ("href".equalsIgnoreCase(m2.group(1))) {
href = m2.group(2);
} else if ("name".equalsIgnoreCase(m2.group(1))) {
name = m2.group(2);
}
}
String tag = "¥001"+m.group(1) + ((href != null)?" href="+href
: "") + ((name != null)? " name="+name : "") + "¥002";
m.appendReplacement(sb, m.quoteReplacement(tag));
} else if ("BR".equalsIgnoreCase(m.group(1)) ||
"STRONG".equalsIgnoreCase(m.group(1))) {
m.appendReplacement(sb, "¥001" + m.group(1) + "¥002");
}
}
m.appendTail(sb);
m = END_TAG_FILTER.matcher(sb.toString());
fragment = m.replaceAll("¥001/$1¥002");
fragment = fragment.replaceAll("<", "<");
fragment = fragment.replaceAll(">", ">");
fragment = fragment.replaceAll("¥001", "<");
fragment = fragment.replaceAll("¥002", ">");
return fragment;
}
public static void main(String[] args) throws Exception {
System.out.println(sanitizing("<script><abc><def ghi=jkl>"));
System.out.println(sanitizing("<script foo=¥"<script>alert(¥'bar¥')</script>¥">alert(¥'foo¥')</script>"));
System.out.println(sanitizing("<script foo=¥"<a href=¥'link¥'>link</a>¥" center>alert(¥'foo¥')</script><BR/>"));
System.out.println(sanitizing("<a href='www.g>oogle.com' id=125>link</a>"));
}
}
|


にしお
#3410()
Rating0/0=0.00
このお題はperezvonさんの提案を元にしています。ありがとうございました。 ただ、いきなりだと難しいかと思ったので、肝の部分以外を先に出題しました。このお題は続編で徐々に難しくなっていきます。
追記:属性に<や>が含まれてしまうケースに漏れのある解答が多いようなのでテストケースを追加します。 これは「この出力なら十分」という意味です。この出力の通りでなければいけないという意味ではありません。 <script foo="<script>alert('bar')</script>">alert('foo')</script> <script foo="<script>alert('bar')</script>">alert('foo')</script> <script foo="<a href='link'>link</a>">alert('foo')</script> <script foo="<a href='link'>link</a>">alert('foo')</script> <a href='www.g>oogle.com'>link</a> <a href="./www.g%3Eoogle.com">link</a>[ reply ]