ディレクトリトラバーサル攻撃とは?ファイルシステムを狙う危険な手法

ディレクトリトラバーサル攻撃とは?

ディレクトリトラバーサル攻撃(パストラバーサル攻撃とも呼ばれます)とは、Webアプリケーションのファイルアクセス機能の脆弱性を悪用して、本来アクセスできないはずのファイルやディレクトリにアクセスする攻撃手法です。

この攻撃が成功すると、以下のような重要な情報が漏洩する可能性があります:

  • 設定ファイル(database.yml、.env など)
  • ソースコード(セキュリティ機能の詳細)
  • システムファイル(/etc/passwd、/etc/shadow など)
  • ログファイル(アクセスログ、エラーログ)

特に../(ドットドットスラッシュ)を使った攻撃が有名で、「ドットドット攻撃」とも呼ばれています。

ディレクトリトラバーサルの仕組み

基本的な攻撃の流れ

まず、脆弱性のあるファイルダウンロード機能を例に説明します。

以下のような脆弱なファイル読み込み処理があるとします:

<?php
// 危険なコード例(絶対に使わないでください)
$filename = $_GET['file'];
$filepath = '/var/www/html/uploads/' . $filename;

// ファイルの内容を表示
echo file_get_contents($filepath);
?>

通常の利用方法

正常な使用の場合、以下のようなURLでファイルにアクセスします:

https://example.com/download.php?file=document.pdf

この場合、実際に読み込まれるファイルは:

/var/www/html/uploads/document.pdf

攻撃者の悪用方法

しかし、攻撃者が以下のようなURLを使った場合はどうでしょうか?

https://example.com/download.php?file=../../../etc/passwd

この場合、実際に読み込まれるファイルは:

/var/www/html/uploads/../../../etc/passwd

これは以下のパスと同じになります:

/etc/passwd

結果として、Linuxシステムのユーザー情報が記載された重要なシステムファイルが表示されてしまいます。

具体的な攻撃例

1. 基本的なディレクトリトラバーサル

最も基本的な攻撃パターンです。

../../../etc/passwd          # Linuxのユーザー情報
../../../etc/shadow          # Linuxのパスワードハッシュ
../../../windows/system32/drivers/etc/hosts  # Windowsのhostsファイル

2. 設定ファイルの窃取

Webアプリケーションの設定ファイルを狙った攻撃:

../../config/database.yml     # データベース接続情報
../../.env                    # 環境変数(API キーなど)
../../wp-config.php          # WordPressの設定ファイル
../../../apache2/apache2.conf # Apache設定ファイル

3. エンコーディングを利用した迂回攻撃

基本的なフィルタリングを回避するため、特殊なエンコーディングを使用:

..%2F..%2F..%2Fetc%2Fpasswd   # URLエンコード
..%252F..%252F..%252Fetc%252Fpasswd  # 二重URLエンコード
....//....//....//etc/passwd  # 冗長なパス表現

4. Null バイト攻撃(古いシステム)

古いシステムでは、Null バイト(%00)を使った攻撃も有効な場合があります:

../../../etc/passwd%00.jpg

実際の被害事例

事例1:大手CMS の脆弱性

2023年に発見された某大手CMSの脆弱性では、以下のような攻撃が可能でした:

/admin/file-manager.php?path=../../../etc/passwd

この脆弱性により、数千のWebサイトでサーバーの機密情報が漏洩するリスクがありました。

事例2:ファイルアップロード機能の悪用

あるWebアプリケーションでは、アップロードされたファイルを表示する機能に脆弱性があり:

/view-uploaded-file.php?file=../../database/backup.sql

この攻撃により、データベースのバックアップファイルが丸ごと窃取される事件が発生しました。

ディレクトリトラバーサルの対策方法

1. 入力値の検証とサニタイゼーション(最重要)

最も重要な対策は、ユーザーからの入力値を適切に検証・サニタイゼーションすることです。

<?php
// 安全なコード例
function sanitizeFilename($filename) {
    // 危険な文字を除去
    $filename = str_replace(['../', '..\\', '..', '/'], '', $filename);

    // 英数字とドット、ハイフン、アンダースコアのみ許可
    $filename = preg_replace('/[^a-zA-Z0-9._-]/', '', $filename);

    return $filename;
}

$filename = sanitizeFilename($_GET['file']);
$filepath = '/var/www/html/uploads/' . $filename;

// さらに実際のファイルが存在し、許可されたディレクトリ内にあるかチェック
if (file_exists($filepath) && strpos(realpath($filepath), realpath('/var/www/html/uploads/')) === 0) {
    echo file_get_contents($filepath);
} else {
    echo "ファイルが見つかりません";
}
?>

2. ホワイトリスト方式の採用

許可されたファイルのみをリスト化する方式:

<?php
// ホワイトリスト方式の実装
$allowedFiles = [
    'document1.pdf',
    'report.xlsx',
    'image.jpg'
];

$filename = $_GET['file'];

if (in_array($filename, $allowedFiles)) {
    $filepath = '/var/www/html/uploads/' . $filename;
    echo file_get_contents($filepath);
} else {
    echo "許可されていないファイルです";
}
?>

3. realpath()を使った絶対パス検証

<?php
// 絶対パスによる検証
$baseDir = '/var/www/html/uploads/';
$filename = $_GET['file'];
$filepath = $baseDir . $filename;

