WP 4.8.2の使い方
Rest-apiでリクエストを処理するときにリクエストURLをチェックするための最良の方法は何ですか?
たとえば、サイトがリクエストを受け取り、それが「許可された」URLから来たかどうかを確認したいとします。 URLが許可されていない場合は失敗します。
これは動作しません:
function my_check_request_url( $request, $url ) {
$bits = parse_url( $url );
if ( $bits['Host'] != 'example.com' )
$request = false;
return $request;
}
add_filter( 'rest_request_from_url', 'my_check_request_url', 10, 2 );
そのフィルタは間違いなくあなたが探しているものではありません。このフィルタは、埋め込みを処理するために内部的にのみ使用されるファクトリメソッドのように見えるWP_REST_Request::from_url()
の結果を返す前に起動します。
もっと良い方法は WP_Error
filter でrest_pre_dispatch
インスタンスを返すことです。
いくつかの注意点:
@miloが述べたように、参照元は信頼できないため、セキュリティチェックには使用しないでください。
また、設定が保証されているわけでもありません。
邪魔にならない人たちのために、rest_pre_dispatch
フィルターを使って、悪いリファラーからのものであればリクエストを失敗させる方法の例を以下に示します。
function wpse281916_rest_check_referer( $result, $server, $request ) {
if ( null !== $result ) {
// Core starts with a null value.
// If it is no longer null, another callback has claimed this request.
// Up to you how to handle - for this example we will just return early.
return $result;
}
$referer = $request->get_header( 'referer' );
if ( ! $referer ) {
// Referer header is not set - If referer is required, return a WP_Error instance instead.
return $result;
}
$Host = wp_parse_url( $referer, PHP_URL_Host );
if ( ! $Host ) {
// Referer is malformed - If referer is required, return a WP_Error instance instead.
return $result;
}
if ( 'mysite.com' !== $Host ) {
// Referer is set to something that we don't allow.
return new WP_Error(
'invalid-referer',
'Requests must contain a valid referer',
compact( 'referer' )
);
}
// Otherwise we are good - return original result and let WordPress handle as usual.
return $result;
}
add_filter( 'rest_pre_dispatch', 'wpse281916_rest_check_referer', 10, 3 );
クライアントから受け取ったものはすべてユーザー入力とみなされ、信頼されるべきではありません。ヘッダーは簡単に操作して悪用される可能性があるため、機密データを信頼している場合はこの方法を使用しないことをお勧めします。
リクエストがページからのものである場合は、別の方法があります。そうでなければ、誰もがどこからもAPIにリクエストを送信し、参照元を変更することができません。
"Allowed" としてフィルタリングされているたくさんのページがあるとしましょう。これらのページにのみナウンスを作成してから、リクエストでそれらを検証できます。
ナウンスが存在し有効であれば、要求は許可されます。そうでなければ、それをブロックします。
@ ssnepenthe 's aswer は、使用しているフックが着信リクエストの正しいものではないということを言っています。
リクエスト情報はPHPですぐに利用できるため、利用可能な最も早いフックを使用して確認できます。また、リクエストAPIのコンテキストでこれを行うには、REST APIリクエストの最も早いフックを使用する必要があります。 @ ssnepentheが提案する'rest_pre_dispatch'
は問題ありませんが、別のオプションは rest_authentication_errors
であり、何か問題が発生した場合にエラーを返すことができます。
しかし、Jack Johanssonは、 言う にあり、HTTPヘッダー(@ssnepentheのaswerで使用されるリファラーヘッダーなど)は信頼できない、クライアントによって非常に簡単に変更されるためです。ですから、ドアの前に警備員を置き、「入場させても安全ですか?」と尋ねるようなものです。行きたい人には:それはうまくいきません。
しかし、Jack Johanssonの答え(ナンス)を提案した解決策も真の解決策ではありません。ナンスの要点は時間とともに変化することであり、パブリックAPIエンドポイントには時間に基づいて変化するものはありません。さらに、WPナンスは、ログインユーザーがいる場合にのみ信頼できます。これは、パブリックAPIの場合ではない可能性があり、ユーザーがログインしている場合、着信ドメインをチェックする理由はおそらくありません:あなたユーザーマシンではなく、ユーザーを信頼します。
だから、何をすべきか?
さて、HTTPヘッダーが信頼できない場合でも、$_SERVER
で利用できるすべての情報がヘッダーから取得されるわけではありません。
通常、$_SERVER
で始まるキーを持つすべてのHTTP_
値はヘッダーから取得され、安全でないユーザー入力として処理する必要があります。
ただし、たとえば、$_SERVER['REMOTE_ADDR']
には、サーバーへのTCP接続に使用されるIPアドレスが含まれています。つまり、is信頼できる1。
また、次のいずれかを意味します。
$_SERVER['REMOTE_Host']
値を生成するようにサーバーを適切に構成します(たとえば、Apacheでは、HostnameLookups On
内にhttpd.conf
が必要です)その値gethostbyaddr
を使用して、逆DNSルックアップを実行し、$_SERVER['REMOTE_ADDR']
に保存されているIPのドメイン名を解決します。ホワイトリストと照合するために使用できるホスト名を非常に確実に取得できます(コードについては、$referer = $request->get_header('referer')
を$referer = gethostbyaddr($_SERVER['REMOTE_ADDR'])
に置き換える@ssnepentheのaswerのコードを適合させることができます)。
しかし、issueがあります。
Webサーバーがリバースプロキシの背後にある場合(実際には非常に一般的なソリューション)、WebサーバーへのTCP接続は実際にプロキシによって行われるため、$_SERVER['REMOTE_ADDR']
はプロキシのIPであり、IPのIPではありません最初にリクエストを送信したクライアント。
そのような場合の元のリクエストIPは通常$_SERVER['HTTP_X_FORWARDED_FOR']
として使用できますが、$_SERVER
で始まるHTTP_
値の1つであるため、実際には信頼できません。
したがって、ウェブサーバーがリバースプロキシの背後にある場合2 $_SERVER['REMOTE_ADDR']
でさえそのようなガードには役に立たず、ドメインベースのホワイトリストはプロキシレベルでのみ実装できます。
要するに、APIエンドポイントのセキュリティ保護のための信頼できるソリューションは、何らかのreal認証メカニズム(oAuthなど)を使用して実装するか、アプリケーションレベルではなくサーバー構成で直接実行する必要があります。
1 理論上、誰かがあなたのISPをハッキングしたり、攻撃者がLAN内から行動したりすると、壊れる可能性があります。どちらの場合も、安全にするためにできることはほとんどありません。
2 リバースプロキシの背後にいるかどうかわからない場合は、ローカルPCからリクエストを送信し、サーバー上の$_SERVER['REMOTE_ADDR']
がローカルPC IPと一致するかどうか、また$_SERVER['HTTP_X_FORWARDED_FOR']
が存在し、ローカルPC IPと一致するかどうかを確認できます。