よくあるNginxの設定ミスの例

よくあるNginxの設定ミスの例

Nginx は現在主流の Web サービスです。 以下に、最も一般的な誤った構成をいくつか示します。

ルートの場所が見つかりません

サーバー{
  ルート /etc/nginx;

  場所 /hello.txt {
    try_files $uri $uri/ =404;
    プロキシパス http://127.0.0.1:8080/;
  }
}

rootディレクティブは、Nginx ルート ディレクトリを指定します。 上記の例では、ルート ディレクトリは /etc/nginx であるため、そのディレクトリの下のファイルにアクセスできます。 上記の構成では、 /の場所 ( location / {...} ) はなく、/hello.txt の場所のみが指定されています。 したがって、ルート ディレクティブはグローバルに設定され、/ へのリクエストはローカル パス /etc/nginx に移動することになります。

GET /nginx.confのような単純なリクエストは、/etc/nginx/nginx.conf に保存されている Nginx 構成ファイルの内容を表示します。 ルートが /etc に設定されている場合、/nginx/nginx.conf への GET リクエストにより設定ファイルが表示されます。 場合によっては、他の構成ファイル、アクセス ログ、さらには HTTP 基本認証の暗号化された資格情報にアクセスできる可能性があります。

私たちが収集した約 50,000 個の Nginx 構成ファイルの中で、最も一般的なルート パスは次のとおりです。

オフバイスラッシュ

サーバー{
  80 default_server をリッスンします。

  サーバー名_;

  場所 /static {
    エイリアス /usr/share/nginx/static/;
  }

  場所 /api {
    proxy_pass http://apiserver/v1/;
  }
}

Off-by-slash 構成エラーでは、/ が欠落しているためにパスを 1 ステップ上に移動できる可能性があります。この手法は、Orange Tsai 氏の Blackhat 講演「Breaking Parser Logic!」で広く知られるようになりました。 この講演では、location ディレクティブのスラッシュが欠落していることと alias ディレクティブを組み合わせることで、Web アプリケーションのソース コードを読み取ることができるようになる方法を説明します。 あまり知られていないのは、proxy_pass などの他のディレクティブと組み合わせて使用​​することもできるということです。 何が起こっているのか、そしてなぜそれが機能するのかを分析してみましょう。

  場所 /api {
    proxy_pass http://apiserver/v1/;
  }

次の構成が Nginx サーバーにアクセスできる場合は、http://apiserver/v1/ の下のパスのみがアクセス可能であると想定できます。

http://server/api/user -> http://apiserver/v1//user

http://server/api/user にリクエストが行われると、Nginx はまず URL を正規化します。 次に、プレフィックス /api が URL と一致するかどうかを確認します。一致する場合は一致します。 プレフィックスは URL から削除され、/user パスが残ります。 このパスはproxy_pass URL に追加され、最終的な URL は http://apiserver/v1//user になります。 location ディレクティブがスラッシュで終わっておらず、 proxy_pass URL パスがスラッシュで終わっているため、URL に二重のスラッシュがあることに注意してください。 ほとんどの Web サーバーは、http://apiserver/v1//user ユーザーを http://apiserver/v1/user に正規化します。つまり、構成エラーがあっても、すべてが期待どおりに動作し、気付かれない可能性があります。

この誤った構成は、http://server/api../ を要求することによって悪用される可能性があり、これにより Nginx は http://apiserver/v1/../ に正規化された URL http://apiserver/ を要求することになります。 これが及ぼす影響は、この誤った構成を悪用することで何を達成できるかによって異なります。 たとえば、これにより、Apache サーバーのステータスが URL http://server/api../server-status 経由で公開される可能性があり、また、パブリックにアクセスできないようにしたいパスにアクセス可能になる可能性があります。

Nginx サーバーが誤って構成されていることを示す兆候の 1 つは、URL 内のスラッシュを削除しても、サーバーが同じ応答を返すことです。 たとえば、http://server/api/user と http://server/apiuser が同じ応答を返す場合、サーバーは脆弱である可能性があります。 これにより、次のリクエストが送信されます。

http://server/api/user -> http://apiserver/v1//user
http://server/apiuser -> http://apiserver/v1/user

安全でない変数の使用

一部のフレームワーク、スクリプト、および Nginx 構成では、Nginx に保存された変数が安全でない方法で使用されます。 これにより、XSS、HttpOnly 保護のバイパス、情報漏洩、場合によっては RCE などの問題が発生する可能性があります。

スクリプト名

