TCP では盗聴をはじめとするさまざまな攻撃があり得るので注意が必要です。
TCP のパケットで送られるデータは, アプリケーションプログラムが送信したデータそのままです。
しかも, 「SEQ 番号」を見ればそれがデータの何バイト目であるか分かりますから, 通信路の途中でパケットを盗み読むことができれば, 双方のホストが送信したデータの流れを再構成することは極めて簡単です。 暗号化していないデータは盗み読みされるものと思っていた方が無難です。
使い捨てパスワードを使っていれば, たとえ盗み読みされても, 盗聴者には次のセッションで必要なパスワードを知ることができないから安全, と思っている人はいませんか ?。 残念ながら盗聴できる場合はハイジャックも可能である場合が多いので, 使い捨てパスワードだから安心とは限らないのです。
例えば, 正規ユーザーが正しい使い捨てパスワードを入力して サーバーへログインしたとします。 この段階で盗聴者はこのセッションをハイジャックしてしまうことが可能です。 なぜなら盗聴者はサーバーが送信した ACK パケットの 「SEQ 番号」と「ACK 番号」を知っていますから, 正規ユーザーの代わりに偽造パケットをサーバーへ送りつけることが可能です (図 8)。
図 8 セッションのハイジャック |
サーバーは, 正規ユーザーが送信したパケットと 攻撃者が送信した偽造パケットを区別できない。 |
サーバーは, 正規ユーザーが送ったデータと攻撃者(ハイジャッカ) が送ったデータを区別することはできませんから, 攻撃者が送ったコマンドをそのまま実行してしまうことでしょう。
図 8 のように, サーバーが攻撃者に対して送った ACK パケットが正規ユーザーのマシンにも届いた場合は, 何もデータを送っていないのに「ACK 番号」が突然増えたことから, ハイジャックされたことを検知できますが, 攻撃者が何らかの方法によって*18, サーバーが送ったパケットが正規ユーザーに届かないようにしてしまえば, 正規ユーザーはハイジャックされたことに気付くのが難しくなります。
さらに, 攻撃者がクライアント側(正規ユーザー)に対しても, 偽造データ・パケットを送って, サーバーの応答の真似をすれば, 正規ユーザーにハイジャックされたことを全く気付かせることなく, サーバーを自由に操作することも不可能ではありません(図 9)。
図 9 二重のハイジャック |
攻撃者は, サーバーのふりをすることによって, 正規ユーザーがハイジャックに気付かないようにできる。 |
ハイジャックを防ぐには, クライアント・プログラム, サーバー・プログラムの両アプリケーション間で相互に認証を行い, かつ相互にやりとりするデータのすべてを暗号化する必要があります。 つまり TCP/IP 接続を含め, 両ホストの間にあるものは何であれ信用してはいけない*19 ということです。
盗聴やハイジャックは, 攻撃者が通信線路上のどこかに細工する必要があります。 可能性のあるのは次の 3 カ所のうちのいずれかでしょう。
(1) | サーバーあるいはクライアントが属する LAN |
(2) | アクセス回線(電話加入者回線など) |
(3) | プロバイダ内部 |
(4) | 専用線(電話局間回線など) |
(4)での細工は, 普通の攻撃者にとっては不可能でしょう。 (2)と(3)は場合によっては可能かも知れませんが, 普通は困難でしょう。 したがってほとんどの場合, 盗聴やハイジャックは(1)で行われると考えて良いでしょう。 つまり攻撃者は攻撃対象のすぐ近くにアクセス*20 できる必要があります。 攻撃者にとって攻撃可能対象はかなり限定されます。
一方, どこのサイトのサーバーに対しても行える攻撃が, IP アドレス・スプーフィング(spoofing ,だますこと)と次で説明する SYN フラッディング(flooding ,氾濫させること)です。 逆に言うと世界中のどこからでもこの攻撃を受ける可能性があるわけで, より注意深く警戒する必要があります。
図 4 の3-ウエイ・ハンドシェイクにおいて, 接続元のホスト A が送るパケットの「送信元アドレス」を, ホスト A の IP アドレスではなく, 全く関係のないホスト C の IP アドレスに設定して, 接続要求パケットを送信したらどうなるでしょうか?。
接続先のホスト B に届く前に, 通信路上のルーターなどの設定によっては, 「送信元アドレス」が異常ということで排除されてしまうこともあるのですが, ここでは間にそのような設定のルーターがなく, ホスト B に届いたと仮定します。
するとホスト B は, 届いたパケットの「送信元アドレス」すなわちホスト C に対して, SYNACK パケットを送信します。 ホスト C がホスト A と全く関係ないサイトにあるマシンであれば, ホスト A でこの ACK パケットを受信することはできません。
したがってホスト A は, ACK パケットに設定されている「SEQ 番号」を知ることができず, 3-ウエイ・ハンドシェイクの ACK パケットの 「ACK 番号」に何を設定したら良いか分からないので ハンドシェイクを完結することができない, だから安全だ, というのが TCP/IP が作られた当時の考え方でした。
ところが, ホスト B のシーケンス番号を予測することは不可能ではありません。 ずばり番号を言い当てることは難しいにしても, ある程度の範囲内で予測できれば, 試してみればいいだけです。 「ACK 番号」に予測した値を入れたパケットを入れた ACK パケット送り, 続いてデータを送ります。
運良く「ACK 番号」がホスト B のシーケンス番号に一致すれば, ホスト B は「ESTABLISHED 」状態になり, 続いて送られてきたデータをホスト C から送られたデータと 思い込ませることができます。
これが, IP アドレス・スプーフィングです。 サーバーが, 特定の IP アドレス(上記の例で言えばホスト C)からの接続を 盲目的に信頼する設定になっていると, この種の攻撃のえじきになります。
例えば rsh(リモート・シェル)は, ~/.rhosts ファイルに特定のホスト名(あるいは,IP アドレス) を書いておくことにより, そのホストからパスワードなしでアクセスできますが, 攻撃者が ~/.rhosts に書いてあるホスト名を何らかの方法で知れば, そのホストからの接続と見せかけて任意のコマンド*21 を実行することができます。
この種の攻撃を防ぐには, 送信元アドレスだけの認証を行わず, 必ず他の認証方法と組み合わせることが重要です。
3-ウエイ・ハンドシェイクにおいて, サーバー側(ホスト B)が SYNACK パケットを送信した後, クライアント側(ホスト A)が ACK パケットを送らないとどうなるでしょうか ?。 サーバーは一定時間待ってもACK パケットが帰ってこないと, SYNACK パケットが途中で失われたと判断して SYNACK パケットを再送します。 しかし, そのためにはクライアントから送られてきた SYN パケットに含まれる, 送信元アドレス, ポート番号, 「SEQ 番号」, そして SYNACK パケットで送った自分のシーケンス番号を 記憶し続けなければなりません。
もちろん, 一定回数再送を試みても ACK パケットが帰ってこなければ, あきらめて記憶内容を破棄するのですが, その前にクライアントが繰り返し SYN パケットを送りつけると, そのたびにサーバーは SYN パケットに含まれる情報と SYNACK パケットで送った自分のシーケンス番号を記憶しなければならず, ついには記憶領域を食いつぶして, 他の TCP/IP 接続を受け付けられなくなってしまいます。 この攻撃は「SYN フラッディング」と呼ばれます。
盗聴や侵入などの直接的な被害を与えるのではなく, 他のユーザーに対するサービスを妨害する攻撃を, 一般に DoS(Denial of Service, サービス妨害)攻撃と呼びますが, SYN フラッディングは他の DoS 攻撃と比べて, 手軽に攻撃できてしまうという(攻撃される側にとっては困った)特徴があります。
SYN フラッディングを回避するために, 最近の Linux には「SYN クッキー」と呼ばれる仕掛けが組み込まれています。 これは SYNACK パケットで送った自分のシーケンス番号を記憶する代わりに, 一方向ハッシュ関数を用いて SYN パケットからシーケンス番号を算出する*22 方法です。 算出した番号(これをクッキーと呼びます)を「SEQ 番号」に設定して SYNACK パケットを送信し, ACK パケットが送られてきたら, 再び同様の方法でクッキーを算出して, その値に 1 を加えた値が ACK パケットの「ACK 番号」に一致していたら ハンドシェイク成功とみなします(図 10)。
図 10 SYN クッキー方式 |
SYNACK パケットの「SEQ 番号」に設定する値を, 相手の IP アドレスなどに基づいてハッシュ関数で算出し, その値を保持しない。 |
SYN クッキーを有効にするには, Linux カーネル構築時に「CONFIG_SYN_COOKIES」項目に対して「Y」と答え, さらに図 11 を実行する*23 必要があります。
echo 1 >/proc/sys/net/ipv4/tcp_syncookies |
図 11 SYN クッキーを有効にするための手順 |
ただし, SYN クッキーは根本的な解決策ではありません。 SYN クッキーのアイデア自体は前述した通り簡単なものですが, 実装方法は複雑です。 詳しくは, http://cr.yp.to/syncookies.html を参照していただくとして, ここでは SYN クッキーの問題点を簡単に説明します。
例えば, SYNACK パケットを送信後, クライアントが送信した ACK パケットが通信エラーなどの原因で 途中で失われてしまった場合を考えます(図 12)。 この場合, 再送の責任はサーバー側にあるのですが, サーバーは SYNACK パケットの内容を記憶していないので, SYNACK パケットを再送することができません。 哀れなクライアントは, 「ESTABLISHED」状態のまま永遠に待ち続けることになります。
図 12 SYN クッキーの問題点 |
SYNACK パケットに対する ACK パケットが途中で失われると, クライアントは「ESTABLISHED 」状態のまま永遠に待ち続ける羽目になる。 |
このような問題点を回避するため, Linux における SYN クッキーの実装では, 攻撃を受けていないときは, SYNACK パケットの内容を記憶して再送に備えるようになっています。 そして SYN フラッディングを検出した時に限って SYNACK パケットを送出後に, 速やかにその内容を忘れてしまいます。 この場合, ACK パケットが運良く失われずに戻ってくれば接続することができます。
(ライターから)
本稿は日経Linux 2000 年 10 月号に掲載された、 実践で学ぶ、一歩進んだサーバ構築・運用術, 第 7 回「ファイアウォール (前編)」を日経BP 社の許可を得て転載したものです。
Copyright(C)2000 by 仙石浩明 <sengoku@gcd.org>
無断転載を禁じます