概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Prevent Links in Text Fields to Foil Spammers - Andy Croll
- 原文公開日: 2019/04/14
- 著者: Andy Croll
Rails: テキストフィールドのリンクを無効にしてスパムを防ごう(翻訳)
多くのアプリケーションでは、招待メールや通知メール、パスワード忘れのリマインダーメールの形でメールを送信します(必ずしもメインの機能とは限りません)。
ユーザーが生成したコンテンツを送信メールで許可してしまうと、たちまちアプリケーションはメールスパム業者の格好の標的になってしまいます。スパム業者はろくでなしの人間のこともあれば彼らの操るボット軍団のこともあり、あなたが正式なユーザーに許可してしまった自由文入力機能を悪用します。
AppleメールやGmailのようなメールクライアントは、メールのWebアドレス的な文字列を自動的にハイライト表示します。つまりハッカーたちはテキストフィールドにそれらしいWebアドレスを入力するだけで、HTMLの注入すら行うことなく、ユーザーをろくでもないWebサイトに導くことができてしまいます。
決して次のように書かないこと
アプリでスパム業者たちがメールを送信できるように書いてしまう。
<%= form_for :invitee do |f| %>
Email: <%= f.text_field :email %>
Message : <%= f.text_field :message %>
<%= f.submit "Send Email" %>
<% end %>
常に次のように書くこと
バリデーションを用いて、テキストフィールドへのWebアドレスの挿入を阻止する。
<%= form_for :invitee do |f| %>
Email: <%= f.text_field :email %>
Message : <%= f.text_field :message %>
<%= f.submit "Send Email" %>
<% end %>
validators/no_urls_validator.rb
class NoUrlsValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if value.blank?
if value.match?(%r{(http|\w+\.\w+\/?)})
record.errors[attribute] << (options[:message] || "Webアドレスが含まれているようです")
end
end
end
app/models/invitee.rb
class Invitee
# ...
validates :message, no_urls: true
# ...
end
そうすべき理由
これは気づかれにくい課題ですが、スパム業者たちに知られていて悪用される攻撃方法であることは間違いありません。ユーザーが入力した自由文を含むメール送信をアプリケーションで許可したら最後、テキストがどれほど短くても確実に標的にされてしまいます。
最も顕著なのは、支払いを受け取る前にメール送信をアプリケーションで許可している場合です。もしメール送信前に正しいクレジットカードの入力を義務付けていなければ、スパム業者が「無料お試し」にサインアップするスクリプトを自動で実行し、アプリからリンクを送信する可能性があります。
アプリから大量のスパムメールが送信されると、アプリのメール配信全体はもちろん、ビジネスドメインにまで悪影響が生じる可能性があります。そんな事態を望む人はいません。
ユーザーが生成したHTMLやJavaScriptからブラウザを保護する機能はRailsの初期から組み込まれていますが、Webサイトらしく見える非HTMLテキストを自動的にリンク化するメールクライアントまでRailsアプリケーションが面倒を見ることはできません。
ユーザーが入力したテキストをバリデーションすることで、このスパム手法の標的になる可能性を減らせます。
そうしない理由があるとすれば
私がバリデーターで使っていた正規表現は出来が今ひとつで、たまにfalse positive(偽陽性)になることがありました。http
という文字列を含んでいたり、like.this
のようにドットでつながった文字があるだけで却下していたのです。
そうした正規表現は、マッチするパターンを狭めるよう変更しましょう。
あるいは、バリデーションで防いだりユーザーにエラーメッセージを表示する代わりに、コールバックでテキストを修正するという手もあるといえばあります(あらゆるドット.
の後ろにスペースを追加するなど)。
"like.this".tr(".", ". ")
#=> "like. this"
他にも、もし可能なら、ユーザーがお金を払うまではテキスト入力などの機能を制限する方法を検討する手もあります。正しいクレジットカードの入力を義務付けることで、スパム業者を効果的に防げることがよくあります。