COREBlog2スパム対策(まとめ)¶
最近仕事の影響で 私 がかなり疎かになってしまっています。そんな中、 takanory.netさんからtrackbackをもらった のでこれに反応して、自分が行っているCOREBlog2スパム対策をまとめてみます。これを読めば一通り設定できるはず>for 未来の自分
# 参考: spamとの戦い(回顧編)
スパム対策共通BuzzWordチェックスクリプト¶
以前にblogに書いたスクリプト と同一です。スパマーがこのエントリを見ているとは思いませんが、念のためbuzzwordは省略します。とはいえ、大抵のスパム投稿には url=
か href
が入っているので、この2つを設定しておけばほとんど防げると思います。
以下のコードをportal_skins/custom等に置きます。
## Script (Python) "validateBuzzWords"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=text, moderated=True
##title=COREBlog2: buzzword checker
##
buzz_words = (
'casino',
)
try:
if not moderated:
return moderated
s = str(text).lower()
for w in buzz_words:
if s.find(w) >= 0:
moderated = False
break
else:
moderated = True
except:
pass
return moderated
コメントスパム対策¶
まずはcbaddCommentの直接呼び出しを拒否するようにカスタマイズします。これが必要になるシーンは滅多にないとは思いますが、自分は喰らってしまったので対策しています。22行目~27行目が追加されています。
## Controller Python Script "cbaddComment"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind state=state
##bind subpath=traverse_subpath
##parameters=
##title=COREBlog2: modify: refuce direct access
##
from Products.CMFPlone import transaction_note
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.utils import log
cbtool = getToolByName(context, 'coreblog2_tool')
REQUEST = context.REQUEST
form = REQUEST.form
RESPONSE = context.REQUEST.RESPONSE
entry = context
# refuse direct access.
if script is REQUEST.get('PUBLISHED',None):
RESPONSE.setStatus(403)
return '403 Forbidden'
#return RESPONSE.redirect('403 Forbidden', 403)
if REQUEST.form.has_key('remember_cookie'):
#Set cookie
for key in ['author','email','url']:
if REQUEST.form.has_key(key):
REQUEST.RESPONSE.setCookie(key,REQUEST.form[key],
path='/'.join(context.blog_object().getPhysicalPath()),
expires='Sun, 01-Dec-2099 12:00:00 GMT')
#Try to add comment
entry.addComment2Entry(author=form['author'],email=form['email'],
url=form['url'],title=form['title'],
body=form['body'],REQUEST=REQUEST)
#Send notify mail if need
if context.getSend_comment_notification():
try:
to_addr = context.getNotify_to()
from_addr = context.getNotify_to()
msgbody = context.translate('comment_notify_body')
elements = {}
for k in ('title','author','url','body'):
if REQUEST.form.has_key(k):
elements[k] = REQUEST.form[k]
else:
elements[k] = ''
elements['post_ip'] = REQUEST.getClientAddr()
elements['entry_url'] = context.absolute_url()
msgbody = msgbody % (elements)
msgsubject = context.translate('comment_notify_title')
mgsheader = """To: %s
From: %s
Mime-Version: 1.0
Content-Type: text/plain; Charset=utf-8
""" % (to_addr,from_addr)
cbtool.send_mail(mgsheader+msgbody, to_addr, from_addr, msgsubject)
except Exception,e:
log( 'COREBlog2/cbaddComment: '
'Some exception occured, %s' % e )
#Set next action
state.setNextAction('redirect_to:string:')
#Display message for user
state.setKwargs({'portal_status_message':'A comment successfully added.'})
return state
return state
validateCommentにBuzzWordをチェックするコードを追加しています。以下のカスタマイズでは、smapの傾向や元IP収集のためにBuzzWordに引っかかった場合に、投稿内容を管理者にメール送信し、投稿フォームにはエラーを表示して投稿自体はされないようにしています。
## Controller Validator "validateComment"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind state=state
##bind subpath=traverse_subpath
##parameters=
##title=COREBlog2: modify: add buzzword check
##
from Products.CMFPlone import transaction_note
REQUEST=context.REQUEST
moderated = True
reqs = ['title','body']
#See setting and append required field list
if context.getComment_require_author():
reqs.append('author')
if context.getComment_require_email():
reqs.append('email')
if context.getComment_require_url():
reqs.append('url')
for key in reqs:
if REQUEST.has_key(key) and not REQUEST[key]:
state.setError(key, 'Please enter a value', new_status='failure')
for key in ['title', 'body', 'author', 'email', 'url']:
if REQUEST.has_key(key):
m = context.validateBuzzWords(REQUEST[key], True)
if not m:
state.setError(key, 'Please remove NG words.', new_status='failure')
moderated = False
#Try to send mail for Bad comment
if not moderated:
context.addCommentMail(
author=REQUEST['author'],email=REQUEST['email'],
url=REQUEST['url'],title=REQUEST['title'],
body=REQUEST['body'], moderated=moderated,
remoteip=REQUEST.getClientAddr())
if state.getErrors():
state.set(portal_status_message='Please correct the errors shown.')
return state
BuzzWordコメント時のメール送信用スクリプトです。これはCOREBlog2がメール送信によるコメント通知をサポートする前に作ったものですが、アクセス元IPを通知してくれるあたりがスパム対策っぽい感じです。
## Script (Python) "addCommentMail"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=author,email,url,title,body,moderated,remoteip='',message=''
##title=
##
try:
mailhost=getattr(context, \
context.superValues(['Secure Mail Host', 'Mail Host'])[0].id)
except:
raise AttributeError, "Mail Host object cant be found."
mMsg = """To: %s
From: %s
Mime-Version: 1.0
Content-Type: text/plain;
Moderate : %s
ManageURL: http://www.freia.jp/taka/blog/%s/entry_comments
ViewURL : http://www.freia.jp/taka/blog/%s
RemoteIP : %s
Author : %s
Title : %s
URL : %s
EMail : %s
EntryID : %s
Body :
%s
Additional message:
%s
"""
try:
to_addr = "admin@example.jp"
from_addr = "admin@example.jp"
parent_id = context.getId()
mTo = to_addr
mFrom = from_addr
mSubj = 'blog: A comment %s' % (moderated and 'added!' or 'NEED MODERATE.')
mMsg = mMsg % (to_addr, from_addr, str(moderated), parent_id, parent_id, \
remoteip, author, title, url, email, parent_id, body, message )
mailhost.send(mMsg, mTo, mFrom, mSubj)
except:
raise
トラックバックスパム対策¶
tbpingをカスタマイズして、validateBuzzWordsとスパム時のメール送信を呼び出すようにしています。
## Script (Python) "tbping"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=Receive trackback: COREBlog2: modify: check buzzwords
##
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.utils import log
cbtool = getToolByName(context, 'coreblog2_tool')
REQUEST = context.REQUEST
form = REQUEST.form
RESPONSE = context.REQUEST.RESPONSE
entry = context
excerpt = ''
if form.has_key('excerpt'):
excerpt = form['excerpt']
title = cbtool.convert_charcode(form['title'])
blog_name = cbtool.convert_charcode(form['blog_name'])
excerpt = cbtool.convert_charcode(excerpt)
#Try to add trackback
try:
# !!!STAART modify by shimizukawa!!!
moderated = True
for text in [title, blog_name, excerpt]:
m = context.validateBuzzWords(text, True)
if not m:
state.setError(key, 'Please remove NG words.', new_status='failure')
moderated = False
#Try to send mail for Bad comment
if not moderated:
context.addTrackbackMail(
title=title, url='',
blog_name=blog_name,
excerpt=excerpt,
moderated=moderated,
remoteip=REQUEST.getClientAddr(),
message='NEED MODERATE',)
raise 'NEED MODERATE'
# !!!END modify by shimizukawa!!!
#Send notify mail if need
if context.getSend_trackback_notification():
try:
to_addr = context.getNotify_to()
from_addr = context.getNotify_to()
msgbody = context.translate('trackback_notify_body')
elements = {}
for k in ('blog_name','title','excerpt','url','excerpt'):
if form.has_key(k):
elements[k] = REQUEST.form[k]
else:
elements[k] = ''
elements['post_ip'] = REQUEST.getClientAddr()
elements['entry_url'] = context.absolute_url()
msgbody = msgbody % (elements)
msgsubject = context.translate('trackback_notify_title')
mgsheader = """To: %s
From: %s
Mime-Version: 1.0
Content-Type: text/plain; Charset=utf-8
""" % (to_addr,from_addr)
cbtool.send_mail(mgsheader+msgbody, to_addr, from_addr, msgsubject)
except Exception,e:
log( 'COREBlog2/tbping: '
'Some exception occured, %s' % e )
entry.addTrackback2Entry(title=title,url=form['url'],\
blog_name=blog_name,excerpt=excerpt)
return context.tbping_result(client=context,REQUEST=REQUEST,\
error_code=0,message='Thanks :-)')
except:
return context.tbping_result(client=context,REQUEST=REQUEST,\
error_code=1,message='Error occured!')
addCommentMailとほぼ同一のスクリプト。トラックバック用。芸のないコピペコード。
## Script (Python) "addTrackbackMail"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=title,url,blog_name,excerpt,moderated,remoteip='',message=''
##title=
##
try:
mailhost=getattr(context, \
context.superValues(['Secure Mail Host', 'Mail Host'])[0].id)
except:
raise AttributeError, "Mail Host object cant be found."
mMsg = """To: %s
From: %s
Mime-Version: 1.0
Content-Type: text/plain;
Moderate : %s
ManageURL: http://www.freia.jp/taka/blog/%s/entry_trackbacks
ViewURL : http://www.freia.jp/taka/blog/%s
RemoteIP : %s
Title : %s
URL : %s
BlogName : %s
EntryID : %s
Excerpt :
%s
Additional message:
%s
"""
try:
to_addr = "admin@example.jp"
from_addr = "admin@example.jp"
parent_id = context.getId()
mTo = to_addr
mFrom = from_addr
mSubj = 'blog: A trackback %s' % (moderated and 'added!' or 'NEED MODERATE.')
mMsg = mMsg % (to_addr, from_addr, str(moderated), parent_id, parent_id, \
remoteip, title, url, blog_name, parent_id, excerpt, message )
mailhost.send(mMsg, mTo, mFrom, mSubj)
except:
raise
ApacheのIPアドレス制限¶
ログの出力を標準のアクセスと別にしたり、アクセス時にZopeにアクセスに行かないように設定したりしてます。httpd.confの書き方を全然調査してないので冗長な感じです。あと本当はエラーページじゃなくて403を返すように設定したい。
SetEnvIf Remote_addr "(24\.244\.170\.180|81\.177\.8\.26)" spam1
CustomLog /var/log/httpd/www.freia.jp-access.log combined env=!spam1
CustomLog /var/log/httpd/www.freia.jp-access-spam1.log combined env=spam1
ErrorLog /var/log/httpd/www.freia.jp-error.log
RewriteEngine On
# for spam filtering.
RewriteCond %{REMOTE_HOST} ^(24\.244\.170\.180|81\.177\.8\.26)
RewriteRule ^/(.*) http://localhost:80/underconstruction/ [P,L]
# rewrite standard zope server.
RewriteRule ^/(.*) http://localhost:8080/VirtualHostBase/http/www.freia.jp:80/VirtualHostRoot/$1 [P,L]
上記のhttpd.conf、見やすくするためにIPアドレス制限を2つだけ書いていますが、本当は以下のIPを制限しています。
24.244.170.180 65.214.44.212 66.246.218.107 69.50.167.122 81.177.7.108 81.177.7.154 81.177.7.37 81.177.7.81 81.177.8.26 85.255.117.18 194.117.134.72 195.39.170.102 200.79.91.5 202.56.253.184 209.190.4.10 209.190.4.106 209.67.219.178