地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【Linux】access(2)はなぜ使うべきではないのか

この記事は「渡部 Advent Calendar 2025」の5日目の記事です。


nxmnpg.lemoda.net

ファイルシステムに関わるプログラムを書いていると、access() システムコールを見かける機会は意外と多いと思います。しかし、この access() は “知っておくと便利” というより、「正体を理解して積極的に使うものではない」種類のシステムコールということを当時のエンジニアの先輩に教わりました。特に、セキュリティや SRE、運用観点でファイルアクセスを扱う際には、access() の特性を誤解すると危険な判断につながることがあります。

この記事では、access() の基本から内部動作、なぜ避けるべきとされるのかあたりについて書いていきます。

access() システムコールとは?

access() は 「このプロセスが、指定したファイルに対してパーミッションを持っているか?」 をカーネルに問い合わせるためのシステムコールです。

#include <unistd.h>

int access(const char *pathname, int mode);
  • R_OK … 読み取り可能か?
  • W_OK … 書き込み可能か?
  • X_OK … 実行可能か?
  • F_OK … 存在しているか?

返り値

  • 0 → 要求されたアクセスが可能
  • -1 → 不可能(errno で理由がわかる:EACCES, ENOENT など)

access() と 通常のパーミッションチェック は違う

  1. access() は「実効UID/実効GID」に基づいてチェックする

UNIX のプロセスには以下があります:

  • 実ユーザID(real UID)
  • 実効ユーザID(effective UID) ← アクセス権の基準
  • 保存セットUID

access() は 実効 UID/GID を使ってアクセス可能かどうか判断 します。

sudo で実際に起きる例

sudo -u www-data ./app

この場合:

access() は www-data の権限で判定する

しかし open() は setuid プログラムなら root 権限で実行される可能性がある。つまり access() が通らないのに open() は成功することがあり得ます。

なぜ access() を使うべきでないと言われるのか?

TOCTTOU (Time-of-check vs Time-of-use) 脆弱性

典型例

if (access(path, W_OK) == 0) {
    fd = open(path, O_WRONLY);
}

access() と open() の間に パスの指し示すファイルが変更される可能性 がある。例えば攻撃者がaccess() の直後にファイルを symlink 別のファイルへ付け替えるた状態のopen()は別のファイルを開いてしまう可能性があります。これが classic TOCTTOU attackです。

en.wikipedia.org

カーネル内部ではどう動くのか?

  • sys_access() が呼ばれる
  • パス名の解決(VFS → inode へ)
  • inode_permission() でパーミッションチェック
  • 実効UID/GID を使って所有者・group・others の判定
  • 必要なら capabilities を評価(CAP_DAC_OVERRIDE など)

open() と違って実際にファイルディスクリプタは作られないです。この辺はVFSのレイヤーで実装されているので追うならnfsdあたりが読みやすいかなと思います。

github.com

代わりに何を使うべきか?

ファイルを開きたい:open() を直接使う

int fd = open(path, O_WRONLY);
if (fd < 0) {
    // ここで errno を見る
}

ファイルの属性だけ知りたい:stat()

struct stat st;
stat(path, &st);

実ユーザ権限での判定が必要:faccessat()

より安全で柔軟な *at() ファミリーを使う。

まとめ

  • access() は 「実効ユーザIDでアクセス可能か?」 の問い合わせ
  • TOCTTOU の危険が大きい
  • 実際のファイルアクセス権は open() の結果で判断すべき
  • setuid プログラムなど特殊用途以外では基本的に使わない

明日は渡部さんによる6日目の記事です。楽しみですね。