いちばんやさしいスパムの殺しかた【スパム対策：スパムの登録防止から削除方法まで】


#概要

**この記事は、『運営しているWebサービスにスパムが登録してきたときの対処法』について書いたものです。**


この記事は、自分が開発している英単語学習サービス「BooQs」に、スパムが大量に登録してきた実体験をもとに書いた備忘録です。

（利用技術：Rails5.1）


###前提：スパムを放置することで発生する不利益

反省点として、
正直なところ、自分はスパムへの対応に遅れました。
スパム登録に気付いてからもしばらくは、何も対応を取りませんでした。

その理由は２つあります。
１つは、多忙によりスパムへの対応よりも優先すべきと思われるタスクが山ほどあったこと。
**そしてもう一つは、自分自身、スパムに登録される不利益について知らなかったため、スパム対策の優先順位がなかなか上がってこなかったためです。**

この反省を踏まえて、まずスパムによって引き起こされる不利益を、運営者の視点とユーザーの視点から紹介します。

これを読めば、すぐにスパムに対応したくなるはず。

####【運営者の不利益】：重要な指標の精度が下がる
リーンスタートアップを読まれた方には馴染みがあるかと思いますが、
サービスが示す数字には、「虚栄の指標」と「行動につながる指標」の２つがあります。
虚栄の指標とは、サービス運営者の虚栄心を満たすだけの意味のない数字で、
行動につながる指標とは、実際にサービスがユーザーに求められていることを示すサービスの成長につながる数字です。
虚栄の指標は、PVやユーザー登録数がよく挙げられ、行動につながる指標には、定着率やアクティブユーザー率などがよく挙げられます。

もちろん、これらの指標はプロダクトの性質によって変わってきますが、少なくともBooQsにとっては、ユーザー登録数は「虚栄の指標」でした。

**それもさらに悪いことに、スパム登録は、虚栄の指標を増やすだけでなく、全体に対するアクティブユーザー率のような「行動につながる指標」の精度を下げるものでした。**

スパムによって見かけ上のユーザー登録数が増えるのは、案外、運営者にとっては悪くない心地です。
しかしそれによって本当に意味のある指標の計測が阻まれるなら、自己満足は排除するのが正しい選択です。

つまり、スパム滅ぶべし。

####【運営者の不利益２】：すごい速さで増えていき、時間が経つほど削除が大変になる。

スパムは放置していると、すごい速さで増えていきます。
それもスパムは、登録してくるメールアドレスのバリエーションも多様です。
このあと「スパムの削除法：STEP3」で見ていくように、スパムの削除では、コンソールからメールアドレスのドメインでUserに検索をかけていくことになるので、スパムのメールアドレスのバリエーションが増えれば増えるほど、削除も大変になってきます。
僕はもたもたしていた結果、770人もスパムの登録を許してしまいました。
削除、大変だったなぁ...。

つまり、スパム滅ぶべし。

####【運営者の不利益３】：スパムではないユーザーまで削除してしまう危険が増える

このあと「スパムを削除する方法：STEP2」で紹介するように、スパムを一括で削除するには、登録期間で登録ユーザーを検索することになります。

**そのため、スパムの放置が長引けば長引くほど、スパムではないユーザーまで削除してしまう危険が増えます。**

スパムを確認したら、理想はその日のうちに、できればその週のうちに、遅くてもその月のうちには対処するようにしましょう。

というかむしろ、現時点でスパムに襲われていなくても、現在運営しているサービスに次に紹介するreCAPTCHAを導入してもいいくらいだと僕は思いました。

つまり、スパム滅ぶべし。


####【利用者の不利益】：本来のメアドの持ち主がサービスに登録できなくなる

BooQsを含めたほとんどのWebサービスは、重複したメールアドレスでユーザー登録できないようにバリデーションをかけています。
**つまり、スパムによって勝手にメールアドレスを登録されたユーザーは、そのサービスに登録できないということです。**
このことに気づいたのは、下のようにgravaterでアイコンが設定されたスパムを見かけた時でした。

<img width="958" alt="スクリーンショット 2020-07-02 14.57.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253667/ff6f8eb7-15d8-b092-2567-192ecff073ac.png">

スパムユーザーにgravaterアイコンがあるということは、そのメールアドレスが誰か実在の人物が利用しているものだということです。
スパムによるなりすましを防ぐためにも、スパムを見つけたらすぐに対応しましょう。

つまり、スパム滅ぶべし。

（ほかにも自分が思い至らなかった不利益があれば、ぜひ教えていただけると幸いです🙇‍♂️）


#1, スパムの登録を防ぐ方法

