最近、彼はPHPスクリプトからエンドユーザーのIPアドレスを決定することが本当に必要であるかについての興味深い議論を目撃しました。
実際、主題の各単語は実際の状況を反映しています。 それは宗教的な議論であり、素晴らしい春の天候によって悪化しました。そこでは、善悪はありませんでしたが、少しの研究を促し、私の幸福に、この告白の理解を終わらせましたが、実際には非常に簡単な質問でした。
私のように、私はそれを理解したと確信している人のために、ささいなことを理解
するのが面倒すぎる
ことを恐れました -猫の下で。
背景
Samsung SmartTVプラットフォーム用のVODサービスを開発する場合、著作権所有者が禁止している映画を誤って幸せなユーザーに見せないように、ユーザーの国を確実に知る必要があります。しかし、この契約期間の違反は、数千ドルの子供の罰金を伴いません見落とし)。
[コメントで指摘されているように、質問は合法であり、詐欺は可能ですが、記事はそのような詐欺を防ぐ方法についてではなく、phpとnginxを正しく友達にする方法についてです]サーバーには次のものがあります:php-fpm + nginx
国を決定する方法は? まあ、当然、ユーザーのIPおよびGEO IPベース
maxmindを通じて「... そして、彼のバイクを書かないために、彼
はstackoverflowを
グーグルで検索し 、すべての行を
詳しく調べ 、それをねじ込んでそこに残して、コードが成長しました:
public function getUserHostAddress(){ if (!empty($_SERVER['HTTP_X_REAL_IP']))
そしてうまくいきました! ほぼ1年...予期せぬことが起こるまで。 このコードでは当然のことながら...
PHPまたはプロキシチェーンを混乱させる方法(歴史の一部)
すべてが壊れています! そして、これは、支払いシステムの1つを台無しにしなければならず、1つのアドレスがHTTP_X_FORWARDED_FORではなく、コンマで区切られたアドレスのリスト(厳密に言えば、合法で許容され、
PHPドックで規制されていないため)すべてのコードがクラッシュしたときに起こりました
また、HTTP_X_REAL_IPまたはHTTP_CLIENT_IP(これもドックによって規制されていない)に目的のIPが含まれていたとしても、誰も気付かなかったでしょうが、残念ながらそれらは空でした:(
「さて」と思いました(今はもう一人ではありません)、すべてを書き直して、管理者にユーザーIPをREMOTE_ADDR変数にプッシュするように依頼します。
public function getUserHostAddress(){ $ip=$_SERVER['REMOTE_ADDR']; return $ip; }
そしてうまくいきました! ほぼ1か月...予期しないことが発生するまで。 このコードでは当然のことながら...
タフな男性の春の議論(これは皮肉ではありません-彼らはクールです)
すべてが壊れています! これは、nginxを更新する必要があるために発生しました。 そして、この問題の専門家、つまり管理者に頼りました。
そして、それらは、構成を更新し、REMOTE_ADDRに転送して「松葉杖/松葉杖ではない」(これを理解するまで)を取り除くことにしました。
REMOTE_ADDRは変更されずに残っています。 現在、「127.0.0.1」のようなものが輝いていました
HTTP_X_FORWARDED_FORでは、ユーザーのIPが詐欺されました(それまでは、ブラウザーからヘッダー `x-forwarded-for:999.999.999.999`を送信することで簡単に上書きされました)
そして、それが始まりました-P =開発、A =管理者:
A:すべてが故障しました。nginxプロキシがあるため、必要なアドレスはHTTP_X_FORWARDED_FORにあり、REMOTE_ADDRにはphp-fpmへのクライアントの実際のIPアドレスがあります(つまり127.0.0.1)
R:しかし、HTTP_X_FORWARDED_FORは信じられません。これは、ヘッダーを介してサーバーに簡単に再定義できる変数であり、かなり興味深い
記事を引用して
A:いいえ、エンドユーザーの実際のIPを含め、REMOTE_ADDRにはphpへの実際のクライアントアドレスを含めます
R:その後、プロキシのシーケンスに従いませんが、別のサーバーでの普遍化(プロキシなしなど)のために、これらの構成はREMOTE_ADDRのすべてを突き詰めるとは限りません。
...それは簡単でマットなしです...
その結果、当然、すべてが起動し、必要な状態でプロキシとすべての変数(より正確には注意を払う必要のある変数)なしでクライアントが直接接続されているとphpが判断したときに、透過プロキシに落ち着きました。
ただし、この問題には風水が十分ではなく、実際に
はプロキシがあり、
プロキシはありません。
誰が責任があるのか、誰が正しいのか
私たちが判断するのではなく、誰も判断しません!
本当にPHPに直接クライアントの束、または透過的なプロキシがある場合、すべては簡単です-健康のためにREMOTE_ADDRを使用してお楽しみください。
しかし、通常のプロキシを使用し、PHPにそれを知らせたい場合、ファニングについてはどうでしょうか。
レシピ...しかし万能薬ではない:
- REMOTE_ADDR-直接アクセスするnginxのIPアドレスが含まれます。この例では127.0.0.1です。
- HTTP_X_FORWARDED_FOR-プロキシアドレスのチェーンを含み、最後はプロキシサーバーにアクセスする直接クライアントのIPです。 そして、ここでは2つの特別なケースを考えます:
- カスケードプロキシではありません。 HTTP_X_FORWARDED_FORでは、最後または唯一のIPアドレス(ユーザーがx-forwarded-forヘッダーで送信した/送信しなかったものに応じて)は、実際の検索された同じユーザーアドレスになります。
問題はこの変数を解析し、そこから最後の要素を取得することだと思われます。 しかし、この場合、設定は完全に正しくなく、HTTP_X_FORWARDED_FOR 全体がx-forwarded-forブラウザーからのヘッダーに置き換えられ、 直接ユーザーの実際のIPをそれに固定する必要がありました。
たとえば、産業用vpsホスティングをチェックしました:

そのようなデータを信頼することも怖いですが、設定ですべてが正しく行われた場合、最後のIPはヘッダーの内容に関係なくユーザーのアドレスになります。
- カスケードプロキシ。 この場合、実際にはHTTP_X_FORWARDED_FOR-プロキシアドレスのチェーンが含まれ、最後はプロキシサーバーに向けられたダイレクトクライアントのIPです。 ただし、これはユーザーの実際のIPではなく、リスト内の前のプロキシのIPです。
問題はこの変数を解析し、そこから最初の要素を取得することだと思われます。 しかし、上の図に示すように、これは確かに正しいデータではなく、ユーザーはIPが必要とする最初の要素をx-forwarded-forに送信することで2つのアカウントに誤解させる可能性があります
- HTTP_X_REAL_IP (またはAdminとRazrabが同意する他の変数)-サーバーからphpまたは最初の信頼できないプロキシにアクセスするユーザーのIPアドレスが含まれます(クライアントアドレスと同じです)
便宜上、カスケードプロキシと非カスケードプロキシを決定する問題を解消するnginxの特別なモジュールを使用できますが、デフォルトでは「パラメータ--with-http_realip_moduleなしの何らかの理由で、centos、debik、およびfedoraの標準アセンブリのnginx」(c) Adminになりますそれだけでなく、 HTTP_X_FORWARDED_FOR のチェーンが正しく形成され 、HTTP_X_FORWARDED_FORから最後の要素を取得できる信頼できるプロキシサーバーのアドレスが構成されている必要があります。
ただし、ここでも、HTTP_X_REAL_IPは一般的なエンドユーザーIP ではなく、カスケードプロキシのプロキシリストの最初のIPのみです。
プロキシがカスケード接続されていない場合、エンドユーザーのアドレスもそこにある可能性があります。
プロキシがカスケード接続され、http_realipモジュールが正しく構成されている場合、phpサーバーから数えれば、エンドユーザーIPまたは最初の信頼できないプロキシの正しいIPが存在するはずです。 - HTTP_CLIENT_IP (または管理者とRazrabが同意する他の変数) -HTTP_X_FORWARDED_FORの最初のIPをプロキシするすべてのタイプを含み、プロキシがない場合、httpヘッダーのコンテンツはclient-ipです。 参照のみに使用できます。 そして、ユーザーの実際のIPを決定する場合はありません 。
結論として
php + nginxにはいくつかのプロキシオプションがあります
- 透過的 -_SERVERの変数の定数内容(REMOTE_ADDRを含む)は、PHPで直接作業しているかのように特徴的です
- カスケードではなく透過的ではありません - 管理者とRazrabがユーザーの実際のIPアドレスの保存場所について合意する必要があるのが特徴です:)
- 非透過カスケード -同じことは、 非透過非カスケード + nginx用の正しく構成されたモジュールの特徴です 。カスケードプロキシの可能性と、ユーザーが悪意があり、_SERVER ["HTTP_xxxx"]に非常に誤ったデータを送信できることも覚えておく必要があります
PS後で風水を設定に入れて透過的なプロキシを削除し、両方のプロキシの場合にIPを決定する汎用関数を作成します。
PPS楽しみのために、誰が気にします:コメントの誰かがこの関数を書いて、nginx configが私たちのためにあり、それを使用するなら、正直に、彼は電話に100ルーブルを受け取ります。
しかし、この関数と設定は本当に正統であり、すべてを考慮する必要があります:)すべての手がかりは記事にあります。
主なものはZenです。時間をかけてください-突然、最初のものがエラーで書き込まれ、それらを考慮して、急いでください-突然、最初の正しい答えはあなた次第です。
どうもありがとう。 良い春を! 同僚とアレンジして、彼らを愛してください! :)
UDP:独自の実装:
public function getUserHostAddress( $ip_param_name = null, $allow_non_trusted = false, array $non_trusted_param_names = array('HTTP_X_REAL_IP','HTTP_CLIENT_IP','HTTP_X_FORWARDED_FOR','REMOTE_ADDR') ){ if(empty($ip_param_name) || !is_string($ip_param_name)){