Jupyter Server: ゲートウェイWebSocketハンドラが、カーネル実行の認証をバイパスする
### 概要
Jupyter ServerのゲートウェイWebSocketハンドラは、ユーザーが認証されているかどうかを確認しますが、設定された認証機能(Authorizer)に、そのユーザーがカーネルを実行できるかどうかを問い合わせることはありません。
Jupyter Server 2.0.0から2.5.0までのバージョンにおいて、GatewayClient.urlを有効にすると、通常のカーネルチャネルハンドラが、このゲートウェイ固有のハンドラに自動的に置き換えられました。 その結果、`execute`アクションが`kernels`リソースに対して拒否されている認証済みユーザーでも、カーネルチャネルを開き、上位のカーネルにメッセージを送信できるようになります。
### 詳細
通常のハンドラは、WebSocket接続を作成する前に、期待される認証チェックを実行します。
https://github.com/jupyter-server/jupyter_server/blob/v2.5.0/jupyter_server/services/kernels/websocket.py#L39-L47
一方、ゲートウェイハンドラは、`current_user`のみを確認し、上位のチャネルを開きます。 `authorizer.is_authorized(...)`を呼び出すことはありません。
https://github.com/jupyter-server/jupyter_server/blob/v2.5.0/jupyter_server/gateway/handlers.py#L49-L75
https://github.com/jupyter-server/jupyter_server/blob/v2.5.0/jupyter_server/gateway/handlers.py#L87-L98
ゲートウェイモードが選択されると、このハンドラが、一致するルートを持つハンドラを置き換えることで自動的に選択されます。
https://github.com/jupyter-server/jupyter_server/blob/v2.5.0/jupyter_server/serverapp.py#L437-L446
PR #1261では、通常のハンドラを置き換える処理が停止され、ゲートウェイのトラフィックが`GatewayWebSocketConnection`を通じてルーティングされるようになりました。 これにより、2.6.0でデフォルトのゲートウェイ構成が修正されたようです。 ただし、非推奨のハンドラは現在のリリースでも存在し、直接登録することも可能です。
https://github.com/jupyter-server/jupyter_server/pull/1261
### コードレベルでの検証
1. カスタムの認証モジュールを設定し、通常の認証ユーザーに対して、`kernels`に対する`execute`関数で`false`を返すようにします。
2. Jupyter Server 2.0.0から2.5.0までのバージョンで、`GatewayClient.url`を設定します。
3. 通常の`KernelWebsocketHandler`は、`pre_get`でそのユーザーを403エラーで拒否します。
4. ゲートウェイモードでは、これが`WebSocketChannelsHandler`に置き換えられます。その`authenticate`メソッドは、匿名ユーザーのみを拒否し、`get`は認証の判断なしに、上位のゲートウェイに接続を試みます。
### 影響
これは、認証の回避ではなく、認証チェックの欠落です。これは、厳格なカスタム認証モジュールを使用している環境や、Jupyter Gateway/Enterprise Gatewayを使用している環境で問題となります。読み取り専用またはその他の制限付きの認証ユーザーが、意図された認証境界を越え、カーネルの実行権限を得て、そのカーネルがアクセスできるデータや認証情報にアクセスする可能性があります。
### 影響を受けるバージョン
- デフォルトのゲートウェイ設定: 2.0.0から2.5.0
- 2.6.0以降: デフォルトのパスでは、標準の認証ハンドラが使用されますが、非推奨の`jupyter_server.gateway.handlers`モジュールを明示的に登録すると、古い動作が引き継がれます。
影響を受けるパッケージ:
- pip (Python) jupyter-server: 影響を受けるバージョンは2.0.0以上、2.6.0未満。2.6.0で修正済み。