サーバーのセキュリティ
Signal Lab サーバーは、動作しているマシンから実際の通信を送信します。OSC、UDP、HTTP、MQTT、ストーム、スキャン、ブロードキャストです。サーバーを使える人は、そのマシンからこれらすべてを実行できます。そのため、サーバーは既定で閉じられており、トークンがある場合にだけ開きます。
WARNING
アクセストークンは、そのマシンのネットワークへのパスワードと同じように扱ってください。トークンを持つ人は誰でも、サーバーが到達できるあらゆる宛先に、サーバーから通信を送信できます。
概要
- トークンがなければ、このマシンだけ。 トークンなしでは、サーバーはループバックで待ち受け、ループバックのホスト名にしか応答しません。それ以外のアドレスでは起動を拒否します。
- それ以外の相手にはトークン。 ブラウザーは一度サインインしてセッション Cookie を受け取り、スクリプトはリクエストのたびにトークンを送ります。
- 自分自身のページだけ。 何かを変更するリクエストとイベントの WebSocket は、サーバー自身のオリジンから来る必要があります。コマンドは JSON しか受け付けません。
- 自分自身の名前だけ。 サーバーが応答しないホスト名は拒否されます。これにより DNS リバインディングを防ぎます。
- シークレットは内部にとどまる。 環境変数またはファイルからの読み取り専用で、返されることはなく、表示されうる場所ではマスクされます。
- 役割以上のことはしない。 ホストのファイアウォールを変更せず、データフォルダー外のファイルを提供せず、特権も必要としません。
トークンなしの場合: このマシンだけ
トークンなしで起動すると、サーバーは 127.0.0.1:1430 で待ち受け、サインインを必要としません。このマシンの前にいる人のためのツールです。このマシン上のブラウザーで開いた Web ページが、127.0.0.1 に解決される名前を通じてサーバーに到達すること (DNS リバインディング) を防ぐため、サーバーは Host がループバックの名前 (localhost、.localhost で終わる名前、127.x.x.x、[::1]) か、--allowed-host で許可した名前であるリクエストにだけ応答します。
トークンなしで他のアドレスでの待ち受けを求められると、サーバーは起動せず、理由を示してコード 2 で終了します。
トークン
トークンは 24 文字以上で、スペースや改行を含められません。signal-lab-server token は、16 進数 64 文字のランダムなトークンを出力します。--generate-token (イメージではオンです) は、初回の起動時にトークンを作成し、サーバー自身のユーザーだけが読み取れる形でデータフォルダーに保存して、一度だけ出力します。トークンを渡すすべての方法はアクセストークンを参照してください。
トークンの比較は、どこで違っていても同じ時間で終わります。間違ったトークンには 1 秒の待機とログへの警告が伴うので、推測は遅く、痕跡も残ります。
ブラウザー: セッション
サインインしていないブラウザーは、サインインページに誘導されます。トークンは一度だけセッションと交換され、Cookie に保持されます。この Cookie には次の性質があります。
HttpOnly: ページ上のスクリプトは読み取れません。SameSite=Strict: 他のサイトのページが、ブラウザーにこれを送信させることはできません。- 有効期間は 7 日間です。
--secure-cookieを指定するとSecureになり、HTTPS 経由でのみ送信されます (HTTPS プロキシの背後ではこれを設定してください)。
セッションはサーバーのメモリ上にあります。再起動すると全員がサインアウトされ、サインアウト を押すとそのセッションがすぐに終了します。保持されるセッションは最大 1024 個で、古いものから順に破棄されます。
スクリプト: Bearer トークン
スクリプト、signallab --server、CI は、リクエストのたびにトークンを送ります。
Authorization: Bearer <token>GET /api/health (サーバーが応答しているか、そのバージョン、トークンを要求するか) とサインインページを除き、すべてのエンドポイントにトークンまたはセッションが必要です。トークンなしで API にリクエストすると、エラー auth.required とともに 401 が返ります。ページの場合はサインインページが返ります。
ホスト名
| サーバーの起動方法 | 応答するホスト名 |
|---|---|
| トークンなし | ループバックの名前と、--allowed-host にある名前 |
トークンあり、--allowed-host なし | 任意の名前 |
トークンあり、--allowed-host あり | ループバックの名前と、--allowed-host にある名前 |
--allowed-host (SIGNALLAB_ALLOWED_HOSTS) は、カンマ区切りの名前を受け付けます。比較はポートを除き、大文字と小文字を区別せずに行われます。
signal-lab-server --listen 0.0.0.0:1430 --token-file token.txt --allowed-host lab-pc.example.com,192.0.2.10それ以外の Host には、エラー auth.host とともに 403 が返ります。既知の名前で到達できるサーバーでは、これを設定してください。そうすれば、他のサイトのページが独自の名前でサーバーに到達することはできません。
オリジンとコンテンツタイプ
- 何かを変更するすべてのリクエスト (
GETとHEAD以外) とイベントの WebSocket は、Originを持たないか、サーバー自身のオリジン (Hostと同じホストとポート) を持つ必要があります。他のサイトのページやOrigin: nullを送るページには、auth.originとともに403が返ります。スクリプトとcurlはOriginを送らないため、影響を受けません。 - コマンドは
Content-Type: application/jsonしか受け付けません (それ以外はcommand.json_requiredとともに415)。そのため、他のサイトのフォームからコマンドを送ることはできません。 - サーバーは、クロスオリジン (CORS) のリクエストには応答しません。
レスポンスヘッダー
すべてのレスポンスに、次のヘッダーが付きます。
| ヘッダー | 値 |
|---|---|
Content-Security-Policy | default-src 'self'; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self' |
X-Content-Type-Options | nosniff |
X-Frame-Options | DENY |
Referrer-Policy | same-origin |
Cache-Control | API とサインインページには no-store |
インターフェイスは、サーバーが提供するものしか読み込まず、サーバー以外とは通信せず、他のページのフレームに埋め込まれることもありません。
ファイルとサイズ
- ダウンロード (
GET /api/files?path=…) は、データフォルダーの内側からのみ、最大 256 MiB まで提供されます。それ以外は404です。 - リクエストボディは最大 24 MiB です。
シークレット
シークレットの値は、エンジンの外に出ることがありません。
- サーバーでは、値は読み取り専用です。環境変数
SIGNALLAB_SECRET_<NAME>、またはシークレットフォルダー (既定では/run/secrets/signallab) 内のファイル<NAME>から読み取られます。ブラウザーからの設定や削除は拒否されます (secret.read_only)。ページに入力した値が、より安全性の低い場所に保存されることはありません。シークレットを参照してください。 - 値を返すコマンドはありません。インターフェイスが知るのは、その名前が設定されているかどうかだけです。
- 実験ではシークレットを
{{secret.NAME}}と名前で指定します。実行や送信がそれを使っている間、報告されるすべてのテキスト (ステップ、エラー、実行レポート) ではその代わりに••••が表示され、インスペクター のフレームはバイト単位でマスクされます。 - HTTP ノードの資格情報は、リクエストが送信されるときにだけ
Authorizationヘッダーになります。ステップ、フレーム、レポートに含まれるのはレスポンスだけで、このヘッダーは含まれません。
記録されるもの
ログには、クライアントのアドレスとともに、開始されたすべてのジョブ (ストーム、スキャン、ブロードキャスト、モニター、ジェネレーター、実行、エミュレーター)、すべてのサインイン、間違ったトークンでのすべてのサインインが記録されます。ログを参照してください。
サーバーが決して行わないこと
- ホストのファイアウォールを変更すること。 デスクトップアプリは、依頼されたときにファイアウォールのルールを追加できますが、サーバーではそのコマンドは拒否されます (
firewall.server)。ホストのファイアウォールは、そのホストを運用する人のものです。(ワンコマンドのインストールスクリプトは、サーバーのポートを ufw や firewalld で開くことを提案しますが、先に確認します。ホストのファイアウォールを参照してください。) - ループバック以外のアドレスで、トークンなしで到達可能な状態で起動すること。
- データフォルダーの外のファイルを提供すること。
- ブラウザーに入力されたシークレットを保存すること。
- TLS を自分で話すこと。 前段に HTTPS プロキシを置いてください (HTTPS プロキシの背後で使うを参照)。
エンジンのあらゆる上限 (スイープは最大 1024 ホスト、ビーコンは最大毎秒 50,000 パケット) は、サーバーでもアプリと同様に適用されます。これは安全上の上限であって、許可ではありません。通信を送るのは、所有しているシステムかテストを許可されたシステムに対してだけにしてください。
コンテナー
イメージは特権のないユーザー (uid と gid が 10001) で動作し、書き込むのは /data だけです。読み取り専用のルートファイルシステム、ケーパビリティなし、no-new-privileges のままで変更なしに動作し、インストールスクリプトと deploy/compose.yaml もその設定で起動します。各イメージは、SBOM、ビルドの来歴、署名された GitHub アテステーションとともに公開されます。
gh attestation verify oci://ghcr.io/proanima/signallab:1.0.0 -R ProAnima/SignalLabSignal Lab が外部に送信するもの
あなたが送る通信のほかに、Signal Lab は 2 か所と通信します。どちらもスタジオのものです。
更新の確認
更新を確認するのはデスクトップアプリだけで、サーバーとブラウザーが確認することはありません。Signal Lab について の 1 日 1 回確認 がオンのとき、1 日 1 回、そして 更新を確認 を押すたびに、アプリはスタジオのハブ (hub.proanima.net) に問い合わせます。ハブに接続できない場合にだけ、GitHub の最新リリースを参照します。問い合わせには次のものが含まれます。
- アプリのバージョン。
- オペレーティングシステムとプロセッサーのアーキテクチャ。
- このインストール用のランダムな番号 (
X-Install-Id)。一度だけ作成されてアプリの設定とともに保持され、新しいリリースをまず一部のインストールに届けるために使われます。あなたやコンピューターについては何も示しません。
提示されるのは公開済みのリリースだけです。署名がアプリに組み込まれた鍵と一致しないダウンロードはインストールされず、インストールして再起動 を押すまで何もインストールされません。
フィードバック
開発者に連絡 (ヘッダーの ✉、Signal Lab について にもあります) は、スタジオのハブを通じて開発者にメッセージを送ります。ハブがそれをメールで転送し、アプリはそのためのパスワードを持っていません。送信されるのは、フォームに表示されているものだけです。メッセージ、入力した場合はメールアドレス、追加したスクリーンショット、そして 自動で添付 の下にあるコンソールログと バージョンとシステム です。後の 2 つは、送信前に内容を開いて確認でき、チェックを外せます。これらからは、このコンピューターの名前、そのアドレス、フォルダーが除かれます。ブラウザーからの場合は、サーバーがフォームを送信します。