WebVulnerabilities

ローカルファイルインクルード(Local File Inclusion, LFI)脆弱性について

概要

ローカルファイルインクルード(LFI)脆弱性は、Webアプリケーションがユーザーからの入力を使用してローカルファイルをインクルードする際に、不適切な入力検証により攻撃者が意図しないファイルをインクルードさせる脆弱性です。この脆弱性を利用すると、攻撃者はサーバー内の任意のファイル(例: /etc/passwdなど)を読み込んだり、場合によってはWebシェルなどの悪意のあるコードを実行させることが可能になります。


脆弱性を持つコードの例と攻撃手法

PHP

脆弱なコード例:

<?php
$page = $_GET['page'];
include($page);
?>

攻撃手法:

Python(Flask)

脆弱なコード例:

from flask import Flask, request

app = Flask(__name__)

@app.route('/include')
def include_file():
    page = request.args.get('page')
    with open(page, 'r') as file:
        content = file.read()
    return content

if __name__ == '__main__':
    app.run()

攻撃手法:

Node.js(Express)

脆弱なコード例:

const express = require('express');
const fs = require('fs');
const app = express();

app.get('/include', (req, res) => {
    const page = req.query.page;
    fs.readFile(page, 'utf8', (err, data) => {
        if (err) {
            return res.status(500).send('Error loading file');
        }
        res.send(data);
    });
});

app.listen(3000);

攻撃手法:

Java(JSP)

脆弱なコード例:

<% 
String page = request.getParameter("page");
RequestDispatcher dispatcher = request.getRequestDispatcher(page);
dispatcher.include(request, response);
%>

攻撃手法:


防止策

  1. 入力のバリデーションとサニタイズ:
    • ユーザーが入力したファイルパスを正規化し、ディレクトリトラバーサルのパターン(../など)が含まれていないかチェックします。

    PHPの例:

    $page = basename($_GET['page']);  // ファイル名のみに制限
    $filepath = "/var/www/html/pages/" . $page;
    include($filepath);
    

    Pythonの例(Flask):

    import os
    from flask import Flask, request, abort
    
    app = Flask(__name__)
    
    @app.route('/include')
    def include_file():
        page = request.args.get('page')
        if '..' in page or page.startswith('/'):
            abort(400)
        safe_path = os.path.join('/var/www/html/pages', page)
        if os.path.exists(safe_path):
            with open(safe_path, 'r') as file:
                content = file.read()
            return content
        else:
            abort(404)
    
  2. ホワイトリストの使用:
    • 読み込み可能なファイルを事前にリスト化し、そのリスト内のファイルのみをインクルード可能にします。

    Node.jsの例(Express):

    const express = require('express');
    const fs = require('fs');
    const path = require('path');
    const app = express();
    
    const allowedFiles = ['about.html', 'contact.html'];
    
    app.get('/include', (req, res) => {
        const page = req.query.page;
        if (!allowedFiles.includes(page)) {
            return res.status(400).send('Invalid file');
        }
        const filepath = path.join(__dirname, 'pages', page);
        fs.readFile(filepath, 'utf8', (err, data) => {
            if (err) {
                return res.status(404).send('File not found');
            }
            res.send(data);
        });
    });
    
    app.listen(3000);
    
  3. 絶対パスの使用と検証:
    • ファイルパスを絶対パスに変換し、許可されたディレクトリ内にあるかどうかを検証します。

    Javaの例(JSP):

    List<String> allowedPages = Arrays.asList("home.jsp", "about.jsp", "contact.jsp");
    if (allowedPages.contains(page)) {
        RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/pages/" + page);
        dispatcher.include(request, response);
    } else {
        response.getWriter().write("Invalid page request.");
    }
    
  4. 外部ファイルのインクルード禁止:
    • 外部からのファイルの読み込みやインクルードを禁止し、サーバー内部の特定のディレクトリ内に限定します。
  5. エラーメッセージの制限:
    • ファイル読み込みエラーが発生した場合に、具体的なパスやエラーメッセージを表示しないようにします。

まとめ: ローカルファイルインクルード脆弱性は、サーバー上の任意のファイルを読み込ませることができる非常に危険な脆弱性です。入力のバリデーション、ホワイトリストの使用、絶対パスの検証、外部ファイルのインクルード禁止などの防止策を実装することで、この脆弱性を効果的に防ぐことができます。各言語やフレームワークに適した対策を講じることが推奨されます。