Linuxパーミッションの2つの考え方 — そしてなぜ一方が問題を引き起こすのか
10台以上のLinux VPSを3年間管理してきた中で、ファイルパーミッションへの対処法が全く異なる2つのsysadminグループを見てきました。最初のグループはchmodとchownを応急処置として扱い、フォルダにchmod 777を叩いて終わりにします。2つ目のグループはパーミッションモデルを正しく理解しており、深夜2時にウェブサーバーが本来提供すべきでないファイルを配信している理由を慌てて調べることなど決してありません。
これは「パーミッションとは何か」を解説する記事ではありません。3年間の経験から学んだ、この2つのアプローチが実際の本番環境でどう展開するかについての話です。
アプローチ比較:寛容なショートカット vs. 最小権限の原則
寛容なショートカット(多くの初心者がやること)
多くの初心者はpermission deniedエラーに当たると同じことをします:アプリがエラーを出さなくなるまでパーミッションを広げるのです。エラーが出たので、こう実行します:
chmod 777 /var/www/html/uploads
chown -R root:root /var/www/html
アプリは動きます。問題解決ですよね?そうではありません。すべてのファイルが誰でも読み書きできる状態になりました。そのサーバー上のあらゆるプロセス—侵害されたPHPスクリプトも含めて—がアップロードディレクトリへの書き込みアクセス権を持つことになります。すべてをrootで所有すると、たった1つのウェブエクスプロイトがシステム全体でroot権限で実行されます。
WordPressが何度も改ざんされる理由がわからないと悩んでいた本番サーバーで、まさにこの設定を目にしたことがあります。原因はほぼ常に、root所有権を持つ777のアップロードフォルダでした。
最小権限の原則(本来やるべきこと)
最小権限の原則とは、各プロセスとユーザーが絶対に必要な権限だけを持つことを意味します。それ以上はありません。これを実現するには、chmodとchownが連携してどう機能するかを理解する必要があります—単独ではなく。
こう考えてください:chownはファイルの所有者を決め、chmodはその所有者が実際にファイルで何ができるかを決めます。一貫したシステムを構築するには、両方が連携して機能する必要があります。
# Webアプリケーション専用ユーザーを作成
useradd -r -s /bin/false www-data
# 正しい所有権を設定(ユーザー:グループ)
chown -R www-data:www-data /var/www/html
# 適切なパーミッションを設定:オーナー rwx、グループ rx、その他 なし
chmod -R 750 /var/www/html
# アップロードディレクトリのみ:オーナー rwx、グループ rwx
chmod 770 /var/www/html/uploads
これでウェブサーバープロセス(www-dataとして実行)はアプリファイルの読み取りと実行、アップロードへの書き込みができますが、外部ユーザーや不正なプロセスはディレクトリツリーを覗き見することができません。
メリットとデメリット:各アプローチの限界
寛容なアプローチ
- メリット:設定が速く、パーミッションエラーをすぐに解消できる
- メリット:基本的なモデルを理解する必要がない
- デメリット:1つの侵害されたスクリプトがどこにでも書き込める
- デメリット:誰でも読めるファイルが設定ファイル、ログ、認証情報を露出させる
- デメリット:root所有のウェブファイルはウェブエクスプロイトがrootで実行されることを意味する
- デメリット:監査がほぼ不可能—何が書き込み可能であるべきか、そうでないかの把握が困難になる
最小権限アプローチ
- メリット:何かが侵害されても被害範囲が限定される
- メリット:監査が容易—パーミッションが意図を反映している
- メリット:自分のアプリケーションのファイルアクセスパターンを理解することを促す
- デメリット:正しく設定するために初期作業が多く必要
- デメリット:数値モードとシンボリックモードの知識が必要
- デメリット:最初はパーミッション問題のデバッグに時間がかかる
最後のデメリット群は実際には学習曲線に過ぎません。3〜4台のサーバーを適切に設定した後は、パーミッション問題のデバッグが第二の本能になります—通常は30秒のls -la確認で済みます。
Linuxサーバーの推奨設定
まずパーミッションの三つ組を理解する
Linuxのすべてのファイルとディレクトリには3つのパーミッションセットがあります:オーナー、グループ、その他。各セットには3つのビットがあります:読み取り(r=4)、書き込み(w=2)、実行(x=1)。数値モードは各セットのビットの合計に過ぎません。
# よく使われる本番環境のパーミッション値:
# 755 = rwxr-xr-x (ディレクトリ、スクリプト)
# 644 = rw-r--r-- (通常ファイル、設定ファイル)
# 600 = rw------- (秘密鍵、機密情報)
# 750 = rwxr-x--- (アプリディレクトリ、グループは読み取り可)
# 770 = rwxrwx--- (共有書き込み可能ディレクトリ)
# クイックチェック:ファイルの実際のパーミッションを確認
ls -la /etc/ssh/sshd_config
# -rw-r--r-- 1 root root 3287 Jan 10 09:12 /etc/ssh/sshd_config
私の標準VPS設定
10台以上のサーバーを経験した今、新しいVPSが起動するたびに考えずに適用するパーミッションモデルがこれです:
# SSH秘密鍵:オーナーのみ読み取り可、他は不可
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 700 ~/.ssh
# Webアプリケーションファイル
chown -R appuser:www-data /var/www/myapp
find /var/www/myapp -type f -exec chmod 640 {} \;
find /var/www/myapp -type d -exec chmod 750 {} \;
# 書き込み可能なディレクトリ(アップロード、キャッシュ)
chmod 770 /var/www/myapp/storage
chmod 770 /var/www/myapp/public/uploads
# 機密情報を含む設定ファイル
chmod 600 /var/www/myapp/.env
findを使ったアプローチは重要です。通常ファイルを含むディレクトリでchmod -R 755を実行してはいけません—すべてのファイルを実行可能にしてしまい、それはほぼ確実に望む動作ではありません。常にファイルとディレクトリを別々に扱いましょう。
実装ガイド:実践的なchmodとchown
chown:所有権の設定
# オーナーのみ変更
chown appuser /var/log/myapp.log
# オーナーとグループを変更
chown appuser:appgroup /var/log/myapp.log
# 所有権を再帰的に変更(注意して使用)
chown -R appuser:appgroup /var/www/myapp
# 現在の所有権を確認
stat /var/www/myapp/index.php
# File: /var/www/myapp/index.php
# Uid: ( 1001/ appuser) Gid: ( 33/ www-data)
chmod:パーミッションの設定
# 数値モード(本番環境で最も一般的)
chmod 644 config.ini
chmod 755 deploy.sh
chmod 600 .env
# シンボリックモード(スクリプトで読みやすい)
chmod u+x deploy.sh # オーナーに実行権限を追加
chmod g-w /etc/myapp/config # グループから書き込み権限を削除
chmod o= /var/secrets # その他からすべての権限を削除
# すべてのファイルに再帰的に適用(ファイルのみ)
find /var/www -type f -exec chmod 644 {} +
# すべてのディレクトリに再帰的に適用
find /var/www -type d -exec chmod 755 {} +
特殊ビット:setuid、setgid、スティッキービット
これらはあまり頻繁には登場しませんが、実際のサーバーでは必ず遭遇します。
# 共有ディレクトリのスティッキービット(オーナーのみ自分のファイルを削除可能)
chmod +t /tmp
chmod 1777 /tmp
# 結果:drwxrwxrwt — 末尾の't'がスティッキービット
# 共有プロジェクトディレクトリのsetgid
# (新しいファイルは作成者のプライマリグループではなく、ディレクトリのグループを継承)
chmod g+s /var/www/shared
chmod 2775 /var/www/shared
# 結果:drwxrwsr-x
# ls -laで確認:
ls -la /tmp
# drwxrwxrwt 12 root root 4096 Mar 6 08:00 /tmp
本番環境に適用する前のテスト
本番サーバーで誤ったパーミッション変更を一度経験してから、変更前の厳格な手順を徹底しています。本番環境では絶対にこれを省略しないでください:
# 1. 変更前に現在の状態を記録
ls -laR /var/www/myapp > /tmp/permissions_before.txt
# 2. まず1つのファイルでテスト
chmod 640 /var/www/myapp/index.php
# 3. アプリが正常に動作することを確認
curl -I https://myapp.example.com/
# 4. テストが通った場合のみすべてのファイルに適用
find /var/www/myapp -type f -exec chmod 640 {} +
# 5. 変更内容を確認
ls -laR /var/www/myapp > /tmp/permissions_after.txt
diff /tmp/permissions_before.txt /tmp/permissions_after.txt
このdiffステップのおかげで、少なくとも2回は自分のアプリケーションからロックアウトされるのを防ぐことができました。
パーミッションエラーのデバッグ
アプリがpermission deniedエラーをスローしたとき、最も迅速に原因を特定する方法がこれです:
# 1. プロセスが実行されているユーザーを確認
ps aux | grep nginx
ps aux | grep php-fpm
# 2. 実際のファイルパーミッションを確認
ls -la /path/to/problematic/file
# 3. そのユーザーがファイルにアクセスできるかテスト
sudo -u www-data ls /var/www/myapp/storage
sudo -u www-data cat /var/www/myapp/.env
# 4. 親ディレクトリのパーミッションを確認(多くの場合、真の原因)
namei -l /var/www/myapp/storage/logs/app.log
# パスの各コンポーネントのパーミッションを表示
namei -lコマンドは最初から知っておきたかったツールの一つです。パス内のすべてのディレクトリのパーミッションを表示してくれるので、ツリーを一つずつ確認していくのではなく、どの層がアクセスをブロックしているかを瞬時に特定できます。
機能するセットアップ
寛容なショートカットは速く感じます—深夜に改ざんされたWordPressサイトを復旧するまでは。可視性を失い、セキュリティを失い、最終的に何かがうまくいかなくなったとき睡眠も失います。最小権限モデルは新しいサーバーで余分に20分程度かかるかもしれません。後で節約できるインシデント対応の時間と比べれば、何でもありません。
私の個人的なルール:ファイルがそのパーミッションを持っている理由を説明できない場合、それは調査する価値のある危険信号です。パーミッションはアプリケーションが実際にファイルにアクセスする方法を反映すべきであり、単にエラーメッセージを消したものであってはいけません。

