ファイルアップロード機能があるWebアプリケーションでは、通常、許可された拡張子のみを受け入れるように設計されています。しかし、攻撃者はこの拡張子チェックをバイパスして悪意のあるファイル(例:スクリプトファイル)をアップロードし、サーバーで実行させる攻撃を行うことがあります。拡張子バイパス攻撃は、拡張子の誤検出や、ファイル名に特殊な文字やシーケンスを含めることで発生します。
$allowedExtensions = ['jpg', 'png', 'gif'];
$fileExtension = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
if (in_array($fileExtension, $allowedExtensions)) {
move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $_FILES['file']['name']);
}
malicious.php.jpg
といったファイル名に \0
を挿入することで、サーバー側で実際に .php
として扱われるケースがあります(ただし、最新のPHPバージョンではこのバイパス手法は無効化されています)。また、malicious.php.png
という名前を使い、サーバーがファイルの内容を検証せずに拡張子だけで判定する場合も危険です。ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
if 'file' in request.files:
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
filename
に特殊文字を含めることでバイパスが可能です。例えば、malicious.py.png
のような名前が使われることがあります。また、ファイル内容に悪意のあるスクリプトを含めた画像をアップロードし、処理されるケースもあります。const multer = require('multer');
const upload = multer({
fileFilter: function (req, file, cb) {
const filetypes = /jpeg|jpg|png/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
if (extname) {
return cb(null, true);
}
cb('Error: File type not allowed!');
}
});
app.post('/upload', upload.single('file'), (req, res) => {
res.send('File uploaded!');
});
malicious.js.jpg
)が使われることがあります。この場合、ファイルの拡張子チェックが不十分だと、JavaScriptファイルが実行される可能性があります。@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
String[] allowedExtensions = {"jpg", "png", "gif"};
String fileExtension = FilenameUtils.getExtension(file.getOriginalFilename());
if (Arrays.asList(allowedExtensions).contains(fileExtension)) {
file.transferTo(new File("uploads/" + file.getOriginalFilename()));
return "File uploaded!";
} else {
return "Invalid file type!";
}
}
malicious.jsp.jpg
のような名前を使うと、サーバー側で.jsp
として実行されるリスクがあります。また、ファイル内容の検証が行われないと、任意のコードが実行される可能性もあります。サーバー側で拡張子とMIMEタイプの両方を検査する
ファイルの拡張子だけでなく、MIMEタイプも確認し、不一致があれば拒否するようにします。
ファイル内容の検証
画像やドキュメントファイルとして受け取る場合は、実際にその形式であるかを検証します(例:画像であれば画像ライブラリを使って読み取れるか確認する)。
サーバー側で実行可能な場所に保存しない
アップロードされたファイルがスクリプトとして実行されないように、Webサーバーの公開ディレクトリ外に保存するか、実行権限を制限します。
ファイル名の正規化と安全な保存
secure_filename()
や同様の方法で、ファイル名を正規化し、予期しない文字列やパターンを防ぎます。
拡張子チェックバイパスは多くの言語で発生しうるため、拡張子のチェックだけに頼らず、多層的な検証を行うことが重要です。