periksa url yang diminta

9

Menggunakan WP 4.8.2

Apa cara terbaik untuk memeriksa URL yang meminta ketika memproses permintaan dengan api sisanya?

Misalnya, sebuah situs menerima permintaan dan Anda ingin memeriksa apakah itu berasal dari URL yang 'diizinkan'. Dan gagal jika URL tidak diizinkan.

Ini tidak bekerja:

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 );
shanebp
sumber
Setelah berkomentar kondisional, respons masih dikirim. Jadi saya pikir saya menggunakan pengait yang salah.
shanebp
Sudahkah Anda memeriksa seperti apa $requestdan $urlvars melalui var_dumpatau serupa, saya menemukan bahwa memeriksa input dan output selalu mengarah ke jawaban yang tepat.
farinspace
2
merujuk URL mudah dipalsukan dan tidak dapat digunakan untuk segala jenis keamanan.
Milo
Kami menggunakan token dan ssl. Kami juga ingin memeriksa url rujukan, terlepas dari apakah itu dapat dipalsukan atau tidak.
shanebp
2
itu adalah API yang terbuka untuk web, apa pembicaraan tentang wasit ini karena otentikasi dan SSL tidak relevan. Anda mungkin juga menonaktifkan perlindungan CORS ... Kecuali jika hanya tersedia untuk pengguna yang masuk, ini tidak memiliki keamanan di dalamnya.
Mark Kaplun

Jawaban:

5

Filter itu jelas bukan yang Anda cari. Filter itu menyala sebelum mengembalikan hasil WP_REST_Request::from_url()yang tampaknya merupakan metode pabrik yang hanya digunakan secara internal untuk menangani embed.

Opsi yang lebih baik adalah mengembalikan WP_Errorinstance pada rest_pre_dispatchfilter .

Beberapa peringatan:

Seperti yang disebutkan oleh @ moilo, referer tidak dapat diandalkan dan tidak seharusnya digunakan untuk pemeriksaan keamanan.

Selain itu, tidak dijamin untuk diatur.

Dengan hal-hal yang menghalangi, berikut adalah contoh bagaimana Anda dapat menggunakan rest_pre_dispatchfilter untuk menyebabkan permintaan gagal jika berasal dari pengarah yang buruk:

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 );
ssnepenthe
sumber
4

Apa pun yang Anda terima dari klien dianggap sebagai input pengguna dan tidak boleh dipercaya. Karena tajuk dapat dengan mudah dimanipulasi dan disalahgunakan, saran saya adalah jangan menggunakan metode ini jika Anda mengandalkannya untuk data sensitif.

Jika permintaan datang dari halaman, Anda dapat memiliki pendekatan lain. Kalau tidak, siapa pun dapat mengirim permintaan ke API entah dari mana dan mengubah pengarah.

Katakanlah Anda memiliki banyak halaman yang difilter sebagai "Diizinkan" . Anda dapat membuat nounce hanya untuk halaman-halaman ini, dan kemudian memvalidasinya dalam permintaan Anda.

Jika ada nounce dan valid, maka permintaan diizinkan. Kalau tidak, blokir.

Jack Johansson
sumber
4
+1 ... itu adalah API .... anggapan Anda hanya mendapat panggilan dari browser adalah konyol.
Mark Kaplun
Ya, saya pikir nounce adalah pendekatan yang lebih baik karena tidak ada jika seseorang langsung mengirim permintaan ke API.
Jack Johansson
4

@ssnepenthe 's aswer adalah benar dalam mengatakan bahwa hook Anda gunakan bukan orang yang tepat sesuatu dalam permintaan yang masuk.

Informasi permintaan tersedia segera ke PHP, jadi Anda bisa menggunakan pengait paling awal yang tersedia untuk memeriksanya. Dan jika Anda ingin melakukan ini dalam konteks API permintaan, Anda harus menggunakan hook paling awal dari permintaan REST API. 'rest_pre_dispatch'disarankan oleh @ssnepenthe baik-baik saja, mungkin opsi lain rest_authentication_errorsyang memungkinkan Anda untuk mengembalikan kesalahan jika ada sesuatu yang salah.

Tetapi Jack Johansson benar dalam mengatakan bahwa header HTTP (seperti header referer yang digunakan dalam aswer @ ssnepenthe) tidak dapat dipercaya, karena mereka sangat mudah diubah oleh klien. Jadi itu akan seperti menempatkan seorang penjaga keamanan di depan pintu yang hanya bertanya "apakah aman untuk membiarkan Anda masuk?" untuk siapa saja yang ingin masuk: itu tidak akan berhasil.

