ディレクトリトラバーサル攻撃について
ディレクトリトラバーサル攻撃は、Webアプリケーションがユーザー入力をもとにファイルやディレクトリにアクセスする際に、攻撃者が相対パスを利用して本来アクセスするべきでないシステムファイルや他の機密情報にアクセスする攻撃手法です。攻撃者は../
(親ディレクトリへの移動)などの相対パスを使い、アプリケーションが意図していないディレクトリやファイルを閲覧したり、場合によっては書き換えたりすることができます。
脆弱なコード例:
<?php
$filename = $_GET['file'];
$content = file_get_contents('/var/www/html/files/' . $filename);
echo $content;
?>
攻撃手法:
攻撃者は以下のようなURLを使って、/etc/passwd
のような機密ファイルにアクセスします。
http://example.com/view.php?file=../../../../etc/passwd
このコードは、ユーザーが提供したパスを検証せずに直接ファイルパスに使用しているため、ディレクトリトラバーサル攻撃が成立します。
脆弱なコード例:
from flask import Flask, request, send_file
app = Flask(__name__)
@app.route('/download')
def download_file():
filename = request.args.get('file')
return send_file('/var/www/html/files/' + filename)
if __name__ == '__main__':
app.run()
攻撃手法: 攻撃者は以下のようなURLを使って、システムファイルにアクセスできます。
http://localhost:5000/download?file=../../../../etc/passwd
脆弱なコード例:
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
app.get('/view', (req, res) => {
const filename = req.query.file;
const filepath = path.join(__dirname, 'files', filename);
fs.readFile(filepath, 'utf8', (err, data) => {
if (err) {
return res.status(404).send('File not found');
}
res.send(data);
});
});
app.listen(3000);
攻撃手法: 攻撃者は以下のようなURLを使って、システムファイルにアクセスできます。
http://localhost:3000/view?file=../../../../etc/passwd
脆弱なコード例:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.stereotype.Controller;
import java.nio.file.Files;
import java.nio.file.Paths;
@Controller
public class FileController {
@GetMapping("/view")
public String viewFile(@RequestParam String file) throws Exception {
String filepath = "/var/www/html/files/" + file;
return new String(Files.readAllBytes(Paths.get(filepath)));
}
}
攻撃手法: 攻撃者は以下のようなURLを使って、システムファイルにアクセスできます。
http://localhost:8080/view?file=../../../../etc/passwd
../
など)が含まれていないかをチェックします。ファイル名として許可されるのは、特定のディレクトリ内に存在する安全なファイルのみであるべきです。PHPの例:
$filename = basename($_GET['file']);
$filepath = '/var/www/html/files/' . $filename;
if (file_exists($filepath)) {
$content = file_get_contents($filepath);
echo $content;
} else {
echo "File not found.";
}
Pythonの例(Flask):
import os
from flask import Flask, request, send_file, abort
app = Flask(__name__)
@app.route('/download')
def download_file():
filename = request.args.get('file')
if '..' in filename or filename.startswith('/'):
abort(400)
safe_path = os.path.join('/var/www/html/files', filename)
if os.path.exists(safe_path):
return send_file(safe_path)
else:
abort(404)
Node.jsの例(Express):
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const allowedFiles = ['file1.txt', 'file2.txt'];
app.get('/view', (req, res) => {
const filename = req.query.file;
if (!allowedFiles.includes(filename)) {
return res.status(400).send('Invalid file');
}
const filepath = path.join(__dirname, 'files', filename);
fs.readFile(filepath, 'utf8', (err, data) => {
if (err) {
return res.status(404).send('File not found');
}
res.send(data);
});
});
app.listen(3000);
Javaの例(Spring):
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.stereotype.Controller;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@Controller
public class FileController {
@GetMapping("/view")
public String viewFile(@RequestParam String file) throws Exception {
Path basePath = Paths.get("/var/www/html/files").toRealPath();
Path requestedPath = basePath.resolve(file).normalize().toRealPath();
if (!requestedPath.startsWith(basePath)) {
return "Invalid file path.";
}
return new String(Files.readAllBytes(requestedPath));
}
}
まとめ: ディレクトリトラバーサル攻撃は、ユーザー入力が適切に検証されない場合に発生する脆弱性です。相対パスの除去、ホワイトリストによるファイル名の制限、絶対パスの使用、ユーザー入力のエスケープなどの防止策を講じることで、この攻撃を防ぐことができます。各言語に応じた適切な防止策を実装することが推奨されます。