WebVulnerabilities

CRLFインジェクション攻撃について

概要

CRLFインジェクション攻撃とは、Webアプリケーションにおいて、ユーザーが入力したデータがHTTPレスポンスヘッダーや他のプロトコルのヘッダーにそのまま挿入される場合に発生する攻撃です。CRLFは、キャリッジリターン(\r)とラインフィード(\n)のことで、HTTPヘッダーを終了し、新しいヘッダーやボディを開始するために使われます。この攻撃により、攻撃者は任意のヘッダーを挿入したり、レスポンスのボディを改ざんすることができます。これにより、XSS(クロスサイトスクリプティング)やHTTPレスポンススプリッティングなどの攻撃が実行可能となります。


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

PHP

脆弱なコード例:

<?php
$user = $_GET['user'];
header("Location: /welcome.php?user=" . $user);
?>

攻撃手法:

PHPのバージョンによる違い

PHP 5.xおよびそれ以前のバージョン

PHP 5.1.2以降

PHP 7.x以降

Python(Flask)

脆弱なコード例:

from flask import Flask, request, redirect

app = Flask(__name__)

@app.route('/redirect')
def redirect_user():
    user = request.args.get('user')
    return redirect(f'/welcome?user={user}')

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

攻撃手法:

Pythonのバージョンによる差異

標準ライブラリ

Pythonの標準ライブラリ自体には、HTTPヘッダーの生成や操作に特化した関数は含まれていません。代わりに、http.clientモジュール(Python 3.x)やhttplibモジュール(Python 2.x)を使用して、HTTPリクエストを作成したり、ヘッダーを操作することができます。CRLFインジェクションに関しては、Pythonの標準ライブラリ自体にバージョンによる大きな差異はありません。

Webフレームワーク(FlaskやDjango)

PythonでWebアプリケーションを構築する際には、FlaskやDjangoなどのWebフレームワークを使用することが一般的です。これらのフレームワークでは、ユーザー入力を適切にサニタイズしないと、CRLFインジェクションのリスクがあります。

Node.js(Express)

脆弱なコード例:

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

app.get('/redirect', (req, res) => {
    const user = req.query.user;
    res.setHeader('Location', `/welcome?user=${user}`);
    res.status(302).end();
});

app.listen(3000);

攻撃手法:

Java(Spring)

脆弱なコード例:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletResponse;

@Controller
public class RedirectController {

    @GetMapping("/redirect")
    public void redirectUser(@RequestParam String user, HttpServletResponse response) {
        response.setHeader("Location", "/welcome?user=" + user);
        response.setStatus(HttpServletResponse.SC_FOUND);
    }
}

攻撃手法:


防止策

  1. ユーザー入力のエスケープまたはサニタイズ:
    • ユーザーが入力したデータをHTTPレスポンスヘッダーに挿入する前に、CRLF文字列(\r\n)をエスケープまたは削除します。

    PHPの例:

    $user = str_replace(["\r", "\n"], '', $_GET['user']);
    header("Location: /welcome.php?user=" . $user);
    

    Python(Flask)の例:

    from flask import Flask, request, redirect
    import re
    
    app = Flask(__name__)
    
    @app.route('/redirect')
    def redirect_user():
        user = re.sub(r'[\r\n]', '', request.args.get('user'))
        return redirect(f'/welcome?user={user}')
    
    if __name__ == '__main__':
        app.run()
    

    Node.js(Express)の例:

    const express = require('express');
    const app = express();
    
    app.get('/redirect', (req, res) => {
        const user = req.query.user.replace(/[\r\n]/g, '');
        res.setHeader('Location', `/welcome?user=${user}`);
        res.status(302).end();
    });
    
    app.listen(3000);
    

    Java(Spring)の例:

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import javax.servlet.http.HttpServletResponse;
    
    @Controller
    public class RedirectController {
    
        @GetMapping("/redirect")
        public void redirectUser(@RequestParam String user, HttpServletResponse response) {
            user = user.replaceAll("[\\r\\n]", "");
            response.setHeader("Location", "/welcome?user=" + user);
            response.setStatus(HttpServletResponse.SC_FOUND);
        }
    }
    
  2. HTTPレスポンスヘッダーにユーザー入力を直接使用しない:
    • 可能な限り、ユーザー入力をHTTPレスポンスヘッダーに直接使用しないように設計します。動的な値が必要な場合は、定義された範囲や形式の中でのみ使用するようにします。
  3. セキュリティフレームワークの利用:
    • フレームワークやライブラリで提供されるセキュアなメソッドやコンポーネントを利用し、手動でヘッダーを構築しないようにします。これにより、セキュリティリスクを低減できます。
  4. セキュリティテストの実施:
    • CRLFインジェクション脆弱性がないかを検証するため、セキュリティテストを実施し、コードが安全であることを確認します。

