ENGLISH

サーバーのセキュリティ

外部からの攻撃に対する防御

セキュリティに関しては世の中の認識も向上しており一般常識になりつつあるが、確認の意味もあり「そんなこと当然」という事項も含めて記載する。
今回のプロジェクトで使うインフラは、GCP ( Compute Engine ) + WEBサーバー( nginx ) + Firebase という構成になっている。したがって守る箇所はこの3か所で考える。
Compute Engine を守るポイントは、ファイアウォールとSSHの防御になる。WEBサーバーについてはアプリケーション担当に負うところが大きい。Firebase では、Firestore と Authentication 、Analytics の3つを使用している。
全体に共通する防御のポイントとして、Google アカウントの保護が最重要になる。

Compute Engine


ファイアウォールの設定:
Compute Engine では、デフォルトでファイアウォールが設定されており、SSH / RDP / ICMP /HTTP / HTTPS だけが許可されている状態がデフォルト。
RDP については私の サーバーは Linux でGUIは不要なので即刻無効にした。ICMP についても今のところ使い道がないので無効にしている。
地道な行動がセキュリティの第一歩になる。まだ空いている人がいたら必ず不要なポート塞いで頂きたい。
追加で穴を空ける場合には、当然ながら必要最小限のポートにして、可能な限りフィルタで IP を制限することが必要。

SSH の防御:
デフォルトの状態では、どこからでも SSH 可能な設定になっている。
もし、この状態のまま使用している人がいたら、一度セキュリティログを確認して欲しい。(CentOS の場合、/var/log/secure.log ) 不正ログインを試みた形跡 ( invalid user ) が大量に出ているはずだ。デフォルトの状態ではパスワード総当たり攻撃を大量に受けることになる。
ちなみに、当チームのサーバーへのログインは OS Login にしている[公式ドキュメント:OS Login の設定]。パスワードの設定が無いのでパスワード総当たり攻撃でやられる心配は無い。それでも攻撃を受けていることは間違いないので、攻撃を受けないようにファイアウォールを設定すべきだ。
社内LANからのみアクセスするような場合は、ファイアウォールのフィルタでIPを限定してすれば良いので簡単だ。
当プロジェクトでは色々と事情があり、サーバーへのログインに使用するPCの中にはローカルへのインストールが禁止されているものが含まれている。したがって、端末から直接SSH接続するのではなく、ブラウザからログインする方式とした。ブラウザからアクセスする場合の元のIPは固定されていないので、ファイアウォールのフィルタでIPを限定する方法は使えない。別の方法で SSH 接続に制限を設ける必要がある。
このような場合のセキュリティの確保はGoogle の Identity-Aware Proxy(IAP) を使って実現できる [公式ドキュメント:TCP 転送での IAP の使用]。この設定で、GCP の IAM で許可したGoogleアカウントだけがブラウザ経由でアクセスできるようになる。これで、セキュリティログに invalid user のエラーが出なくなった。

WEBサーバーの防御


開発WEBサーバー:
Compute engine 上には 本番用と開発用のWEBサーバーが動いている。開発WEBサーバーは nginx の設定で アクセス可能な IP を限定している。リモートワークしているメンバーの自宅のIPも設定に追加してある。
なぜ、開発WEBサーバーの方がセキュリティのレベルを上げているかというと、 本番WEBサーバー:
本場WEBサーバーには IP の制限は無いので、あらゆるところからアクセスがある。こちらも、エラーログ ( nginx なら /var/log/nginx/error.log ) を確認してみてほしい。色々なURLを試されているハズだ。私のところにも /db/dbadmin/index.php とか /admin/sqladmin/index.php とか色々来ている。URLから推察するに、メンテナンス系のページを狙っているらしい。この手のメンテナンスの機能を一般に公開している WEBサーバーで使っている場合は即座に消すべきだ。
一般に公開している本番のWEBサーバーでは、アクセスが来ないようにインフラ側でブロックすることはできない。インフラ担当者できることは、メンテナンス系など触られたくない機能はリリースしないことくらいだ。あとは、アプリケーション担当者の仕事である。
当プロジェクトではメンテナンス機能はサーバー上に bash で作ることが多い。ただしWEB画面が欲しくなるメンテナンス機能もあるので、その場合は開発WEBサーバーで動かしている。

index.htm:
デフォルトでは nginx は、indexファイルがなかった時にそのディレクトリ内のファイル一覧を自動表示することは無い。もし表示するようになっているなら、攻撃者に余分な情報を与えることになるから止めた方が良い。

蛇足

WEB サーバーのエラーログの中に robots.txt があったら対応してほしい。
これはハッカー的なアクセスではなく、むしろ積極的に読みに来てほしいアクセス。
robots.txt は検索エンジンのクローラに対して情報を提供している。[公式ドキュメント:robots.txt の概要] を読んで登録しよう。

検索エンジン関連の話題をもう一つ

