ファイルを開くのはどれくらい難しいでしょうか?

2026/04/24 11:01

ファイルを開くのはどれくらい難しいでしょうか?

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

最も重要なセキュリティ上の変化は、ファイルパス文字列を渡し続けるのではなく、ファイルディスクリプタ(fd)を用いることで TOCTOU など危険なランダム競争状態攻撃を防ぐことにあります。「fd を常に持つ」という規律により、セキュリティチェックと実際のファイル使用の間で下の файловシステムが変化してもアクセスは安全に保たれる一方、パスは攻撃者が置いたシンボリックリンクや不安全な bind マウントを経由して悪用される可能性があります。

libglnx
などのライブラリでは、より安全な代替案を提供しており、特に新たな
glnx_chaseat
ユーティリティによって安全なパスの移動を可能にしていますが、多くの標準 POSIX API とツールの依然として従来の不安全なパスベースのアブストラクションを採用しており、セキュリティ境界をまたいで安全に使用するには広範な監査が必要です。Flatpak における最近の脆弱性が、ポータルから信頼できる環境へ不安全なパスを渡すことで深刻なサンドボックス突破(CVE-2026-34078)を引き起こしたことを示しました。この問題の解決には、パス文字列を特定のファイルディスクリプタオプション(
--app-fd
--bind-fd
など)に置き換え、コールチェーン全体を監査する必要がありました。今後の進捗としては、O_PATH デスクリプターに対して不透明なハンドルを採用することが含まれますが、15 年前のサブサンドボクシング設計から現代的でカーネルネイティブの実装への移行により、Steam、WebKit、Chromium などのソフトウェアが動作しなくなるという初期の回退が発生しました。業界は今後、これらの現代的手法へ移行することによって、システム全体の不安定性をさらに引き起こす前に重大な欠陥を排除する必要があるのです。

本文

過去数ヶ月間、私はこの問いを自身で何回も自問する必要がありました。文脈によって答えは以下のどちらかになります:

  • とてもシンプルに、標準ライブラリの関数を呼び出すだけである場合
  • 極めて難しく、何も信用してはならない場合です。

アプリケーション開発者であれば幸運なほうに当たります。ほぼすべてのケースで最初の答えが適用可能です。しかし、ファイルを何らかの形で扱いつつセキュリティ境界線(separation boundary)を設けるようなものを開発する場合は、正解はおそらく第二段の場合である可能性が高いのです。

ファイルを開く「困難な方法」

多くの場合と同様に、詳細はその具体条件に依存しますが、最悪のケースでは、セキュリティ境界線の両側にプロセスが存在し、それらが共有するファイルシステムツリー上で動作しているという状況が考えられます。

より権限の多いプロセスが、より権限が少ないプロセスの代わりとしてファイル上の操作を代行한다고 しましょう。この制限を特定のディレクトリ内のファイルに限ることが望ましい場合があり、例えば SSH キーを盗むことなどを防ぐためです。その際に、あるディレクトリに対して相対的なサブパスを使用することになります。

まずの問題

まず真っ先に顕著な問題は、サブパスが

..
を含むことで、指定されたディレクトリ外のファイルを参照してしまうことです。もし権限の多いプロセスが
../.ssh/id_ed25519
というサブパスを受け取ると、大変なことになります。簡単な対策は、パスを正規化(normalization)し、ターゲットディレクトリ外に出ようとすればエラーにすることです。

次の課題

次の問題は、パスのすべてのコンポーネントがシンボリックリンク(symlink)である可能性があることです。権限の多いプロセスが

link
というサブパスを受け取り、かつ
link
../.ssh/id_ed25519
へのシンボリックリンクである場合も危険です。より権限が少ないプロセスがそのツリーの一部にファイルを作成できない場合、マルウェア用のシンボリックリンクを作成できず、問題はありません。しかし、それ以外のすべてのシナリオでは、すべてが「正常」とは言えません。簡単な対策としては、シンボリックリンクを解決し、パスを展開してから正規化することです。

ここまでは多くの人が「これで完了だ」と考えてしまいます。「ファイルを開くなどそんなに難しいことではない、あとはもっと楽しいことを考えよう」という感覚が生まれます。しかし、本当に問題はこれから始まります。

上記の対策は、より権限が少ないプロセスがファイルのパス上のどこかでファイルシステムツリーを変更できない限り有効です。より権限の高いプロセスがそのファイルをアクセスしようとする間に、より権限の低いプロセスが何らかの変更を加えない場合が一般的です。例えば、攻撃者から提供されたアーカイブを、攻撃者がアクセスできないディレクトリに展開する場合などは該当します。しかし、変更を加えてしまえる場合は、古典的な TOCTOU(Time-of-Check to Time-of-Use)競合条件問題が発生します。