// 実際のパスを取得
$realPath = realpath($filepath);
$realBaseDir = realpath($baseDir);

// ベースディレクトリ内に存在するかチェック
if ($realPath && strpos($realPath, $realBaseDir) === 0) {
    echo file_get_contents($realPath);
} else {
    echo "アクセスが拒否されました";
}
?>

4. chroot環境の利用

Linuxシステムでは、chrootを使ってプロセスのルートディレクトリを制限:

# chroot環境の構築例
sudo mkdir /var/chroot/webapp
sudo chroot /var/chroot/webapp /usr/bin/php /path/to/script.php

5. ファイル権限の適切な設定

# ファイル権限の設定例
chmod 644 /var/www/html/uploads/*        # 一般ファイル
chmod 755 /var/www/html/uploads/         # ディレクトリ
chmod 600 /etc/mysql/my.cnf              # 設定ファイル

フレームワーク別の安全な実装

Laravel(PHP)

// Laravelでの安全な実装
public function downloadFile(Request $request)
{
    $filename = $request->get('file');

    // Storage facade を使用(安全なパス管理)
    $path = storage_path('app/public/uploads/' . $filename);

    if (Storage::exists('public/uploads/' . $filename)) {
        return response()->download($path);
    }

    return abort(404);
}

Express.js(Node.js)

// Express.jsでの安全な実装
const path = require('path');
const fs = require('fs');

app.get('/download/:filename', (req, res) => {
    const filename = req.params.filename;

    // パスを正規化
    const safePath = path.normalize(path.join(__dirname, 'uploads', filename));
    const uploadsDir = path.join(__dirname, 'uploads');

    // ディレクトリトラバーサルを防ぐ
    if (!safePath.startsWith(uploadsDir)) {
        return res.status(403).send('Access denied');
    }

    if (fs.existsSync(safePath)) {
        res.download(safePath);
    } else {
        res.status(404).send('File not found');
    }
});

Django(Python)

# Djangoでの安全な実装
import os
from django.http import HttpResponse, Http404
from django.conf import settings

def download_file(request, filename):
    # 安全なパス構築
    safe_path = os.path.join(settings.MEDIA_ROOT, 'uploads', filename)

    # パスが許可されたディレクトリ内にあるかチェック
    if not safe_path.startswith(os.path.join(settings.MEDIA_ROOT, 'uploads')):
        raise Http404("Access denied")

    if os.path.exists(safe_path):
        with open(safe_path, 'rb') as f:
            response = HttpResponse(f.read())
            response['Content-Disposition'] = f'attachment; filename="{filename}"'
            return response
    else:
        raise Http404("File not found")

脆弱性の発見方法

1. 手動テスト

以下のようなパラメータでテストを実施:

?file=../../../etc/passwd
?file=..%2F..%2F..%2Fetc%2Fpasswd
?file=....//....//....//etc/passwd
?path=../../../windows/system32/drivers/etc/hosts

2. 自動化ツールの活用

Burp SuiteOWASP ZAPを使用した自動テスト:

# OWASP ZAP での自動テスト例
zap-cli quick-scan --spider --ajax-spider http://example.com

3. 静的解析ツールの利用

# PHPStan を使用した静的解析
./vendor/bin/phpstan analyze src/ --level=max

# SonarQube を使用したコード品質分析
sonar-scanner -Dsonar.projectKey=myproject

WebサーバーでのセキュリティLevel強化

Apache での設定

# .htaccess での設定例
<Files ~ "^\.">
    Order allow,deny
    Deny from all
</Files>

# 危険なファイルへのアクセスを禁止
<FilesMatch "\.(conf|log|sql|yml|yaml|env)$">
    Order allow,deny
    Deny from all
</FilesMatch>

Nginx での設定

# nginx.conf での設定例
location ~* \.(conf|log|sql|yml|yaml|env)$ {
    deny all;
    return 403;
}

# 隠しファイルへのアクセスを禁止
location ~ /\. {
    deny all;
    return 403;
}

セキュリティテストの実施

1. 定期的なペネトレーションテスト

# Niktoを使用したWebサーバーのスキャン
nikto -h http://example.com

# Dirb を使用したディレクトリ探索
dirb http://example.com /usr/share/dirb/wordlists/common.txt

2. コードレビューでのチェックポイント

  • ファイルパスの構築方法
  • 入力値の検証ロジック
  • ファイルアクセス権限の設定
  • エラーハンドリングの実装

まとめ

ディレクトリトラバーサル攻撃は、適切な対策を行えば防ぐことができる脆弱性です。

重要なポイント:

  1. 入力値の検証とサニタイゼーションを徹底する
  2. ホワイトリスト方式でアクセス可能なファイルを制限
  3. realpath()等を使った絶対パス検証を実装
  4. ファイル権限の適切な設定を行う
  5. 定期的なセキュリティテストを実施する

特に、../のような相対パス表現を含む入力は絶対に信頼してはいけません。常に疑いの目を持って、厳格な入力検証を行うことが重要です。

セキュリティは多層防御が基本です。一つの対策に頼るのではなく、複数の対策を組み合わせて、堅牢なWebアプリケーションを構築しましょう。


コメント

タイトルとURLをコピーしました