はじめに
突然ですが、皆さんのお子さんはスマートフォンのルールを守っていますか?
我が家では中学1年生の子供にiPhoneを持たせています。
家庭内ルールとして平日の夜22時半以降はスマホ禁止と決めており、iOSのスクリーンタイム機能で休止時間を設定し、さらにパスワードもかけていましたが・・・ある夜、私のiPhoneに通知が届きました。

スクリーンタイムのパスワードが使用されました
最初は「あれ?」と思いつつも放置していたのですが、それが何日か続くようになりました。
さすがにおかしいと思い、子供を呼んで確認しました。

スクリーンタイムのパスワードを使用すると私のiPhoneに通知がくるって知ってる?
子供はすぐに謝ったので今回はお咎めなしとしましたが、気になるのはなぜパスワードをかけているのに突破できたのかという点です。
家族の誕生日や何か連想されるようなパスワードにしていなかったため、本人に確認したところ、返ってきた答えがこれでした。

総当たりでやった
スクリーンタイムのパスワードは4桁の数字です。つまり0000〜9999の1万通り、時間をかければ確かに全部試せます。ロックアウトもなければ試行回数の制限もありません。
これはいわゆるブルートフォースアタック(総当たり攻撃)です。
ちなみに設定していたパスワードは1万通りあるなかのちょうど真ん中あたりなので、0000から試していたら最低でも5000回以上試していたことになります。
子供のスマホに対する執念は半端ではありません。。。
家庭の事件が仕事への気づきになった
普段何気なく触っているEasyBlocksには、これまで様々な記事で紹介してきた通り、Web UIが搭載されており、ブラウザからログインして管理操作を行う仕組みになっています。

「そういえば、このWeb UIに対してブルートフォースアタックをされたらどうなるんだろう?」
現時点では、EasyBlocks リモート監視以外、ブルートフォースアタックに対するアカウントロックやアクセスブロックといった機能は実装されていません。
セキュリティ強化は今後のアップデートで対応していく方針ではあるものの、今できる範囲で何か対策できないか考えました。
機能として防ぐことはまだできなくても、せめて「怪しいログイン試行が続いていること」を検知して通知できれば、管理者が気づいて対応できます。
そこで今回は、Zabbixを使ったログイン失敗の監視ができないかを検討することにしました。
まずはログを観察する
監視の仕組みを作るには、まず「ログイン失敗時にどんなログが出るのか」を把握する必要があります。EasyBlocksには内部でWebサーバーが稼働していて、アクセスログがSyslog経由で出力されます。
実際にログイン失敗と成功を試して、ログを比較してみました。
▼ログイン失敗時
POST /system/login.php HTTP/1.1″ 200 5099
▼ログイン成功時
POST /system/login.php HTTP/1.1″ 302 5100
一目でわかる違いは、/system/login.php へのPOSTリクエストに対するHTTPステータスコードです。失敗時は 200、成功時は 302 が返ってきています。
「200って正常じゃないの?」という疑問
HTTPの200が意味すること
ここで一つ疑問が浮かぶかもしれません。HTTPの200は「正常」を意味するコードではないのか? と。
これは少しややこしいポイントで、HTTPの200は「リクエストの処理が正常に完了した」という意味であって、「ログインが成功した」という意味ではありません。
ログイン失敗時の処理の流れを整理するとこうなります。
①ブラウザが POST /system/login.php を送信
②サーバーがID/PWを検証 → 認証失敗と判定
③「認証失敗というエラーメッセージを表示するページを正常に返した」 → 200
つまりサーバーは「失敗を伝える仕事をきちんとこなした」ので200になり、通信レベルでは何も問題は起きていません。
一方で302はどういう意味かというと、「別のURLへ移動してください」というリダイレクト指示です。ログイン成功時にわざわざ302を使っているのは、「認証が通ったから管理画面へ連れて行く」という意図です。
他のパターンも確認する
「POSTに対して200=認証失敗」という仮説を立てましたが、本当にこれだけで判断して問題ないか、いくつかのパターンも検証しました。
パスワード空欄で送信した場合 → POST → 200 ※失敗と同じ
存在しないユーザー名で試みた場合 → POST → 200 ※失敗と同じ
ログイン済みの状態でlogin.phpに直接アクセスした場合 → GET → 200(ログイン画面が表示される)※POSTではないので監視対象外
結果をまとめると以下の通りです。
| パターン | メソッド | ステータス |
|---|---|---|
| ログイン失敗(ID/PW間違い) | POST | 200 |
| ログイン失敗(空欄送信) | POST | 200 |
| ログイン失敗(存在しないユーザー) | POST | 200 |
| ログイン成功 | POST | 302 |
| セッション有効中にlogin.phpへ直接アクセス | GET | 200 |
どの失敗パターンでも POST → 200 という結果は変わりませんでした。
監視に使うパターンはシンプルに以下の1つで十分だと思います。
“POST /system/login.php HTTP/1.1” 200
整理するとこうなります。
| ステータス | HTTPとしての意味 | ログインの結果 |
|---|---|---|
| 200 | レスポンスを正常に返した | 失敗(ログイン画面を再描画) |
| 302 | 別URLへリダイレクト | 成功(管理画面へ転送) |
つまり正確な読み方は「200が来たから異常」ではなく、「login.php へのPOSTに対して200が返った=ログイン画面に留まった=認証失敗」ということになります。
Zabbixでの監視方針
ログイン失敗の特徴が掴めたので、次はZabbixでの監視設計です。
ログイン失敗1回で即アラートにすると、単純な入力ミスでも通知が飛んでしまいます。ブルートフォースアタックの特徴は短時間に大量の試行を繰り返すことなので、閾値を設けて検知します。
今回は「1分以内に5回以上のログイン失敗」をトリガー条件として設定する方針としました。
具体的なZabbixの設定方法や実際の動作確認については、次の記事で紹介予定です。
次回予告:実際に設定して検知させてみる【後編】
前編では、今回の記事を書こうと考えた背景、EasyBlocksのWeb UIアクセス時ログの分析と監視方針の設計まで行いました。
「POST /system/login.php に対して200が返ってきたらログイン失敗」という明確な判断基準も得られたので、あとは実際にZabbix連携して監視できるか、後編で実践・解説していきます。
後編で実践する内容は以下を予定しています。
・EasyBlocks Syslogにて自分自身のログ受信設定、Zabbix連携機能の設定
・Zabbixサーバーでのトラッパーアイテムとトリガーの設定
・1分以内に5回以上のログイン失敗を実際に再現して検知・通知されるか確認
おわりに
まさか子供のiPhoneブルートフォースアタックから、本ブログに繋がるとは思いませんでしたが、、、日常の出来事が気づきになることは意外とあるのかと思いました。
次回は実際にEasyBlocks SyslogとZabbixの設定を行い、検知できるかどうかを確認していきます。
(ちなみに、スクリーンタイムのパスワードは変えました。。。)
