Trackback Spam を防ぎたい
書いた人: noriaki 2007,08月02日(木) 22:40
ブログを書いているとコメントスパムやトラックバックスパムとの闘いは避けられないんですが,あまりにもひどいのでなんとか防いでみようと思い立ちました.その際の考え方や,利用しているブログシステム「typo」に追加した機能などをご紹介します.
現状
まず,現状のスパム対策はどうなっているかを説明します.スパムには大きく分けてコメントスパムとトラックバックスパムが存在するわけですが,どちらも誘導したいWebページのリンクを一方的にこのブログに貼り付けていきます.
コメントスパムは実のところほとんどありません.これは,このブログがjavascriptを利用したコメントシステムを採用しており,スパマーが使っているであろうツールはjavasriptを理解しないためだと思います.
一方,トラックバックスパムは毎日100件のオーダーで届いています.各エントリにほとんど表示されていないのは,デフォルトで付いているAkismetプラグインを利用して自動判定を行っているためです.
問題点
しかし,Akisumetはスパムかどうかの判定はしてくれるのですが,それが本当にスパムかどうか最終的に判断するのは私であり,スパムと判断したものを削除するのも私なので,毎日確認と削除を行う手間がかかってしょうがないのです.
解決策
ここで,トラックバックの意義を考えたとき,トラックバックを送信した側のブログエントリにはトラックバックを受信したブログエントリへのリンクが存在するはずです.そこで,ブログシステム側でトラックバック送信ブログにリンクが存在するかどうかをチェックすることによってトラックバックスパムをそもそもはじいてしまうことにしました.
具体的な施策
基本的な考え方は,トラックバックの ping を受信したら,トラックバック登録する前に送信元のエントリを取得して,リンクとしてエントリURLが存在しているかチェックします.さらに,各エントリのトラックバックURLの表示部分にリンクの有無を調べることを書いておけば,スパムじゃないトラックバックを送ってくださる人は通るでしょう.
とりあえず,typoではトラックバックの受信をどうやってるのか調べました.typoのディレクトリでgrep-findしてみると,app/controller/articles_controller.rb に trackback アクションを発見.
app/controller/articles_controller.rb
# Receive trackbacks linked to articles
def trackback
@error_message = catch(:error) do
if params[:__mode] == "rss"
# Part of the trackback spec... will implement later
# XXX. Should this throw an error?
elsif !(params.has_key?(:url) && params.has_key?(:id))
throw :error, "A URL is required"
else
begin
settings = { :id => params[:id],
:url => params[:url], :blog_name => params[:blog_name],
:title => params[:title], :excerpt => params[:excerpt],
:ip => request.remote_ip, :published => true }
this_blog.ping_article!(settings)
rescue ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid
throw :error, "Article id #{params[:id]} not found."
rescue ActiveRecord::RecordInvalid
throw :error, "Trackback not saved"
end
end
nil
end
endどうやら,app/models/blog.rb 内の ping_article! メソッドを呼んでるみたいです.
app/models/blog.rb
def ping_article!(settings)
settings[:blog_id] = self.id
article_id = settings[:id]
settings.delete(:id)
trackback = published_articles.find(article_id).trackbacks.create!(settings)
endそこで,blog.rb 内にトラックバック送信元ページにリンクが存在するかチェックするメソッドを追加し,ping_article! 内で呼び出すようにしましょう.チェックしてリンクが存在しなければエラーにして,articles_controller.rb の trackback アクションでエラーをキャッチするという方針でいきます.
app/models/blog.rb
class TrackbackSpam < StandardError; end ## Error class
def ping_article!(settings)
settings[:blog_id] = self.id article_id = settings[:id]
settings.delete(:id)
article = published_articles.find(article_id)
raise Blog::TrackbackSpam unless check_link_exist(article, settings[:url]) ## check link exist
trackback = article.trackbacks.create!(settings)
end
# checking article link existance
def check_link_exist(article, url)
return nil unless /^http/ =~ url
article_url = URI.parse(settings['canonical_server_url']) + article.location
regexp = Regexp.new(%q(href=['"]?) + Regexp.quote(article_url.to_s) + %q(["']?))
doc = open(url).read.toutf8
regexp =~ doc
end
private :check_link_exist例外クラスの Blog::TrackbackSpam を追加し,トラックバックを送信してきたブログ内にリンクが存在するかどうかを調べます.
自分のブログエントリのURLを取得するのにsettings・・みたいな手間のかかることをやっているのは,普通はうまく絶対パスでURLを取得できる以下の方法が上手くいかなかったからです.
article.location(nil, false)また,正規表現で探しているのはなぜかというと,最初はREXMLで読もうと思ったのですが,ルーズなHTMLを解析できなかったので諦めました.Hpricotとかを利用した方がパフォーマンスは良さそうですね.
app/controller/articles_controller.rb
# Receive trackbacks linked to articles
def trackback
@error_message = catch(:error) do
if params[:__mode] == "rss"
# Part of the trackback spec... will implement later
# XXX. Should this throw an error?
elsif !(params.has_key?(:url) && params.has_key?(:id))
throw :error, "A URL is required"
else
begin
settings = { :id => params[:id],
:url => params[:url], :blog_name => params[:blog_name],
:title => params[:title], :excerpt => params[:excerpt],
:ip => request.remote_ip, :published => true }
this_blog.ping_article!(settings)
rescue ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid
throw :error, "Article id #{params[:id]} not found."
rescue ActiveRecord::RecordInvalid
throw :error, "Trackback not saved"
rescue Blog::TrackbackSpam
throw :error, "Link not exist in sent trackback page"
end
end
nil
end
end変更点は以下を追加したことです.その後,typo を再起動しました.
rescue Blog::TrackbackSpam
throw :error, "Link not exist in sent trackback page"今までは一日100件以上来ていたトラックバックスパムが明日になってどうなっているか楽しみです.また結果をご報告しますので,お楽しみに.
あ,エントリページのトラックバック欄を直すの忘れてますね.それは,また次回.


http://wizardbible.org/32/32.txt 文字参照を使う方法はマニアックかつ手軽でよさそうです。
hidetoxさん
コメントありがとうございます.
文字列参照を埋め込むんですかー.マニアックですね(笑)でも,javascriptを有効にしていない人にもコメントしてもらえるのでアクセシビリティが高まりそうですね.