WebVulnerabilities

キャッシュポイズニング攻撃について

概要

キャッシュポイズニング攻撃は、Webサーバーやプロキシサーバーのキャッシュ機構を悪用して、攻撃者が不正なコンテンツをキャッシュに注入する攻撃です。これにより、ユーザーがキャッシュされた不正なコンテンツを受け取ることになり、フィッシングやマルウェア配布などの攻撃が可能になります。キャッシュポイズニングは、特にキャッシュのキーが不適切に管理されている場合や、キャッシュポリシーが不十分な場合に発生しやすいです。


脆弱性を持つコードとサーバー構成と設定の例と攻撃手法

サーバー構成

キャッシュポイズニングは、これらのサーバーが不適切に構成されている場合に発生します。

攻撃手法の例

攻撃者は、キャッシュサーバーやプロキシサーバーが不適切に構成されていることを利用して、特定のリクエストに対するレスポンスを不正にキャッシュさせます。その後、他のユーザーが同じキャッシュされたレスポンスを受け取ることで、不正なコンテンツが提供されるようになります。

PHP

脆弱なコード例:

<?php
$token = $_GET['token'];
if ($token === 'admin') {
    $content = "Sensitive admin content";
} else {
    $content = "Public content";
}
header("Cache-Control: public, max-age=3600");
echo $content;
?>

サーバー設定の例:

攻撃手法: 攻撃者は、token=adminのリクエストを送信し、その結果がキャッシュされるように仕向けます。その後、他のユーザーが同じURLをリクエストすると、キャッシュされた管理者専用のコンテンツが提供されます。

Python(Flask)

脆弱なコード例:

from flask import Flask, request

app = Flask(__name__)

@app.route('/content')
def content():
    user_type = request.args.get('user_type')
    if user_type == 'admin':
        content = "Sensitive admin content"
    else:
        content = "Public content"
    
    response = app.make_response(content)
    response.headers['Cache-Control'] = 'public, max-age=3600'
    return response

サーバー設定の例:

攻撃手法: 攻撃者がuser_type=adminを指定してリクエストを送信し、そのレスポンスがキャッシュされると、他のユーザーもそのキャッシュされた管理者専用コンテンツを受け取ることになります。

Node.js(Express)

脆弱なコード例:

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

app.get('/content', (req, res) => {
    const userType = req.query.userType;
    if (userType === 'admin') {
        res.set('Cache-Control', 'public, max-age=3600');
        res.send('Sensitive admin content');
    } else {
        res.set('Cache-Control', 'public, max-age=3600');
        res.send('Public content');
    }
});

app.listen(3000);

サーバー設定の例:

攻撃手法: 攻撃者は、userType=adminを指定してリクエストを送信し、そのレスポンスがキャッシュされると、他のユーザーが同じコンテンツを取得することになります。

Java(Spring)

脆弱なコード例:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;

@RestController
public class ContentController {

    @GetMapping("/content")
    public String getContent(@RequestParam String userType, HttpServletResponse response) {
        if ("admin".equals(userType)) {
            response.setHeader("Cache-Control", "public, max-age=3600");
            return "Sensitive admin content";
        } else {
            response.setHeader("Cache-Control", "public, max-age=3600");
            return "Public content";
        }
    }
}

サーバー設定の例:

攻撃手法: 攻撃者がuserType=adminを指定してリクエストを送信し、そのレスポンスがキャッシュされると、他のユーザーもそのキャッシュされたコンテンツを受け取ることが可能になります。


防止策

  1. キャッシュキーの適切な管理:
    • ユーザー固有の情報が含まれるレスポンスにはキャッシュが適用されないように、キャッシュキーを適切に設定します。例えば、tokenuserTypeのようなパラメータに基づいてキャッシュキーを分離することで、他のユーザーに影響を与えるキャッシュを避けます。

    PHPの例:

    <?php
    if ($token === 'admin') {
        header("Cache-Control: private, max-age=0");
    } else {
        header("Cache-Control: public, max-age=3600");
    }
    echo $content;
    ?>
    
  2. キャッシュ制御ヘッダーの適切な設定:
    • センシティブな情報を含むレスポンスにはCache-Control: privateno-cacheを設定し、共有キャッシュに保存されないようにします。

    Pythonの例:

    if user_type == 'admin':
        response.headers['Cache-Control'] = 'private, no-store, no-cache, must-revalidate'
    else:
        response.headers['Cache-Control'] = 'public, max-age=3600'
    
  3. コンテンツに基づくキャッシュ制御:
    • 静的なコンテンツと動的なコンテンツを明確に区別し、動的なコンテンツにはキャッシュを適用しないようにします。
  4. ユーザーの認証情報に基づくキャッシュの無効化:
    • 認証情報に基づいてレスポンスを提供している場合、そのレスポンスがキャッシュされないように設定します。特に、セッション情報やトークンを含むリクエストには注意が必要です。

    Node.jsの例:

    if (req.isAuthenticated()) {
        res.set('Cache-Control', 'private, no-store');
    } else {
        res.set('Cache-Control', 'public, max-age=3600');
    }
    
  5. キャッシュサーバーの設定を最適化:
    • プロキシやキャッシュサーバー(Varnish、Redisなど)の設定を最適化し、不要なキャッシュや共有キャッシュによる情報漏洩を防ぎます。
  6. キャッシュポイズニングの検出と監視:
    • キャッシュポイズニング攻撃の兆候を検出するための監視を行い、不審なリクエストやキャッシュの異常を確認します。

まとめ: キャッシュポイズニング攻撃は、キャッシュ機構を悪用して不正なコンテンツをユーザーに提供する攻撃です。キャッシュキーの適切な管理、キャッシュ制御ヘッダーの設定、認証情報に基づくキャッシュの無効化などの防止策を実施することで、この脆弱性を効果的に防ぐことが可能です。各言語やサーバー構成に合わせた最適な対策を講じることが推奨されます。