問い合わせフォームにボット対策を入れようとして、先に別のことが分かりました。
いまの作りでは、ブラウザ側にどんな対策を入れても効きません。
送信先のURLが配信されるJavaScriptに入る作りになっていた話と、その直し方です。あわせて、画像の文字を読ませる方式を選ばなかった理由も書きます。
先頭の6文字が、置き場所を決めていた
このサイトはNext.jsで作っています。環境変数の名前には、決まりがあります。
| 名前 | どこから見えるか |
|---|---|
CONTACT_ENDPOINT |
サーバーだけ |
NEXT_PUBLIC_CONTACT_ENDPOINT |
ブラウザにも配られる |
名前の先頭に NEXT_PUBLIC_ が付いていると、その値は組み立てのときにJavaScriptの中へ埋め込まれます。訪問者に配られるファイルの一部になる、ということです。
フォームはブラウザの中で動く部品なので、送信先を知る必要がありました。だから素直に NEXT_PUBLIC_ を付けていました。付けなければ読めないので、それ以外に選びようが無いように見えていました。
鍵の掛かっていない扉が、隣に並ぶ
この形だと、送信先のURLはフォームを開かなくても分かります。配られたファイルを開けば書いてあります。
見えない入力欄を足しても、送信までの時間を測っても、それらはフォームを使った場合にだけ効きます。
送信先を直接叩く相手には、そもそも通り道が違うので関係がありません。鍵を掛けた扉の横に、鍵の掛かっていない扉が並んでいる状態でした。
動かしていなかったから、漏れていなかった
ここで確認したところ、本番の環境変数は設定されていませんでした。
フォームは、送信先が無いときは「お問い合わせ先は準備中です」と出す作りにしてあります。つまり公開されてからずっと、フォームは表示されておらず、URLもどこにも出ていませんでした。
結果として何も漏れていませんが、設定を入れた瞬間に漏れる形のまま置いてありました。
この種の問題は「まだ起きていない」と「起きない」の区別が付きにくく、動かし始めた日にまとめて効いてきます。
ついでに言うと、フォームが出ていなかったこと自体も問題でした。問い合わせ先として案内していた場所が、ずっと準備中だったということになります。
自分のサイトに窓口を作る
直し方は、間に1枚挟むことでした。
| 前 | 後 | |
|---|---|---|
| 送信先 | ブラウザ → 外部サービス | ブラウザ → 自分のサイト → 外部サービス |
| URLの在り処 | 配られるJavaScript | サーバーだけが持つ |
| 判定の場所 | ブラウザ(迂回できる) | サーバー(迂回できない) |
ブラウザから見えるのは /api/contact だけになります。ここを直接叩くことはできますが、叩いた先で何を見ているかは分かりません。
画像の文字を読ませる方式は、入れなかった
ボット対策というと、ゆがんだ文字や、信号機の写真を選ばせるものが思い浮かびます。今回は入れていません。
問い合わせは、そもそも件数が多くありません。1日に何百件も来るものではない。
それに対して、1件取りこぼす損のほうが大きいと考えました。迷惑な送信を100件止める価値より、相談しようとした人が1人あきらめる損のほうが重い、という判断です。
人がやらないことを、3つ見る
代わりに置いたのは、訪問者の手間が一切増えない3つです。
| 見ること | 人がやらない理由 |
|---|---|
| 見えない欄が埋まっている | 画面に出ていないので、触りようがない |
| 開いてすぐ送っている | 3秒で文章は書けない |
| 短時間に何度も送っている | 同じ相談を10分に4回はしない |
どれも、正しく使っている人には何も起きません。止めるために何かをさせるのではなく、やっていないことを確認する形にしています。
見えない欄の名前を、慎重に決めた
ひとつだけ、注意が必要な部分があります。見えない欄の名前です。
この欄を name や url のような一般的な名前にすると、ブラウザやパスワード管理ソフトの自動入力が、画面に出ていなくても勝手に埋めることがあります。
この欄が埋まっている送信は、機械とみなして捨てます。しかも成功したように見せて捨てます。理由を返すと、次からその欄を避ければ通ると分かってしまうからです。
つまり自動入力に埋められた人は、送信したつもりで、何も届いていない状態になります。これがこの仕組みの一番の失敗です。
自動入力が狙わない名前にしたうえで、キーボードの移動先からも読み上げの対象からも外しています。
回数の数え方には、限界がある
正直に書いておくと、短時間の回数を数える部分は完全ではありません。
このサイトのサーバー側の処理は、必要なときに起動して、しばらくすると消えます。数えた記録は共有されず、消えるときに一緒に無くなります。厳密には数えられません。
それでも、続けて叩いてくる相手は同じ実体に当たりやすいので、一番困る「短時間に何百件」は止まります。正確さが要るようになったら、外に記録を置くことになります。いまの流量でそこまで持つ必要は無いと判断しました。
今回の学び
まず、名前の先頭6文字が、置き場所を決めていたということ。NEXT_PUBLIC_ は便利な省略ではなく、「これは配る」という宣言でした。付けなければ読めないから付ける、では順番が逆でした。
次に、ブラウザ側の判定は、通り道が1本のときにだけ効くこと。もう1本、鍵の掛からない道が並んでいれば、そちらが使われます。対策を足す前に、道が何本あるかを数える必要がありました。
そして、止めるために手間を増やさないこと。問い合わせのように件数が少ないものでは、止めた数より取りこぼした数のほうが重くなります。人がやらないことを確認するだけなら、正しく使う人には何も起きませんでした。

コメントを残す