まとめ: CRLFインジェクションは、Webアプリケーションがユーザー入力を適切に処理しない場合に発生する攻撃です。攻撃者は、この脆弱性を利用してHTTPレスポンスヘッダーを改ざんし、不正なクッキーの設定やレスポンスの改ざんなどを行うことができます。ユーザー入力のエスケープやサニタイズ、セキュリティフレームワークの利用などの防止策を講じることで、この脆弱性を防ぐことが可能です。

CRLFインジェクションとHTTPヘッダインジェクションは密接に関連している攻撃手法であり、両者はしばしば混同されることがありますが、それぞれが焦点を当てる部分には若干の違いがあります。

CRLFインジェクション

概要:

HTTPヘッダインジェクション

概要:

違い

まとめ

これらの違いを理解し、両方の攻撃手法に対する適切な防御策を講じることが、Webアプリケーションのセキュリティを強化するために重要です。


CRLFインジェクションしないHTTPヘッダインジェクション

CRLFインジェクションでないHTTPヘッダインジェクションの例について説明します。

HTTPヘッダインジェクションの概要

HTTPヘッダインジェクションは、攻撃者が不正なデータをHTTPヘッダーに挿入することを狙った攻撃です。HTTPヘッダインジェクションの一部にCRLFインジェクションが含まれますが、ここではCRLFを用いない形のインジェクションの例について説明します。

HTTPヘッダインジェクションの例

PHPの例

<?php
$user = $_GET['user'];
header("X-User: " . $user);

攻撃手法:

Python(Flask)の例

from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def index():
    user_agent = request.args.get('user_agent')
    response = app.response_class(
        response='Hello, World!',
        status=200,
        headers={'User-Agent': user_agent}
    )
    return response

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

攻撃手法:

Node.js(Express)の例

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

app.get('/', (req, res) => {
    const referrer = req.query.referrer;
    res.setHeader('Referrer', referrer);
    res.send('Hello, World!');
});

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 javax.servlet.http.HttpServletResponse;

@Controller
public class HeaderController {

    @GetMapping("/set-header")
    public void setHeader(@RequestParam String value, HttpServletResponse response) {
        response.setHeader("X-Example", value);
    }
}

攻撃手法:


防止策

  1. ユーザー入力のサニタイズ:
    • ヘッダーに挿入されるユーザー入力は、エスケープまたはサニタイズ処理を行い、不正な文字列が含まれないようにします。

    PHPの例:

    $user = htmlspecialchars($_GET['user'], ENT_QUOTES, 'UTF-8');
    header("X-User: " . $user);
    

    Python(Flask)の例:

    import html
    user_agent = html.escape(request.args.get('user_agent'))
    
  2. HTTPヘッダーの構造に適さない文字列を除去:
    • ヘッダーに挿入されるデータがヘッダーとして無効な文字列を含んでいないかをチェックします。

    Node.jsの例:

    const referrer = req.query.referrer.replace(/[^a-zA-Z0-9\-.]/g, '');
    res.setHeader('Referrer', referrer);
    
  3. セキュリティフレームワークの利用:
    • 可能な限り、ユーザー入力をHTTPヘッダーに直接使用しないようにし、セキュアなライブラリやフレームワークを利用します。

まとめ: CRLFインジェクションはHTTPヘッダインジェクションの一種ですが、HTTPヘッダインジェクション全般には、ユーザー入力がエスケープされないままHTTPヘッダーに挿入されることで、不正な動作が発生するリスクがあります。ユーザー入力のエスケープやサニタイズ、無効な文字列の除去などの防止策を実施することで、これらの脆弱性を防ぐことができます。