構成は次のとおりです。

  場所 ~ \.php$ {
    fastcgi_params を含めます。
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    127.0.0.1:9000; をデフォルトとして設定します。
  }

主な問題は、ファイルがディスク上に存在しない場合でも、Nginx が .php で終わるすべての URL を PHP インタープリターに送信することです。 これは、Nginx が作成した「落とし穴とよくある間違い」ドキュメントに記載されている、Nginx の多くの誤った構成の 1 つです。

PHP スクリプトが SCRIPT_NAME に基づいてベース URL を定義しようとすると、XSS が発生します。

<?php

if(basename($_SERVER['SCRIPT_NAME']) ==
ベース名($_SERVER['SCRIPT_FILENAME']))
 dirnameをエコーし​​ます($_SERVER['SCRIPT_NAME']);

?>

GET /index.php/<script>アラート(1)</script>/index.php
SCRIPT_NAME = /index.php/<script>alert(1)</script>/index.php

$uri を使用すると CRLF インジェクションが発生する可能性があります

Nginx 変数に関連するもう 1 つの誤った構成は、$request_uri の代わりに $uri または $document_uri を使用することです。 $uri と $document_uri には正規化された URI が含まれており、Nginx での正規化には URI の URL デコードが含まれます。 Volema は、Nginx 構成でリダイレクトを作成すると、多くの場合 $uri を使用して CRLF インジェクションが発生する可能性があることを発見しました。

脆弱な Nginx 構成の例は次のとおりです。

位置 / {
 302 https://example.com$uri を返します。
}

