ELBのターゲットでWidnows認証を利用する場合はALBではなくNLBが必要
はじめに
先日、ALBのターゲットにWindows認証を用いるIISのWebサーバーを配置する構成のWebシステムを構築していました。
Webシステムは認証が通るとユーザーの情報をDBから取得する一般的なシステムです。
このシステムにおいて他のユーザーの情報が表示されるという事象が起きました。
取り扱う情報によっては危険な状態ですね。
なぜこのような事象が発生したのか、また対処法についても調べてみました。
先にまとめ
AWSのロードバランサーのターゲットにWindows認証を用いる場合、ロードバランサーはALBではなく、NLBが必要です。
Windows側の条件
Windows認証では、クライアント ↔︎ サーバー
間で単一のTCP接続内で認証情報をやりとりする必要がある。
ALB
ALBは クライアント ↔︎ サーバー
間の一貫したTCP接続をサポートしていない。
ALBを中継する場合はクライアント ↔︎ ALB
とALB ↔︎ サーバー
の2つの接続が確立される。
NLB
NLBは クライアント ↔︎ サーバー
間のTCP接続をサポートしている。
全体像
まずは今回事象が発生した構成を記載します。
次に対策を行なった構成です。
変更点はロードバランサーの部分のみですね。
事象
まずはどのような手順でどのような事象が起きたか確認します。
- ユーザーAがALB経由でWebサーバーへアクセス
- ユーザーAは認証情報を入力してログイン
- その後、別の端末でユーザーBがALB経由でWebサーバーへアクセス
- ユーザーBは(意図せず)ユーザー認証なしでログイン
- ユーザーBの画面にユーザーAの認証情報でログインしないと表示されないデータが表示される
なぜこんなことが起こるのか
なぜこのような事象が起こるのでしょうか?
Windows認証の仕様
まずはWindows認証を利用する際の注意点を理解します。
ロードバランサーのターゲットでWindows認証使用する際の注意点としてAWSのドキュメントに以下の記載がありました。
Windows認証では、クライアントからサーバーへの接続において送信元ポートが保持される必要があります。TCPリスナーを備えたネットワークロードバランサーは、負荷分散された接続において送信元ポートを保持します。そのため、Windows認証を使用する場合は、ネットワークロードバランサーを使用してください。
参照: Windows認証でロードバランサーを使用する
Windows認証を利用する際、送信元ポートが保持されている必要があるようです。
つまりALBは送信元ポートを保持する機能を持たないため、Windows認証を利用する場合は送信元ポートを保持できるNLBを利用する必要があるということです。
なぜ送信元ポートの保持が必要なのか気になったので、Windows認証に関するドキュメントも確認してみました。
以下はKerberos認証/NTLM認証に利用される認証スキームに関する説明を抜粋したものです。
ネゴシエート認証が正しく機能するためには、同じ接続で複数の交換を行う必要があります。
参照: WinHTTP での認証
統合Windows認証では同じTCP接続で認証情報の交換が行われる必要があるようです。
AWSのドキュメントに記載されていた「送信元のポートを保持する必要がある」というのは、この同じ接続で認証情報を交換するために必要ということだと思われます。
つまりターゲットでWindows認証を利用する場合、クライアント ↔︎ ターゲット
間でTCP接続が確立され、その中で認証情報を交換する必要があるということが分かりました。
それではAWSのロードバランサーで上記を実現するためにはどのロードバランサーが適切なのか考えてみます。
ALBの場合
まずはALBから見ていきましょう。
Classic Load Balancer と Application Load Balancer はどちらも接続多重化を使用します。つまり、複数のフロントエンド接続を持つ複数のクライアントからのリクエストを、単一のバックエンド接続を介して特定のターゲットにルーティングできます。
参照: Elastic Load Balancingの仕組み
上記のドキュメントからも分かる通り、ALBは複数のフロントエンド接続(クライアント↔︎ALB
)とバックエンド接続(ALB↔︎ターゲット
)の2つのTCP接続を確立します。
つまり、ALBで一度TCP接続が終端されるため、Windows認証に必要な同じ接続での認証情報の交換は実現できません。
ここからは推測ですが、ALBを利用した場合は次のようなフローで別のユーザーのデータが表示されていると推測されます。
まずクライアントAがALB経由でターゲットに接続します。
この時、クライアントA ↔︎ ALB
とALB ↔ ︎ターゲット
の2つの接続が確立されます。
次にクライアントBがALB経由でターゲットに接続します。
この時、新たにクライアントB ↔︎ ALB
とALB ↔ ︎ターゲット
の2つの接続が確立されることを期待します。
しかし、実際はクライアントB ↔︎ ALB
は新しい接続が確立されますが、ALB ↔ ︎ターゲット
間は既存の接続が再利用されます。
その結果、ターゲットから見ると「このリクエストはすでに認証済みのユーザーA(実際はALB)だからユーザAの情報を返そう」となり、ユーザーBの画面にユーザーAの情報が表示されるということだと推測されます。
NLBの場合
次はNLBを見ていきます。
ブログから下図を拝借していますが、NLBはTCP接続を終端せずにクライアント ↔︎ ターゲット
間で直接3ウェイハンドシェイクを実行することができます。
これにより、クライアントがNLB経由でターゲットに接続した場合、クライアント毎にクライアント ↔︎ ターゲット
間の接続が確立されます。
NLBを利用することでクライアント毎に単一の接続を確立することができ、同じTCP接続で認証情報を交換することができるようになります。
まとめ
ロードバランサーのターゲットでWindows認証を利用する際の注意点について書きました。
普段利用している中でTCPレベルのコネクションまで気を使うことはなかったので、改めてAWSのロードバランサーがどのようにリクエストを処理しているか学ぶことができました。
ALB + Windwos認証という組み合わせ自体、最近はあまり多くはないかもしれませんが、別ユーザーの情報が表示されるとなるとデータ次第では大きな問題になり得ます。
もし周りにALB + Windows認証を検討されている方がいたら、NLBが必要であることを伝えてあげてください。
また、ALBが使えないことでALBの機能(細かいルールベースのルーティングやmTLSなど)が使えなくなるため、そういった要件がある場合はWindows認証以外の認証方式も検討するのもいいかもしれません。