Tetapi soluisi yang diajukan jawaban Jack Johansson (nonce) juga bukan solusi nyata: seluruh titik nonces adalah berubah seiring waktu, dan titik akhir API publik tidak dapat memiliki hal-hal yang berubah berdasarkan waktu. Selain itu, WP nonces hanya dapat dipercaya ketika ada pengguna yang masuk, yang mungkin tidak berlaku untuk API publik dan jika pengguna masuk, mungkin tidak ada alasan untuk memeriksa domain yang masuk: Anda mempercayai pengguna, bukan mesin pengguna.

Jadi, apa yang harus dilakukan?

Ya, meskipun header HTTP tidak dapat dipercaya, tidak semua informasi yang tersedia $_SERVERberasal dari header.

Biasanya, semua $_SERVERnilai yang kunci-kuncinya dimulai yang dimulai dengan HTTP_berasal dari header dan harus diperlakukan sebagai input pengguna yang tidak aman .

Tetapi, misalnya, $_SERVER['REMOTE_ADDR']berisi alamat IP yang digunakan untuk koneksi TCP ke server Anda, yang berarti itu dapat dipercaya 1 .

Yang juga berarti, bahwa:

  • mengkonfigurasi server dengan benar untuk menghasilkan $_SERVER['REMOTE_HOST']nilai (misalnya di Apache Anda perlu HostnameLookups Ondi dalam Anda httpd.conf) nilai itu
  • gunakan gethostbyaddruntuk melakukan pencarian DNS terbalik untuk menyelesaikan nama domain dari IP yang disimpan di$_SERVER['REMOTE_ADDR']

Anda dapat memperoleh nama host yang cukup andal yang dapat Anda gunakan untuk mengecek daftar putih (untuk kode, Anda dapat mengadaptasi kode dari aswer @ ssnepenthe di mana Anda akan menggantinya $referer = $request->get_header('referer')dengan $referer = gethostbyaddr($_SERVER['REMOTE_ADDR'])).

Tapi ada masalah .

Jika server web Anda berada di belakang proksi terbalik (solusi yang cukup umum, sebenarnya) koneksi TCP ke server sebenarnya dibuat oleh proksi tersebut, maka itu $_SERVER['REMOTE_ADDR']akan menjadi IP dari proksi tersebut, dan bukan IP dari klien yang awalnya mengirim permintaan.

IP permintaan asli dalam kasus seperti itu biasanya tersedia sebagai $_SERVER['HTTP_X_FORWARDED_FOR'], tetapi menjadi salah satu dari $_SERVERnilai - nilai yang dimulai dengan HTTP_itu tidak benar-benar dapat dipercaya.

Jadi, jika server web Anda berada di belakang proxy terbalik 2 bahkan $_SERVER['REMOTE_ADDR']tidak akan berguna untuk penjaga tersebut dan daftar putih berbasis domain hanya bisa diimplementasikan di tingkat proxy.

Singkatnya, solusi yang dapat diandalkan untuk pengamanan titik akhir API harus diimplementasikan dengan menggunakan beberapa mekanisme otentikasi nyata (misalnya oAuth) atau harus dilakukan dengan bertindak langsung pada konfigurasi server dan tidak pada tingkat aplikasi.


Catatan

1 Ya, secara teori itu bisa rusak jika seseorang meretas ISP Anda atau jika penyerang bertindak dari dalam LAN Anda, dalam kedua kasus sangat sedikit yang bisa Anda lakukan untuk aman.

2 Jika Anda tidak tahu apakah Anda berada di belakang proxy terbalik, Anda dapat mengirim permintaan dari PC lokal Anda dan memeriksa apakah $_SERVER['REMOTE_ADDR']di server cocok dengan IP PC lokal dan juga jika $_SERVER['HTTP_X_FORWARDED_FOR']ada dan cocok dengan IP PC lokal.

gmazzap
sumber
OP sedang mencoba untuk mendapatkan referer, jadi saya berasumsi dia ingin melakukannya pada halaman, tidak langsung melakukan ping API.
Jack Johansson
@JackJohansson sebenarnya perujuk OP tidak pernah disebutkan :) Mereka mengatakan mereka ingin "untuk memeriksa apakah itu berasal dari URL 'diizinkan'" yang terdengar mereka mencari titik akhir API daftar putih untuk domain speficic, dan juga potongan kode di OP memberikan hal yang sama ide untuk saya.
gmazzap