パス

foo/id_ed25519
とし、シンボリックリンクを解決し、パスを展開してから正規化したとしましょう。その間、別のプロセスがまさにチェックした直後に確認していたディレクトリ
foo
(通常ディレクトリ)を、
../.ssh
へのシンボリックリンクに置き換えてしまった場合が典型的です。私たちはそのパスが目標ディレクトリ内に解決されると確認しただけであり、幸福に
foo/id_ed25519
を開いてしまったことになります。これはさぞ簡単そうでない修正が必要になります。

では、ここで根本的な問題は何でしょうか?

/home/user/.local/share/flatpak/app/org.example.App/deploy
のようなパス文字列は、ファイルシステム名前空間内の場所を記述しているに過ぎません。ファイルへの参照そのものではありません。「そのパスを口に出す」行為が終わる頃には、その名前の指し示す対象が変更されている可能性があります。

安全な基本単位(primitive)はファイルディスクリプタです。あるインノード(inode)を指し示す fd が用意された後、カーネルはそのインノードをピン留め(pin)します。ディレクトリはアンリンクされても、名前変更されても、シンボリックリンクで置き換えられても、fd は気にしません。一般的な誤解の 하나는「ファイルディスクリプタとはオープンされているファイルを意味する」という考えです。確かにそうなることはありますが、

O_PATH
で開かれた fd には実際のファイルをオープンする必要はなく、それでも安定したインノードへの参照を提供します。

ここで学ぶべき教訓は、パス文字列を権限を持つプロセスに渡してはならないということです。それは「絶対的」です。ファイルディスクリプタを渡すことにも、呼び出し側プロセスが実際にはそのリソースにアクセス権を持っているという証明としての利点があります。

また、重要な教訓として、ファイルディスクリプタからパスに降下させることは、再び競合条件の問題を生じさせます。例えば、ファイルを指定する fd からバインドマウント(bind mount)を実行したいが、従来のマウント API しか利用できないため、fd をパスに変換してマウント関数に渡した場合を考えましょう。残念ながら、攻撃者が配置しえた可能性のあるパス内のシンボリックリンクをカーネルは解決します。場合によっては、インストール後の事象を検出することも可能ですが、例えばマウントされたファイルと fd のインノードおよびデバイスの一致を確認するなどです。

その話を一旦脇に置きながら、パスを使用せざるを得ない場合もあるため、それについても見ていきましょう。

上記のシナリオでは、すべてのパスが解決するディレクトリがあり、かつ攻撃者が制御できないディレクトリがあります。そのため、攻撃者による再方向化を許さない

O_PATH
フラグ付きで開いてから fd を取得できます。

さらに、

openat
システムコールを用いれば、直前に開いた fd に対する相対パスとしてファイルを開くことができます。上記と同じ課題を抱えますが、同時に
O_NOFOLLOW
フラグも渡せるという点で異なります。このフラグをセットした場合、パスの最右端のコンポーネントがシンボリックリンクである場合でも、それを追跡せず、実際のシンボリックリンクそのもののインノードに対応した fd を返します。他のコンポーネントは依然としてシンボリックリンクであり、それらは引き続き追跡されます。しかし、パスを分割して、次のパスセグメント用の次(fd)を開き、すべてのコンポーネントでシンボリックリンクの解決を手動で行うことができます。

libglnx の chase

libglnx
は GNOME C プロジェクト向けのユーティリティライブラリで、主に fd ベースのファイルシステム操作を提供します。
glnx_openat_rdonly
glnx_file_replace_contents_at
glnx_tmpfile_link_at
などの関数はすべてディレクトリ fd を受け取り、それに対する相対パスでの操作を行います。このライブラリは、「常に fd を保有し、可能な限り絶対パスを避ける」という規律を中心に構築されています。

最新に追加された

glnx_chaseat
は、安全なパストラバーサルを提供する関数で、systemd の
chase()
に着想を得ており、まさに上記の通り動作します。

int glnx_chaseat (int              dirfd,
                   const char      *path,
                   GlnxChaseFlags   flags,
                   GError         **error);

これは解決済みのパスに対応した

O_PATH | O_CLOEXEC
フラグ付きの fd を返すか、エラーの場合は
-1
を返します。真の魔法はフラグにあります:

typedef enum _GlnxChaseFlags {
  /* デフォルト */
  GLNX_CHASE_DEFAULT = 0,
  /* Automount のトリガーを無効にする */
  GLNX_CHASE_NO_AUTOMOUNT = 1 << 1,
  /* パスの右端成分を追跡しない。パスの右端成分がシンボリックリンクである場合、
   * そのシンボリックリンク自体の O_PATH fd を返す。*/
  GLNX_CHASE_NOFOLLOW = 1 << 2,
  /* 解決プロセスのいずれかのコンポーネントが dirfd で示されたディレクトリの descendant ではない場合、
   * パスの解決を成功させない。*/
  GLNX_CHASE_RESOLVE_BENEATH = 1 << 3,
  /* シンボリックリンクは与えられた dirfd を基準として根付き (root) ではなく解決される。*/
  GLNX_CHASE_RESOLVE_IN_ROOT = 1 << 4,
  /* どのシンボリックリンクも遭遇した場合、エラーにする。*/
  GLNX_CHASE_RESOLVE_NO_SYMLINKS = 1 << 5,
  /* パスの右端成分が普通ファイル (regular file) ではない場合、エラーにする。*/
  GLNX_CHASE_MUST_BE_REGULAR = 1 << 6,
  /* パスの右端成分がディレクトリでない場合、エラーにする。*/
  GLNX_CHASE_MUST_BE_DIRECTORY = 1 << 7,
  /* パスの右端成分がソケットでない場合、エラーにする。*/
  GLNX_CHASE_MUST_BE_SOCKET = 1 << 8,
} GlnxChaseFlags;

実装自体はあまり複雑そうに見えませんが、多くの詳細が非常にこまかいです。実装では利用可能なシステムコールと要求される振る舞いに応じて

openat2
open_tree
openat
を使い分けており、自動マウントの動作を処理し、既に見たパスが変更されていないことを保証するなど、いくつかの追加機能も実装しています。

標準ライブラりに対する一言

POSIX API はこの問題に対処する方面ではあまり優れているとはいえません。GLib/Gio API(GFile など)はさらに悪く、パスを受け取るだけです。確かに、それらは fd が普遍的な概念ではないクロスプラットフォーム抽象化の役割も果たします。不幸なことに、Rust でも完全にパスに基づくクロスプラットフォーム抽象化が存在します。

これらの API のいずれかを使用すれば、ほぼ確実に脆弱性を生み出していることになります。より根深い問題は、これらのパスベースの API がファイルとの相互作用における標準方法となりがちであることです。これにより、合成されたコードのセキュリティについて論理的に考えられなくなります。自らのコードを綿密に監査し、すべてのファイルを開く際に

O_PATH | O_NOFOLLOW
を使用し、
*at()
呼び出しを注意深くチェーンしても、第三者ライブラリが内部で
open(path)
を呼び出すことを許容すれば、コード内で確立したセキュリティ特性は、そのライブラリ呼び出しを通じて保持されなくなります。

つまり、ファイルシステムのセキュリティに関心のあるシステムレベルのコードは、すべての推移的依存関係(transitive dependencies)を監査するか、そもそもそれらを避ける必要があります。

では、より良い GLib クロスプラットフォーム API はどのようなものになるでしょうか?

chaseat()
とあまり変わらないと考えるのが妥当でしょうが、不透明なハンドル(opaque handles)を返すだけで、Unix 上では
O_PATH
fd を持ち、表示・デバッグなどに使えるパスも含むものとなります。そこからファイルを開くと、読み書きなどのための別の種類の不透明なハンドルが得られます。

現在の

GFile
も GVfs の実装のために設計されました:
g_file_new_for_uri("smb://server/share/file")
はローカルファイルのように
g_file_read()
が可能な
GFile
を返します。これは目指すべき方向性は正しいですが、抽象化の層が間違っています。むしろこの種のアクセスは FUSE によって提供され、URI を特定の FUSE マウント上のパスに変換するのが望ましいです。これによる利点は以下の通りです:

  • fd chasing アプローチは、カーネル管理の実在のファイルシステムだからどこでも機能する
  • ファイルシステムは GLib に依存しなくなり、Rust などから使用可能になる
  • Flatpak で使われる XDG デスクトップドキュメントポータルなどの他の FUSE ファイルシステムとスタックできる

それなら、なぜこれを話すのか?