スパムを見つけたら、まず登録画面にreCATPCHを導入しましょう。
reCAPTCHとは、下の画像の「私はロボットではありません」というあのおなじみのフォームです。
きっと誰もが一度は見たことあるはず。

<img width="939" alt="スクリーンショット 2020-07-02 15.38.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253667/7c15f994-1bbd-1981-8d6b-f282308c079a.png">

reCAPTCHの導入は、Railsなら下の記事が詳しいです。
[RailsでreCAPTCHA対応](https://qiita.com/k_tada/items/eee5a3965fb5abadab13)

**スパムを見つけたら、reCAPTCHだけでもすぐに導入することをオススメします。**

（reCAPTCHの他にも、スパム対策にipでbanするなども試しましたが、自分に襲いかかってきたスパムはなぜか全員ipアドレスが異なっていたため、防壁の役割を果たさなかったようでした。）



#2, 登録済みスパムを削除する方法

ここからは、すでに登録されてしまったスパムを削除する方法について紹介します。


##STEP1：非アクティブユーザーを絞り込む

スパムの弱点は、**『登録しかしない』**ところです。
プラットフォームやそのユーザーに利益をもたらしてくれる活動は、何一つしません。
彼らは、アクティブユーザーではないのです。
そのため、**非アクティブユーザーを絞り込むことで、ユーザー全体からスパムユーザーを絞り込むことができます。**
Railsなら、非アクティブユーザーは、ユーザーの行動によって生成される、Userモデルに関連づけられているレコードが存在すれば、簡単に導き出せます
以下はBooQsの例です。
（BooQsでは、ユーザーが問題を解くことでAnswerHistory（解答履歴）レコードが生成されます。）

```ruby:非アクティブユーザーを絞り込む.rb
active_user_ids  = AnswerHistory.group(:user_id).pluck(:user_id)
non_active_users = User.where.not(id: active_user_ids)

non_active_users.count
=> 1747

```
登録後に一度も問題を解いたことのない非アクティブユーザーは、1747人でした。

この1741人からスパムを見つけ出して削除すれば良いということになります。

##STEP2：登録期間で絞り込む

スパムが登録してきた期間がわかるなら、登録期間で非アクティブユーザーを絞り込むことで、スパムを絞り込むことができます。
**このため、スパムの登録を確認したら、reCAPTCHの導入などの対応を迅速に行いましょう。**
理想としてはその日のうちに、できるならその週のうちに、遅くてもその月のうちに、スパム登録の防止策を取るようにしましょう。
Railsを使っているなら、次のようにスパムかもしれないユーザーを絞り込めます。

```ruby:非アクティブユーザーを期間で絞り込む.rb
#その日のうちにスパムに対処できた場合
daily_non_active_users = non_active_users.where(created_at: "2020-05-15".in_time_zone.all_day)
#その週のうちにスパムに対処できた場合
weekly_non_active_users = non_active_users.where(created_at: "2020-05-15".in_time_zone.all_week)
#その月のうちにスパムに対処できた場合
monthly_non_active_users = non_active_users.where(created_at: "2020-05-15".in_time_zone.all_month)
#その四半期のうちにスパムに対処できた場合
quarterly_non_active_users = non_active_users.where(created_at: "2020-05-15".in_time_zone.all_quarter)
#その年のうちにスパムに対処できた場合
yearly_non_active_users = non_active_users.where(created_at: "2020-05-15".in_time_zone.all_year)

```


[参考：16 Timeの拡張](https://railsguides.jp/active_support_core_extensions.html#time%E3%81%AE%E6%8B%A1%E5%BC%B5)


BooQsの場合、スパムへの対応が遅れたため、５月の初めから終わりまでがスパムの登録期間となりました。
結果として、`"2020-05-15".in_time_zone.all_month`を使って検索をかけたところ、スパムの可能性のあるユーザーは、821人まで絞り込めました。




##STEP3：ドメインで絞り込む
スパムユーザーの情報を観察していると、彼らが日本人には馴染みのないメールアドレスで登録してきていることがわかります。

`@comcast.net`、`@verizon.net`、`@sbcglobal.net`、`@bellsouth.net`、`@yahoo.com`などの海外サービスのドメインを利用して登録しているのです。

そのため、特定の期間に登録してきた非アクティブユーザーを、これら海外ドメインで絞り込むことで、スパムを絞り込むこともできます。

```ruby:ドメインで絞り込む.rb
spams  =  monthly_non_active_users.where(['email LIKE ?', "%comcast.net%"])

spams.count
=> 158

```
これで一応、スパムたちを絞り込み作業は終わりなのですが、一つ注意点もあります。
それは、ここまで絞り込んでもまだ、スパムではない一般ユーザーを削除する可能性を完全に排除できてはいないということです。
日本のユーザーの中にも、海外のドメインを利用している方もいるかもしれません。
実際、BooQsのスパムではなさそうなユーザーの中にも`@yahoo.com`を利用されている方が3人いらっしゃいました。
幸い、彼らの登録はスパム登録期間の外だったため削除には至りませんでしたが、ここには重要な事実が隠れています。
**それは、このスパムではないユーザーまで削除してしまう危険性は、スパム登録への対応が遅れ、スパム登録期間が長くなるにつれて高くなるということです。
なので、reCAPTCHなどのスパムへの対応は、スパムの存在に気づいたらすぐに行いましょう。**
　
さぁ、あとはこの絞り込んだスパムを削除するだけなのですが、Railsの場合、ちょっと気をつけるべき点もあります。
次に進みましょう。


##STEP4：スパムはdestroy_allで削除する。

1ヶ月くらい放置していると、スパムは平気で数百人を超えてきます。
こうした大量のユーザーを削除しようとするとき、思いつくのは、`spams.destroy_all`か`spams.delete_all`の２種類の方法です。
　
もしあなたが、User作成時に、Userと関連づけられたモデルのレコードも同時に自動で生成していないのなら、`delete_all`を使っても構わないでしょう。

しかし、もしそうでないなら、`destroy_all`を使うべきです。
　
**なぜなら`delete_all（とdelete）`は、ActiveRecordを介さず直接SQLを実行して削除するメソッドなので、
関連づけられたレコードも一緒に削除してくれる`dependent: :destroy`が効かないからです。**

自分は、このことを忘れてやらかしました。
BooQsでは、Userモデルには、「解答設定」を決めるAnswerSettingモデルをhas_oneで紐づけられています。
そしてユーザーが新規登録するときには、自動でデフォルトの設定をもつAnswerSettingsレコードを作成して、ユーザーに紐づけます。
しかし僕は、大量のスパムを削除するときに、（すぐに処理が終わるからという安直な理由で）delete_allを使ってしまったのです。
結果的にDBに残ったのは、親を失った大量のAnswerSettingたちでした。
　
**スパムを削除するときは`destroy_all`を使いましょう。**


```ruby:スパムはdestroy_allで削除する.rb

spams.destroy_all
```


##結論
今回削除したスパムユーザーの数は、**770人**でした。
821人の月間非アクティブユーザーのほとんどを占めていたということになります。ひどい。

しかし、これでもスパムを全員削除できたとは限りません。
スパムユーザーと思しきものの中には、`@gmail.com`や`@icloud.com`や`@hotmail.com`など日本でもよく利用されているドメインで登録されているものもありましたが、流石にそれらには怖くて手がつけられませんでした。

**こうした事態を避けるためには、自分のようにスパムへの対応を送らせず、遅くてもスパムを確認したその週にはスパムの登録防止策を講じるべきです。**

というか、なぜスパムさんはうちのような弱小サービスを狙ってきたのでしょうか？
何が目的なのでしょうか？
本当に疑問です。

ともあれ、スパムは滅ぶべきだと考える次第である。
　

（今回のスパム対策では、[運営者ギルド](https://qiita.com/organizations/admin-guild)の方々にとても助けられました。
それにつけても運営者ギルドは存続させるべきである。）

##宣伝

スパムにも大人気の英単語学習サービス、[BooQs](https://www.booqs.net/)を開発しています！
200万回以上も解かれた、BooQsの中でもとくに人気のあるコンテンツは、[NGSL（New General Service List）と呼ばれる、一般的な英文の９割を網羅した英単語帳](https://note.com/kawanjin01/n/na861d9264699)です。
ぜひご利用くださいませ！




<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">BooQs（ブックス） は、<br>楽しく効率的に英単語を覚えられるサービスです。<br><br>現在、『基礎英単語』『学術英単語』『TOEIC英単語』『ビジネス英単語』を学ぶことができます。<br><br>ぜひご活用ください！！<a href="https://twitter.com/hashtag/BooQs?src=hash&amp;ref_src=twsrc%5Etfw">#BooQs</a><a href="https://t.co/Ygvf3QeZKL">https://t.co/Ygvf3QeZKL</a></p>&mdash; 挫折させない英単語サービス『BooQs』 (@BooQs_net) <a href="https://twitter.com/BooQs_net/status/1240537593806086145?ref_src=twsrc%5Etfw">March 19, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>