稼働確認のために "localhost" をWEBサーバーとして動かしているケースは多いと思うが、そこで表示させるページの内容には注意が必要だ。私は本番サーバーと同じ内容を表示させていたので問題が発生した。今はカスタマイズした404エラー表示が出るように変更してある。
詳しくはこちらをご覧ください。[Web サーバーの localhost の設定には気を付けよう]

Firebase

Firestore:
前回のブログで書いた通り、Firestore のルールは[ロックモード]にして、ブラウザ(Javascript)からの直接アクセスは禁止にしている。
詳しくはこちらをご覧ください。複数の Firebase project を使い分ける方法

Authentication:
ユーザーアカウント管理に必要な機能は全部そろっている。絶対にお勧めです。
当チームのアプリは以下のようなフローでユーザー認証を行っている。
  1. アカウント新規作成
  2. 開発対象はビジネスアプリで、保有しているアカウント数に応じて各企業に課金するスキームにしている。企業側でアカウント数をコントロールする必要があるので、アカウントの作成は利用者本人ではなく各企業のシステム管理者が登録する。アカウントにはメールアドレスを使用する。
  3. 初期パスワード
  4. アカウントの新規作成時には初期パスワードはシステム側でランダムな英数字を割り当てている。アカウントを登録するのは利用者本人ではないので、初期パスワードは誰にも判らない状態にしてある。
  5. コンファメーション
  6. アカウント作成に成功したらメールアドレス宛にコンファメーションメールが送られる。メールにはパスワード変更画面(当チームで独自に作成した画面)へのリンク URL を添付しておく(※)。URL にはあるロジックでIDと初期パスワードに変換が可能な、一見ランダムな文字列をパラメータとして付けておく。パスワード変更画面ではこれを復号してパスワード変更の処理に使用する。
  7. パスワード失念
  8. パスワード変更の申請をすると、メールアドレスにパスワード変更画面(Google が用意しているデフォルトの画面)へのリンクが届く。
  9. 初回ログイン
  10. 初期パスワードは誰にも判らないので、コンファメーションメールを受領しているか、パスワード変更の申請からメールを受け取るか、どちらかの方法でパスワード変更しない限りログインはできない。どちらかの方法で、そのメールアドレスの正当な管理者であることが確認されたアカウントだけが使用可能となる。
アカウント管理関係のロジックは共通プログラムとして提供しているので、複数のプロジェクトで同じ仕組みが使用できる。

※ ここで困ったことが発生した。
コンファメーションメールにパスワード変更画面のリンクを添付するには以下のようにすればよい。公式ドキュメント:メール アクションで状態 / 続行 URL を渡す


 const firebase = require('firebase');
 ........
 let actionCodeSettings = {
  "url": "https://exsample.com/change_password.html" + "#" + 「ID と初期パスワードが入ったパラメータ」,
  "handleCodeInApp": false,
 };
 firebase.auth().currentUser.sendEmailVerification(actionCodeSettings)


ここで使われている Firebase Authentication はフロントエンド用のライブラリで、バックエンド用には Admin SDK しか用意されていない。そして Admin SDK にはリンク付きのコンファメーションメールを送る機能が無い。
つまり、JavaScript ではリンク付きのメールを送れるが、python では送れないのだ。
バックエンドで JavaScript を実行するニーズが生じたため、nodeserver を起動することで対応した。

Analytics:
統計情報を取得するためのもので、アプリケーションの要件と関係が無いので、apiKey は別管理にしている。
各アプリケーション用の Firebase プロジェクトには、Analytics 専用にアプリケーションを登録している。(Firebase の、プロジェクトの概要=>プロジェクトの設定=>全般=>アプリを追加)
ここで作成した apiKey には HTTP リファラーを設定して、 Analytics に最低限必要な権限だけを設定している。許可している権限は Firebase Installations API と Firebase Management API の二つである。
権限の内容が極小なので、この apiKey は公開しても何ら問題ないが、複数の Firebase プロジェクトで使い分ける必要があるため、サービスアカウントのキーファイルと同様な管理をしている。

Google Account の保護:

当然と言えば当然だが、Google のクラウド環境を守る上で最重要の保護対象が Google Account である。ここを突破されたら全てが崩壊する。当然 Google Account は2段階認証を設定している。
Google Account は IAM で管理されるが、インフラ担当者のみが登録されている。アプリ担当者はサーバーにログインする必要が無いので登録しない。
インフラ担当者がクラウドの管理に使用する Google Account はインフラ管理業務専用に作成したものを使用しており、別の目的でこのアカウントを使用することは禁止。インフラ担当者に配布するアカウントの2段階認証用の電話番号は全部システム管理者の番号にした方が良い。普段と違うPCを使用する場合は必ず管理者の許可が必要になる。
管理者は、インフラ担当者からスマホやタブレットの使用申請が来ても許可してはいけない。一旦それらが信頼できるデバイスとして登録されていまうと、そこを起点に別のデバイスにログインできるようになってしまう。