現在私は小型プロジェクトである Flatpak の維持を任されています。最近 Codean Labs が Flatpak のセキュリティ解析を行いましたが、いくつかの課題が発見されました。Flatpak 開発者がファイルシステムの危険性を認識していたこともあり、これのために libglnx を作成しましたが、発見された問題の大部分はまさにそれに関するものでした。その一つ(CVE-2026-34078)は完全にサンドボックスエスケープという重大なものだったのです。

flatpak run
は信頼されるユーザー向けのコマンドラインツールとして設計されています。
flatpak run org.example.App
と入力した際、あなたは引数を制御します。引数を処理するコードは呼び出し方が正当であると想定して書かれています。それはパス文字列を受け取るだけで、コマンドラインツールの標準的な振る舞いです。

その後 Flatpak ポータルは、サンドボックス化されたアプリケーションがサブサンドボックスを開始するために呼び出す D-Bus サービスとして構築されました。これは実質的に

flatpak run
の呼び出しを構築して実行することで実現されています。つまり、信頼できる入力用に設計されたコンポーネントを、非信頼な呼び出し元(サンドボックス内アプリ)に直接接続してしまったのです。

その接続が確立されると、「flatpak run」における「呼び出し元の正当性に関するすべての前提条件」が潜在的な脆弱性に変換されます。修正策は「一つの関数を変更するだけ」ではなく、「ポータルリクエストから bubblewrap の実行に至るまでの一連の呼び出しを監査し、すべてのパス文字列を fd に置き換える」ことに他なりません。それはポータルに変わるコミット、

flatpak-run
flatpak_run_app
flatpak_run_setup_base_argv
、bwrap の引数構築、そして新しいオプション(
--app-fd
--usr-fd
--bind-fd
--ro-bind-fd
)をこれらすべてにわたって通す作業でした。

GLib 標準のファイル・パス API が安全であれば、この問題は発生しませんでした。

もう一つの問題は、Flatpak のサブサンドボックス化アプローチが現在では必要以上に古く(15 年前)、未権限付与のユーザーネームスペースが一般的ではなかった時代からのものということです。現代ではアプリケーションに対してカーネルネイティブの未権限付与ユーザーネームスペースを利用させて独自のサブサンドボックスを構築させることも可能(かつ必要)です。

残念ながら、大きな変更に伴うリスクとして何か不具合が発生する可能性が高いです。数日間にわたって、Steam や WebKit、Chromium ベースのアプリケーションの起動を防いでいたいくつかの回帰(regression)を修正するために必死で動きました。Simon McVittie 氏に心より感謝申し上げます!

結局、私たちはすべての問題を解決し、Flatpak をより安全に改修し、エコシステム全体がこの種の課題に対処する能力が高まりました。きっと何かしら学んでもらえたhopefully もあるはずです。

同じ日のほかのニュース

一覧に戻る →

2026/04/22 16:20

無料ユニバーサルコンストラクションキット

## Japanese Translation: Free Universal Construction Kit は、10 の主要な建築玩具システム(Lego、Duplo、Fischertechnik、Gears! Gears! Gears!、K'Nex、Krinkles(Bristle Blocks)、Lincoln Logs、Tinkertoys、Zome および Zoob)の相互運用性を可能にし、「技術的ロックイン」を解消することに成功しました。これらすべてのシステムは、光学的コンパレーターを用いて 0.0001 インチ以内の精度で設計されたほぼ 80 の両方向アダプターブロックを通じて統合されています。これらのモデルは、Thingiverse.com、F.A.T. Lab ウェブサイトの 29MB の.zip ファイル、および The Pirate Bay の"physibles"チャンネルにある Torrent ファイルを介して自由にご利用可能です。ユーザーはオープンソースのデスクトッププリンター(例:Makerbot、RepRap、Ultimaker、Printrbot)または Ponoko.com などの高解像度サービスを利用して部品を再現できます。 fist サイズの Universal Adapter Brick は、すべてのサポートされたネットワークを一つの統合されたシステムに統一します。このプロジェクトは、リバースエンジニアリングを企業の「技術的ロックイン」や特許制約を超えようとする市民活動として位置付けています。法的には、レゴ(1958 年に特許出願)、Lincoln Logs(1920 年に特許出願)など従来のブランド向けのホーム印刷アダプターは「適合例外」に該当し、フェアユースによって保護されています。一方、Zoob および ZomeTool のアダプターは、依然として有効な特許制限により、それぞれ 2016 年 12 月および 2022 年 11 月までリリースが遅延しています。本キットは Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported ライセンスの下で提供されており、商業的大規模生産は禁止されていますが、オープンソースプリンターやサービス bureau を通じた個人による製造は許可されています。F.A.T. Lab と Sy-Lab が開発し、 Adapterz LLC が法的代表を務め、 Riley Harmon がビデオ製作を担当した本プロジェクトには、小さな部品が含まれているため 3 歳未満の幼児には不適切であるという窒息危険に関する警告表示があります。結局のところ、このオープンなアプローチは、創業者が既存デザインにおける保護された知的財産権を尊重しつつ相互運用性を育むことを可能にします。