HTTP リクエストの改行文字は\r (キャリッジ リターン) と\n (ライン フィード) です。 改行文字を URL エンコードすると、次の文字%0d%0aが表現されます。 これらの文字がサーバーへの誤って構成されたリクエストに含まれている場合 (たとえば、http://localhost/%0d%0aDetectify:%20clrf)、URL デコード後の$uri変数に改行文字が含まれるため、サーバーは Detectify という名前の新しいヘッダーで応答します。

HTTP/1.1 302 一時的に移動しました
サーバー: nginx/1.19.3
コンテンツタイプ: text/html
コンテンツの長さ: 145
接続: キープアライブ
場所: https://example.com/
検出: clrf

任意の変数

場合によっては、ユーザー提供のデータを Nginx 変数として扱うことができます。 なぜこのようなことが起こるのかは明らかではありませんが、この H1 レポートに示されているように、これは珍しいことではなく、テストも容易ではありません。 エラー メッセージを検索すると、SSI フィルター モジュールで見つかることがわかります。つまり、これは SSI が原因であることがわかります。

テスト方法は次のとおりです。

$ curl -H 'Referer: bar' http://localhost/foo$http_referer | grep 'foobar'

生のバックエンド応答の読み取り

Nginx のproxy_passを使用すると、バックエンドによって作成されたエラーと HTTP ヘッダーをインターセプトできます。 これは、内部エラー メッセージとヘッダーを非表示にして、Nginx で処理できるようにする場合に便利です。 バックエンドがリクエストに応答できない場合、Nginx は自動的にカスタム エラー ページを提供します。 しかし、Nginx がこれが HTTP 応答であることを理解しない場合はどうなるでしょうか?

クライアントが無効な HTTP リクエストを Nginx に送信した場合、リクエストはそのままバックエンドに転送され、バックエンドは元のコンテンツで応答します。 Nginx は無効な HTTP 応答を理解できず、クライアントに転送します。 次のような uWSGI アプリケーションを想像してください。

defアプリケーション(environ、start_response):
 start_response('500 エラー', [('コンテンツタイプ',
'text/html'),('シークレットヘッダー','シークレット情報')])
 return [b"秘密情報なので、表示しないでください!"]

Nginx の設定は次のとおりです。

http {
 エラーページ 500 /html/error.html;
 proxy_intercept_errors がオン;
 proxy_hide_header シークレットヘッダー;
}

proxy_intercept_errors は、バックエンドの応答ステータスが 300 より大きい場合にカスタム応答を提供します。上記の uWSGI アプリケーションでは、500 エラーを送信しますが、これは Nginx によってインターセプトされます。

proxy_hide_header: 指定された HTTP ヘッダーをクライアントから非表示にできます。

通常の GET リクエストを送信すると、Nginx は次を返します。

HTTP/1.1 500 内部サーバーエラー
サーバー: nginx/1.10.3
コンテンツタイプ: text/html
コンテンツの長さ: 34
接続: 閉じる

ただし、次のような無効な HTTP リクエストを送信した場合、

GET /? XTTP/1.1
ホスト: 127.0.0.1
接続: 閉じる

次のような返答が届きます。

XTTP/1.1 500 エラー
コンテンツタイプ: text/html
シークレットヘッダー: secret-info

秘密情報なので、見られてはいけません!

merge_slashes がオフに設定されています

デフォルトでは、 merge_slashesディレクティブはonに設定されています。これは、2 つ以上のスラッシュを 1 つに圧縮するメカニズムであり、 ////になります。 Nginx がリバース プロキシとして使用されており、プロキシされたアプリケーションがローカル ファイルのインクルードに対して脆弱な場合、リクエストで余分なスラッシュを使用すると、悪用される余地が生じる可能性があります。 Danny Robinson 氏と Rotem Bar 氏がこれについて詳しく説明しています。

上記は、よくある Nginx の誤った設定の詳細です。Nginx の誤った設定の詳細については、123WORDPRESS.COM にアクセスして、その他の関連記事をご覧ください。

以下もご興味があるかもしれません:
  • nginxリバースプロキシサービスは、設定ファイルのエラーによりリソースにアクセスするときに404エラーを引き起こします。
  • 404 エラー ページをリダイレクトするように NGINX サーバーを構成する方法
  • Nginx キャッシュとエラーページの設定
  • Nginx サーバーでよくあるアップロードおよび接続エラーを解決するために設定を変更します
  • Nginx サーバーで 404 エラー ページを構成する際に注意すべき点
  • Nginx サーバーの 414 および 504 エラーの構成ソリューション
  • Nginx の worker_connections 設定が低すぎるため、500 エラーが発生します
  • Nginx で PHP-FPM を使用するときに PHP エラー ログを構成する方法
  • NGINX で 404 エラー ページを構成する方法

<<:  角丸四角形の HTML+CSS 実装コード

>>:  MySQL で固定されていない位置から文字列要素を抽出する方法

推薦する

TypeScript における列挙型の理解と応用シナリオ

目次1. 何ですか2. 使用数値列挙文字列列挙異種列挙自然3. 応用シナリオ要約する1. 何ですか列...

Ansibleを使用してディレクトリ内のすべてのコンテンツを削除する方法

Ansible を使用する学生は、以下に示すように、Ansible が特定のフォルダーまたはファイル...

MySQL が uuid または snowflake id を主キーとして使用することを推奨しない理由の詳細な分析

前書き: MySQL でテーブルを設計する場合、MySQL では UUID や非連続かつ非繰り返しの...

Win10 構成 Tomcat 環境変数チュートリアル図

設定する前に、次の操作を行う必要があります。 1. まずjdk bloggerをインストールします。...

インストールされていないバージョンの MySQL を使用する手順とパスワードを忘れた場合の解決策

最初のステップは、圧縮されたパッケージを対応するディスクに解凍することです。 2 番目の手順は、cm...

SSHを使用してDockerサーバーに接続する方法

初めて docker に触れたときは本当に戸惑いました。初心者向けのチュートリアルを長い間読みました...

CentOS 7 で MySQL 接続数が 214 に制限される問題の解決方法

問題を見つける最近、プロジェクトで問題が発生しました。接続が多すぎるため、「接続が多すぎます」という...

ByteDance インタビュー: JS を使用して Ajax 同時リクエスト制御を実装する方法

序文正直に言うと、最近とても混乱していると感じています。テクノロジーと人生について。また、将来の発展...

問題におけるJS演算子の調査

問題は、誰もが「メモリ リーク」について知っていることです。一般的なシナリオはいくつかあります。クロ...

MySQLにデータを素早くインポートする方法

序文:日々の勉強や仕事の中で、データをエクスポートする必要に迫られることがよくあります。たとえば、デ...

MySQLで偽または真を保存する方法

MySQL ブール値、偽または真を格納つまり、データベースに保存されるブール値は 0 と 1 であり...

XHTML: フレーム構造タグ

フレーム構造タグ <frameset></frameset>フレームを使用す...

Mysql の追加、削除、変更、クエリステートメントのシンプルな実装

Mysql の追加、削除、変更、クエリステートメントのシンプルな実装追加されたレコード: テーブル名...

Docker JVM メモリ使用量の表示

1. Docker コンテナのホスト マシンに入り、指定されたイメージを実行しているコンテナ ID ...

Docker で MySQL データベースを使用して LAN アクセスを実現する

1. MySQLイメージを取得する docker pull mysql:5.6注: mysql5.7...