Java で正規表現を使って実装するとこんな感じでしょうか。
import java.util.regex.*;
private static Pattern linkPattern = Pattern.compile(
"(https?)(://[\\p{Alnum}\\+\\$\\;\\?\\.%,!#~*/:@&=_-]+)");
public static String link(String s, int length, String suffix) {
StringBuffer buff = new StringBuffer((int) (s.length() * 1.2f));
Matcher m = linkPattern.matcher(s);
while (m.find()) {
String g = m.group();
if (length > 0 && g.length() > length) {
g = g.substring(0, length);
if (suffix != null) {
g += suffix;
}
}
g = m.quoteReplacement(g);
m.appendReplacement(buff, "<a href=\"$1$2\">" + g + "</a>");
}
m.appendTail(buff);
return new String(buff);
}
ところが、ユーザの投稿した文書をこれでフィルタして HTML として出力するときには CSS(クロスサイトスクリプティング)対策としてサニタイジングを行う必要があり、少し注意しなければなりません。例えば、次のような文書をユーザが投稿したとします。
URL サンプルでは <http://example.com/> を使いましょう。
まず、CSS 対策としてサニタイジングします。
URL サンプルでは <http://example.com/> を使いましょう。
これを自動リンクフィルタすると次のようになります。
URL サンプルでは<<a href="http://example.com/>">http://example.com/></a>を使いましょう。
本来は http://example.com/ をリンクとして出力したいのですが、http://example.com/> がリンクになってしまいます。
これを回避するためにサニタイジングと自動リンクを同時に行います。
private static Pattern linkHtmlPattern = Pattern.compile(結構面倒くさいです。。色々考えたのですが、今のところこれが一番かなと思います。
"((https?)(://[\\p{Alnum}\\+\\$\\;\\?\\.%,!#~*/:@&=_-]+))|([&<>\"\'])");
public static String linkHtml(String s, int length, String suffix) {
StringBuffer buff = new StringBuffer((int) (s.length() * 1.2f));
Matcher m = linkHtmlPattern.matcher(s);
while (m.find()) {
String g = m.group();
if (g.length() == 1) {
if (g.equals("&")) {
g = "&";
} else if (g.equals("<")) {
g = "<";
} else if (g.equals(">")) {
g = ">";
} else if (g.equals("\"")) {
g = """;
} else if (g.equals("\'")) {
g = "'";
}
m.appendReplacement(buff, g);
continue;
}
if (length > 0 && g.length() > length) {
g = g.substring(0, length);
if (suffix != null) {
g += suffix;
}
}
g = m.quoteReplacement(g);
m.appendReplacement(buff, "<a href=\"$1\">" + g + "</a>");
}
m.appendTail(buff);
return new String(buff);
}
Perl の場合は URI::Find という便利なクラスがあります。使い方はこんな感じです。
use URI::Find;
my $finder = URI::Find->new(
sub {
my($uri, $orig_uri) = @_;
return qq {<a href="$uri">$orig_uri</a>};
});
$finder->find(\$text);
では、サニタイジングと併せて使う場合はどうやったらいいのでしょう?すごく悩んでこんな実装にしたことがあります。
$text =~ s/>/ __GT__ /g;
$text =~ s/</ __LT__ /g;
$text =~ s/&/ __AMP__ /g;
$text =~ s/"/ __QUOT__ /g;
$text =~ s/'/ __APOS__ /g;
my $finder = URI::Find->new(
sub {
my($uri, $orig_uri) = @_;
return qq {<a href="$uri">$orig_uri</a>};
});
$finder->find(\$text);
$text =~ s/ __GT__ />/g;
$text =~ s/ __LT__ /</g;
$text =~ s/ __AMP__ /&/g;
$text =~ s/ __QUOT__ /"/g;
$text =~ s/ __APOS__ /'/g;
うむむ。もっといい方法ないですかね。。
0 件のコメント:
コメントを投稿