Outline、SSO連携でアカウント乗っ取りの恐れ 1.11.0で修正
社内wikiのOutlineに、複数のログイン方式(SSO/OIDC)を併用していると、別の方式からのログインで他人のアカウントに入れてしまう弱点が見つかりました。自社でOutlineを運用していて、Googleなど複数のログイン方式を有効にしている組織が影響を受けます。修正版は1.11.0なので、該当する場合は更新し、ログイン方式の設定を確認してください。
急ぎ度: 早めに
CVSS 9.1の深刻な弱点で修正版も出ていますが、複数のログイン方式を併用している環境に限られ、実際の攻撃で悪用されたという情報は公表されていません。
関係があるか
Outlineを自社で運用(セルフホスト)し、1つのチームに2種類以上のログイン方式(例: Googleと独自のOIDC)を設定している組織が対象です。ログイン方式が1つだけの環境では、この経路は成立しにくいと考えられます。
| 製品 | 影響を受ける版 | 直った版 |
|---|---|---|
| Outline | 1.11.0より前の版(影響を受ける最も古い版は公表されていません) | 1.11.0 |
確かめ方
- 1運用中のOutlineの版を確認します。管理者に聞くか、導入方法に応じてコンテナイメージのタグなどを見てください。1.11.0より前なら更新の対象です。
- 2Outlineの管理画面の認証(Authentication)設定で、有効なログイン方式が2種類以上ないかを確認します。
- 3とくに、Googleなどの標準の方式に加えて、独自のOIDC接続を設定していないかを確認します。
- 4Outlineをベンダーのホスティング版で利用している場合は、ベンダーの告知やセキュリティ情報で対応状況を確認してください。対応状況は現時点で明らかになっていません。
何が起きたか
Outlineで、ログインしてきた人が「誰のアカウントか」を判定する処理に弱点がありました。Outlineは、GoogleやSlack、独自のOIDCといった外部のログイン方式が発行する利用者ID(外部ID)を使って、既存のアカウントと結び付けています。この判定が、外部IDの一致だけに頼っていました。
外部IDは、そのログイン方式の中でしか重複しないことが保証されていません。別のログイン方式どうしで同じ文字列が出てくることもありえます。それなのに、別方式のIDが既存のアカウントと一致すると、Outlineは「ログイン方式を切り替えた正規の利用者」とみなします。そして、メールアドレスが一致するか、確認済みかといった本来の確認を行わず、既存の利用者としてログインさせてしまいます。
該当するのはserver/commands/userProvisioner.tsの処理です。データベースの一意制約もproviderIdとuserIdの組み合わせで、「providerIdとログイン方式の組み合わせ」ではありません。そのため、別方式で同じIDが存在することをデータベースも止めません。報告者は、この処理を直接呼び出す形で、他人のアカウントが返ることを確認したとしています。弱点の種類はCWE-287(不適切な認証)で、CVSSは9.1(AV:N/AC:L/PR:N/UI:N)です。
攻撃の流れ
-
1
複数方式を併用
チームがGoogleと独自OIDCなど、2種類以上のログイン方式を設定しています。
-
2
被害者が先に登録
被害者が一方の方式でログインし、その外部IDがアカウントに結び付けられます。
-
3
外部IDを入手
攻撃者が被害者の外部IDを知ります。この値は秘密情報ではなく、知られていることがあります。
-
4
別方式でログイン
攻撃者がもう一方の方式で、被害者と同じIDを名乗ってログインします。
-
5
別人として入る
Outlineが方式の切り替えと誤認し、被害者のアカウントでログインさせます。
どう悪用されるか
悪用には、チームに2種類以上のログイン方式が設定されていることと、攻撃者が被害者の外部IDを知っていることが前提です。さらに、攻撃者が2つ目のログイン方式で認証を完了できる必要があります。たとえば自分で接続を設定できるOIDCの接続先を使える場合です。報告では、攻撃者のメールアドレスが無関係で未確認でも成立し、被害者の操作やパスワードは不要とされています。
悪用されると、チーム内の既存ユーザーのアカウントを丸ごと乗っ取られます。そのユーザーが読める文書の閲覧や、そのユーザーとしてできる操作(文書の編集など)が可能になります。機密情報を社内wikiに置いている場合は、情報漏えいと改ざんの両方が懸念されます。可用性への影響は評価されていません。
KEV(実際の悪用の確認)への掲載やEPSS(悪用される見込み)の値は、現時点で示されていません。実際の攻撃で悪用された事例は公表されていません。
今すぐやること
-
1
Outlineを1.11.0以降に更新する
セルフホスト環境では、Outlineを1.11.0以降にします。更新前に、データベースと設定のバックアップを取ってください。
-
2
有効なログイン方式を棚卸しする
管理画面の認証設定で、使っていない方式が有効のままになっていないかを確認します。不要な方式は無効にします。
-
3
独自OIDC接続の管理者と接続先を確認する
誰が接続を設定でき、どの接続先を信頼しているかを見直します。外部の第三者が管理する接続先が混ざっていないか確認します。
-
4
不審なログインがないか確認する
身に覚えのないログインや、文書の不自然な閲覧・編集がないかを確認します。Outlineの履歴や、前段のリバースプロキシ、IdP側のログインログを見ます。特定の痕跡の見つけ方は公表されていません。
-
5
ベンダーの告知を確認する
ホスティング版の利用者は、ベンダーの対応状況を確認します。セルフホストの利用者も、公式のセキュリティ情報を定期的に確認してください。
すぐ更新できないとき
公式の回避策は公表されていません。更新まで時間がかかる場合は、弱点が成立する条件を減らすのが現実的です。利用していないログイン方式を無効にし、ログイン方式を1つに絞ります。独自OIDCの接続先は、信頼できる管理下のものに限り、ほかの人が自由に接続を追加できない状態にしてください。ただし、これらは公式に検証された対策ではないため、修正版への更新を優先してください。
背景
Outlineは、チームで文書や手順書、議事録を共有するオープンソースの知識共有ツールです。社内wikiとして使われ、設計書や運用手順、顧客情報など機密性の高い内容が集まりがちです。ログインはGoogleやSlackなどの外部サービス、または社内のOIDC対応IdPに任せる構成がよく使われます。
シングルサインオンでは、外部から届く情報(IDやメールアドレス)を、どのアカウントと結び付けるかの判定が要になります。この結び付けの判定が甘いと、認証そのものが正しくても他人のアカウントに入られます。複数のID基盤を併用する製品では、このタイプの弱点が繰り返し問題になってきました。判定の根拠にする情報が、本当に「その人だけを指す」と言えるかが問われます。
よくある質問
Q.ログイン方式が1つだけでも影響を受けますか。
Q.すでに攻撃を受けたかどうかは分かりますか。
Q.外部IDは秘密ではないのに、なぜ危険なのですか。
まだ分かっていないこと
- 影響を受ける最も古い版は公表されていません。修正版は1.11.0です。
- 実際の攻撃で悪用されたかどうかや、悪用される見込み(EPSS)は示されていません。
- ホスティング版での対応状況は明らかになっていません。公式の告知で確認してください。
この記事は、取得元の公開情報をもとに生成 AI で作成しています。誤りを含むことがあります。対応を決める前に、下の原文と参照先を確認してください。
影響パッケージ
| 種類 | パッケージ | 影響を受ける版 | 直った版 |
|---|---|---|---|
| other | Outline |
< 1.11.0 |
1.11.0 |
原文の説明 取得元が公開している説明をそのまま載せています (多くは英語)。
Vulnerability Details
File: server/commands/userProvisioner.ts
Line: 76-114
CWE: CWE-287 — Improper Authentication
Severity: Critical
CVSS: 9.1 — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Root Cause
userProvisioner() looks up an existing UserAuthentication record purely by providerId (the external user ID reported by whichever OAuth/OIDC provider is being used, e.g. a Google sub claim, a Slack member ID, a Discord snowflake, or a custom OIDC sub) and team:
const auth = authentication
? await UserAuthentication.findOne({
where: { providerId: String(authentication.providerId) },
include: [{ model: User, as: "user", where: { teamId }, required: true }],
})
: undefined;
if (auth && authentication) {
const { user } = auth;
// We found an authentication record that matches the user id, but it's
// associated with a different authentication provider... Auto-migrate to the new provider.
if (auth.authenticationProviderId !== authenticationProviderId) {
await auth.update({ authenticationProviderId });
}
if (user) {
if (emailVerified === true) {
await user.update({ email });
}
await auth.update(rest);
return { user, authentication: auth, isNewUser: false };
}
}
providerId values are only guaranteed unique within their own provider's namespace — nothing in the code or database guarantees uniqueness across different provider types. I confirmed directly: the actual DB…
出典: GitHub Repository Security Advisories (Game のスタック) GitHub Advisory OSV
参照URL
- GITHUB_REPO_ADVISORY outline/outline