2026/04/22 22:46

『一ビット:北斎「巨浪」(2023)』

## Japanese Translation: このデジタル・プロジェクトは、葛飾北斎『富嶽三十六景』を忠実な 1 ビット・ピクセルアートとして再製作する、5 年前のイニシアチブを蘇らせます。意図的に早期モノクロの Macintosh の美学を喚起し、Susan Kare のデザインへのレガシーを称え、Quadra 700 または PowerBook 100(System 7 が動作中)上で Aldus SuperPaint 3.0 を使用します。すべての画像は厳密にオリジナルの Mac 解像度 512×342 ピクセルに合致しています。 ユーザー @polyducks の提案によりシリーズを『神奈川沖浪裏』から開始し、寄稿者 hypertalking が 01/36 としてキュレーションした本シリーズには、近日中に追加作品も掲載されます。Mac デスクトップ背景用にボーナスの高解像度バージョン(640×480)も用意されています。本作はクリエイティブ・コモンズ Attribution-NonCommercial-NoDerivatives 4.0 International ライセンスの下で共有されており、画像を再利用または投稿する場合は創作者へのクレジットとウェブサイトのリンクが必要です。 ## Text to translate Summary remains good; minor improvement in flow and clarity below: This digital project revives a five‑year‑old initiative to recreate Hokusai's "Thirty‑Six Views of Mount Fuji" as authentic one‑bit pixel art. It intentionally evokes early black‑and‑white Macintosh aesthetics, honoring the design legacy of Susan Kare and using Aldus SuperPaint 3.0 on a Quadra 700 or PowerBook 100 running System 7. Every image strictly matches the original Mac resolution of 512 × 342 pixels. Launched with "The Great Wave off Kanagawa"—suggested by user @polyducks and curated as item 01/36 by contributor hypertalking—the series will feature additional works soon. A bonus high‑resolution version (640 × 480) is available for Mac desktop backgrounds. The artwork is shared under a Creative Commons Attribution‑NonCommercial‑NoDerivatives 4.0 International License; users must credit the creator and link back to the website when reproducing or posting the images.

2026/04/26 1:11

これまで完了することのなかったプロジェクトを、コーディング支援ツールを使って再開させる行為。

## Japanese Translation: 元のサマリーは質が高く、明確で、不必要な冗長性を排除しつつテキストの核心を捉えていますが、主要項目に記載されている特定の AI モデル(Claude Code/Opus)を明示的に記載することで、および使用された核心的なコーディング規約(例:Pydantic V2 の注釈、Google スタイルの docstring など)を簡潔に列挙することで、記述のプロセスの厳密さを強調し、その精度をさらに高めることができます。 これらの軽微な改善を盛り込みながら、流れを維持した上で若干洗練させたバージョンを示します。 ## 精査後のサマリー 著者は「Sub-standard」という個人用音楽プロジェクトを約一夜間で機能させることに成功しました。このプロセスでは、**Claude Code (Opus 4.6)** を活用して複雑なコーディングタスクを処理させました。この実験は、明確な規約(Pydantic V2 の注釈や Google スタイルの docstring など)とコンテキストを与えることで、AI アシスタントが「Tsundoku」現象(時間的あるいは能力的な制約により停滞するプロジェクト)を超えて個別のコーディング目標を完了させることが可能であることを示しています。このワークフローでは、ストリーミングに `yt-dlp`、検索に `ytmusicapi` といった特定のライブラリを利用し、約 80 の OpenSubsonic エンドポイントを扱うために SQLite ストレージを統合しました。初期の構築では、構造化データを正しく返すようにstubbed エンドポイントを見直す必要がありましたが、最終的な結果はプロフェッショナルなリリースよりも個人の願望実現を最優先し、意図的に認証をスキップしました。このアプローチは、開発者が AI に過剰に依存することで「deskilling」という潜在的なリスクを浮き彫りにしますが、クリエイターにとって強力なアクセラレータとなります。完全なリポジトリは git 上で入手可能であり、本来なら未完了のままになる可能性のある個人用プロジェクトの迅速実行のための青写真を提供しています。