Apa cara paling akurat untuk mengambil alamat IP yang benar dalam PHP?

301

Saya tahu ada sejumlah besar variabel $ _SERVER header tersedia untuk pencarian alamat IP. Saya bertanya-tanya apakah ada konsensus umum tentang bagaimana cara paling akurat mengambil alamat IP asli pengguna (juga mengetahui tidak ada metode yang sempurna) menggunakan variabel tersebut?

Saya menghabiskan waktu mencoba mencari solusi yang mendalam dan menghasilkan kode berikut berdasarkan sejumlah sumber. Saya akan senang jika seseorang bisa menyodok jawaban atau memberi penjelasan tentang sesuatu yang mungkin lebih akurat.

sunting mencakup optimisasi dari @Alix

 /**
  * Retrieves the best guess of the client's actual IP address.
  * Takes into account numerous HTTP proxy headers due to variations
  * in how different ISPs handle IP addresses in headers between hops.
  */
 public function get_ip_address() {
  // Check for shared internet/ISP IP
  if (!empty($_SERVER['HTTP_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_CLIENT_IP']))
   return $_SERVER['HTTP_CLIENT_IP'];

  // Check for IPs passing through proxies
  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
   // Check if multiple IP addresses exist in var
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    foreach ($iplist as $ip) {
     if ($this->validate_ip($ip))
      return $ip;
    }
   }
  }
  if (!empty($_SERVER['HTTP_X_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_X_FORWARDED']))
   return $_SERVER['HTTP_X_FORWARDED'];
  if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
   return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
  if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && $this->validate_ip($_SERVER['HTTP_FORWARDED_FOR']))
   return $_SERVER['HTTP_FORWARDED_FOR'];
  if (!empty($_SERVER['HTTP_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_FORWARDED']))
   return $_SERVER['HTTP_FORWARDED'];

  // Return unreliable IP address since all else failed
  return $_SERVER['REMOTE_ADDR'];
 }

 /**
  * Ensures an IP address is both a valid IP address and does not fall within
  * a private network range.
  *
  * @access public
  * @param string $ip
  */
 public function validate_ip($ip) {
     if (filter_var($ip, FILTER_VALIDATE_IP, 
                         FILTER_FLAG_IPV4 | 
                         FILTER_FLAG_IPV6 |
                         FILTER_FLAG_NO_PRIV_RANGE | 
                         FILTER_FLAG_NO_RES_RANGE) === false)
         return false;
     self::$ip = $ip;
     return true;
 }

Words of Warning (pembaruan)

REMOTE_ADDRmasih merupakan sumber alamat IP yang paling dapat diandalkan . $_SERVERVariabel lain yang disebutkan di sini dapat dipalsukan oleh klien jarak jauh dengan sangat mudah. Tujuan solusi ini adalah untuk mencoba menentukan alamat IP klien yang duduk di belakang proxy. Untuk tujuan umum Anda, Anda dapat mempertimbangkan menggunakan ini dalam kombinasi dengan alamat IP yang dikembalikan langsung dari $_SERVER['REMOTE_ADDR']dan menyimpan keduanya.

Untuk 99,9% pengguna solusi ini akan sesuai dengan kebutuhan Anda dengan sempurna. Itu tidak akan melindungi Anda dari 0,1% pengguna jahat yang ingin menyalahgunakan sistem Anda dengan menyuntikkan header permintaan mereka sendiri. Jika mengandalkan alamat IP untuk sesuatu misi penting, gunakan REMOTE_ADDRdan jangan repot-repot melayani mereka yang berada di belakang proxy.

Corey Ballou
sumber
2
Untuk pertanyaan whatismyip.com, saya pikir mereka melakukan sesuatu seperti skrip ini, apakah Anda menjalankannya secara lokal? Jika Anda adalah alasan mengapa Anda memiliki IP internal, tidak ada yang dikirim melalui antarmuka publik dalam kasus itu sehingga tidak ada informasi untuk mendapatkan php
Matt
2
Pastikan Anda mengingat hal ini ketika menerapkan ini: stackoverflow.com/questions/1672827/…
Kevin Peno
19
Ingat bahwa semua header HTTP ini sangat mudah dimodifikasi: dengan solusi Anda, saya hanya perlu mengonfigurasi browser saya untuk mengirim header X-Forwarded-For dengan IP acak dan skrip Anda akan dengan senang hati mengembalikan alamat palsu. Jadi tergantung pada apa yang Anda coba lakukan, solusi ini bisa kurang dapat diandalkan daripada hanya menggunakan REMOTE_ADDR.
gnomnain
14
OMFG, "ip tidak bisa diandalkan"! Pertama kali saya melihat omong kosong di sini di SO. Satu-satunya alamat IP yang dapat diandalkan adalah REMOTE_ADDR
Your Common Sense
3
-1 ini rentan terhadap spoofing. Yang Anda lakukan hanyalah menanyakan kepada pengguna apa alamat IP-nya.
benteng

Jawaban:

269

Berikut ini cara yang lebih singkat dan lebih bersih untuk mendapatkan alamat IP:

function get_ip_address(){
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
        if (array_key_exists($key, $_SERVER) === true){
            foreach (explode(',', $_SERVER[$key]) as $ip){
                $ip = trim($ip); // just to be safe

                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
                    return $ip;
                }
            }
        }
    }
}

Saya harap ini membantu!


Kode Anda tampaknya sudah cukup lengkap, saya tidak dapat melihat kemungkinan bug di dalamnya (selain dari peringatan IP yang biasa), saya akan mengubah validate_ip()fungsi untuk mengandalkan ekstensi filter:

public function validate_ip($ip)
{
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false)
    {
        return false;
    }

    self::$ip = sprintf('%u', ip2long($ip)); // you seem to want this

    return true;
}

HTTP_X_FORWARDED_FORCuplikan Anda juga dapat disederhanakan dari ini:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    // check if multiple ips exist in var
    if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false)
    {
        $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

        foreach ($iplist as $ip)
        {
            if ($this->validate_ip($ip))
                return $ip;
        }
    }

    else
    {
        if ($this->validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
}

Untuk ini:

// check for IPs passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

    foreach ($iplist as $ip)
    {
        if ($this->validate_ip($ip))
            return $ip;
    }
}

Anda mungkin juga ingin memvalidasi alamat IPv6.

Alix Axel
sumber
4
Saya sangat menghargai filter_varperbaikan karena menghapus sekelompok cek int unsigned hackish pada alamat IP. Saya juga menyukai kenyataan itu memberi saya opsi untuk memvalidasi alamat IPv6 juga. The HTTP_X_FORWARDED_FORoptimasi juga dihargai. Dalam beberapa menit saya akan memperbarui kode.
Corey Ballou
33
-1 ini rentan terhadap spoofing semua yang Anda lakukan adalah bertanya kepada pengguna apa alamat ip-nya.
benteng
7
@Rook: Ya, saya tahu. OP sadar akan hal itu, dan saya juga menyebutkannya dalam jawaban saya. Tapi terima kasih atas komentarnya.
Alix Axel
1
FYI: Saya harus menghapus FILTER_FLAG_IPV6 agar kode Alix Axel berfungsi.
darkAsPitch
2
@ rubenrp81 TCP socket handler adalah satu-satunya sumber kanonik, yang lainnya dikendalikan oleh penyerang. Kode di atas adalah impian penyerang.
benteng
12

Namun demikian, mendapatkan alamat IP asli pengguna akan menjadi tidak dapat diandalkan. Yang perlu mereka lakukan adalah menggunakan server proxy anonim (server yang tidak menghormati header untuk http_x_forwarded_for, http_forwarded, dll) dan yang Anda dapatkan hanyalah alamat IP server proxy mereka.

Anda kemudian dapat melihat apakah ada daftar alamat IP server proxy yang anonim, tetapi tidak ada cara untuk memastikan bahwa 100% akurat juga dan yang paling banyak dilakukan adalah memberi tahu Anda bahwa itu adalah server proxy. Dan jika seseorang pandai, mereka bisa menipu header untuk HTTP ke depan.

Katakanlah saya tidak suka perguruan tinggi setempat. Saya mencari tahu alamat IP apa yang mereka daftarkan, dan mendapatkan alamat IP mereka dilarang di situs Anda dengan melakukan hal-hal buruk, karena saya tahu Anda menghormati HTTP ke depan. Daftar ini tidak ada habisnya.

Lalu ada, seperti yang Anda duga, alamat IP internal seperti jaringan perguruan tinggi yang saya kenal sebelumnya. Banyak yang menggunakan format 10.xxx. Jadi yang Anda tahu adalah bahwa itu diteruskan untuk jaringan bersama.

Maka saya tidak akan mulai banyak ke dalamnya, tetapi alamat IP dinamis adalah cara broadband lagi. Begitu. Bahkan jika Anda mendapatkan alamat IP pengguna, harapkan itu berubah dalam 2 - 3 bulan, paling lama.

Peter Mortensen
sumber
Terima kasih atas masukannya. Saat ini saya menggunakan alamat IP pengguna untuk membantu otentikasi sesi dengan menggunakan IP C kelas mereka sebagai faktor pembatas untuk membatasi pembajakan sesi tetapi memungkinkan IP dinamis sesuai alasannya. IP palsu dan server proxy anonim adalah sesuatu yang harus saya hadapi untuk sekelompok individu tertentu.
Corey Ballou
@cballou - Tentunya untuk tujuan ini REMOTE_ADDR adalah yang benar untuk digunakan. Setiap pendekatan yang mengandalkan header HTTP rentan terhadap spoofing header. Berapa lama satu sesi? IP dinamis tidak berubah dengan cepat.
MZB
Mereka melakukannya, terutama jika saya menginginkannya (ubah alamat mac yang didukung banyak driver). Hanya REMOTE_ADDR saja sudah cukup untuk mendapatkan server apa yang terakhir kali digunakan. Jadi dalam situasi proxy Anda mendapatkan IP proxy.
8

Kita gunakan:

/**
 * Get the customer's IP address.
 *
 * @return string
 */
public function getIpAddress() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    } else if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim($ips[count($ips) - 1]);
    } else {
        return $_SERVER['REMOTE_ADDR'];
    }
}

Ledakan pada HTTP_X_FORWARDED_FOR adalah karena masalah aneh kami telah mendeteksi alamat IP ketika Squid digunakan.

Gabrielk
sumber
Ups, saya baru sadar Anda melakukan hal yang sama pada dasarnya dengan meledak, dan sebagainya. Ditambah sedikit tambahan. Jadi saya ragu jawaban saya banyak membantu. :)
gabrielk
Ini mengembalikan alamat localhost
Scarl
3

Jawaban saya pada dasarnya hanya versi @ AlixAxel yang dipoles, sepenuhnya divalidasi, dan dikemas penuh:

<?php

/* Get the 'best known' client IP. */

if (!function_exists('getClientIP'))
    {
        function getClientIP()
            {
                if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) 
                    {
                        $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
                    };

                foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key)
                    {
                        if (array_key_exists($key, $_SERVER)) 
                            {
                                foreach (explode(',', $_SERVER[$key]) as $ip)
                                    {
                                        $ip = trim($ip);

                                        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
                                            {
                                                return $ip;
                                            };
                                    };
                            };
                    };

                return false;
            };
    };

$best_known_ip = getClientIP();

if(!empty($best_known_ip))
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip;
    }
else
    {
        $ip = $clients_ip = $client_ip = $client_IP = $best_known_ip = '';
    };

?>

Perubahan:

  • Ini menyederhanakan nama fungsi (dengan gaya format 'camelCase').

  • Ini termasuk pemeriksaan untuk memastikan fungsi tersebut belum dideklarasikan di bagian lain dari kode Anda.

  • Ini memperhitungkan kompatibilitas akun 'CloudFlare'.

  • Ini menginisialisasi beberapa nama variabel "terkait IP" ke nilai yang dikembalikan, dari fungsi 'getClientIP'.

  • Ini memastikan bahwa jika fungsi tidak mengembalikan alamat IP yang valid, semua variabel diatur ke string kosong, bukan null.

  • Hanya (45) baris kode.

James Anderson Jr.
sumber
2

Pertanyaan terbesar adalah untuk tujuan apa?

Kode Anda hampir selengkap mungkin - tetapi saya melihat bahwa jika Anda melihat apa yang tampak seperti header yang ditambahkan proxy, Anda menggunakan BUKAN dari CLIENT_IP, namun jika Anda ingin informasi ini untuk keperluan audit maka diperingatkan - sangat mudah untuk palsu.

Tentu saja Anda tidak boleh menggunakan alamat IP untuk segala jenis otentikasi - bahkan ini dapat dipalsukan.

Anda bisa mendapatkan pengukuran yang lebih baik dari alamat ip klien dengan mendorong keluar flash atau applet java yang menghubungkan kembali ke server melalui port non-http (yang karenanya akan mengungkapkan proxy transparan atau kasus di mana header yang disuntikkan proxy salah - tetapi ingatlah bahwa, di mana klien HANYA dapat terhubung melalui proxy web atau port keluar diblokir, tidak akan ada koneksi dari applet.

C.

symcbean
sumber
Mempertimbangkan saya sedang mencari solusi PHP saja, apakah Anda menyarankan saya tambahkan $_SERVER['CLIENT_IP']sebagai pernyataan if if yang kedua?
Corey Ballou
Tidak - hanya itu jika Anda ingin menempatkan signifikansi pada data yang dikembalikan, maka itu akan menjadi ide yang baik untuk mempertahankan alamat titik akhir jaringan (klien IP) serta apa pun yang menunjukkan nilai yang berbeda dalam header tambah proxy (misalnya Anda dapat lihat banyak alamat 192.168.1.x tetapi berasal dari ips klien yang berbeda) C.
symcbean
1

Saya menyadari ada jawaban yang jauh lebih baik dan lebih ringkas di atas, dan ini bukan fungsi atau skrip yang paling anggun di sekitar. Dalam kasus kami, kami perlu menampilkan x_forwarded_for yang dapat spoofable dan remote_addr yang lebih andal dalam peralihan simplistic per-say. Itu diperlukan untuk memungkinkan kosong untuk menyuntikkan ke fungsi lain jika-tidak ada atau jika-tunggal (daripada hanya mengembalikan fungsi yang sudah diformat sebelumnya). Itu membutuhkan var "on atau off" dengan label khusus per-switch untuk pengaturan platform. Itu juga membutuhkan cara agar $ ip menjadi dinamis tergantung pada permintaan sehingga akan mengambil bentuk forwarded_for.

Saya juga tidak melihat siapa pun alamat isset () vs! Empty () - kemungkinan untuk memasukkan apa pun untuk x_forwarded_for namun masih memicu isset () kebenaran yang menghasilkan blank var, cara untuk berkeliling adalah menggunakan && dan menggabungkan keduanya sebagai syarat. Ingatlah bahwa Anda dapat memalsukan kata-kata seperti "PWNED" sebagai x_forwarded_for jadi pastikan Anda mensterilkan ke sintaks ip nyata jika keluaran Anda di suatu tempat dilindungi atau menjadi DB.

Juga, Anda dapat menguji menggunakan google translate jika Anda memerlukan multi-proxy untuk melihat array di x_forwarder_for. Jika Anda ingin spoof header untuk diuji, lihat ini Spoof Header Klien Chrome ekstensi . Ini akan default ke standar hanya remote_addr sementara di belakang proxy anon.

Saya tidak tahu apa pun di mana remote_addr bisa kosong, tetapi ada sebagai cadangan untuk berjaga-jaga.

// proxybuster - attempts to un-hide originating IP if [reverse]proxy provides methods to do so
  $enableProxyBust = true;

if (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR'])) && (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) && (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
    $ip = end(array_values(array_filter(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']))));
    $ipProxy = $_SERVER['REMOTE_ADDR'];
    $ipProxy_label = ' behind proxy ';
} elseif (($enableProxyBust == true) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = ' no proxy ';
} elseif (($enableProxyBust == false) && (isset($_SERVER['REMOTE_ADDR']))) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $ipProxy = '';
    $ipProxy_label = '';
} else {
    $ip = '';
    $ipProxy = '';
    $ipProxy_label = '';
}

Untuk membuat ini dinamis untuk digunakan dalam fungsi (s) atau permintaan / gema / tampilan di bawah ini, katakan untuk log gen atau pelaporan kesalahan, gunakan global atau hanya gema di mana pun Anda inginkan tanpa membuat satu ton kondisi lain atau static-schema-output fungsi.

function fooNow() {
    global $ip, $ipProxy, $ipProxy_label;
    // begin this actions such as log, error, query, or report
}

Terima kasih atas semua pemikiran hebat Anda. Tolong beri tahu saya jika ini bisa lebih baik, masih agak baru untuk header ini :)

dhaupin
sumber
1

Saya datang dengan fungsi ini yang tidak hanya mengembalikan alamat IP tetapi sebuah array dengan informasi IP.

// Example usage:
$info = ip_info();
if ( $info->proxy ) {
    echo 'Your IP is ' . $info->ip;
} else {
    echo 'Your IP is ' . $info->ip . ' and your proxy is ' . $info->proxy_ip;
}

Inilah fungsinya:

/**
 * Retrieves the best guess of the client's actual IP address.
 * Takes into account numerous HTTP proxy headers due to variations
 * in how different ISPs handle IP addresses in headers between hops.
 *
 * @since 1.1.3
 *
 * @return object {
 *         IP Address details
 *
 *         string $ip The users IP address (might be spoofed, if $proxy is true)
 *         bool $proxy True, if a proxy was detected
 *         string $proxy_id The proxy-server IP address
 * }
 */
function ip_info() {
    $result = (object) array(
        'ip' => $_SERVER['REMOTE_ADDR'],
        'proxy' => false,
        'proxy_ip' => '',
    );

    /*
     * This code tries to bypass a proxy and get the actual IP address of
     * the visitor behind the proxy.
     * Warning: These values might be spoofed!
     */
    $ip_fields = array(
        'HTTP_CLIENT_IP',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_FORWARDED',
        'HTTP_X_CLUSTER_CLIENT_IP',
        'HTTP_FORWARDED_FOR',
        'HTTP_FORWARDED',
        'REMOTE_ADDR',
    );
    foreach ( $ip_fields as $key ) {
        if ( array_key_exists( $key, $_SERVER ) === true ) {
            foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
                $ip = trim( $ip );

                if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
                    $forwarded = $ip;
                    break 2;
                }
            }
        }
    }

    // If we found a different IP address then REMOTE_ADDR then it's a proxy!
    if ( $forwarded != $result->ip ) {
        $result->proxy = true;
        $result->proxy_ip = $result->ip;
        $result->ip = $forwarded;
    }

    return $result;
}
Philipp
sumber
1

Seperti yang dikatakan seseorang sebelumnya, kuncinya di sini adalah untuk alasan apa Anda ingin menyimpan ips pengguna.

Saya akan memberikan contoh dari sistem registrasi tempat saya bekerja dan tentu saja solusinya hanya untuk berkontribusi pada diskusi lama ini yang sering muncul dalam pencarian saya.

Banyak perpustakaan pendaftaran php menggunakan ip untuk menutup / mengunci upaya yang gagal berdasarkan ip pengguna. Pertimbangkan tabel ini:

-- mysql
DROP TABLE IF EXISTS `attempts`;
CREATE TABLE `attempts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(39) NOT NULL, /*<<=====*/
  `expiredate` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 -- sqlite
...

Kemudian, ketika pengguna mencoba melakukan login atau apapun yang berhubungan dengan servis seperti pengaturan ulang kata sandi, sebuah fungsi dipanggil di awal:

public function isBlocked() {
      /*
       * used one of the above methods to capture user's ip!!!
       */
      $ip = $this->ip;
      // delete attempts from this ip with 'expiredate' in the past
      $this->deleteAttempts($ip, false);
      $query = $this->dbh->prepare("SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ?");
      $query->execute(array($ip));
      $attempts = $query->fetchColumn();
      if ($attempts < intval($this->token->get('attempts_before_verify'))) {
         return "allow";
      }
      if ($attempts < intval($this->token->get('attempts_before_ban'))) {
         return "captcha";
      }
      return "block";
   }

Katakanlah, misalnya, $this->token->get('attempts_before_ban') === 10dan 2 pengguna datang untuk ips yang sama seperti halnya dalam kode sebelumnya di mana tajuk dapat dipalsukan , lalu setelah 5 upaya masing-masing keduanya dilarang ! Bahkan yang terburuk, jika semua berasal dari proxy yang sama maka hanya 10 pengguna pertama yang akan dicatat dan sisanya akan diblokir!

Yang penting di sini adalah bahwa kita memerlukan indeks unik di atas meja attemptsdan kita bisa mendapatkannya dari kombinasi seperti:

 `ip` varchar(39) NOT NULL,
 `jwt_load varchar(100) NOT NULL

di mana jwt_loadberasal dari cookie http yang mengikuti teknologi token web json di mana kami menyimpan hanya muatan terenkripsi yang harus berisi nilai arbitrer / unik untuk setiap pengguna. Tentu saja permintaan harus diubah ke: "SELECT count(*) FROM {$this->token->get('table_attempts')} WHERE ip = ? AND jwt_load = ?"dan kelas juga harus memulai a private $jwt.

centurian
sumber
0

Saya bertanya-tanya apakah mungkin Anda harus mengulangi HTTP_X_FORWARDED_FOR yang meledak dalam urutan terbalik, karena pengalaman saya adalah bahwa alamat IP pengguna berakhir di akhir daftar yang dipisahkan koma, jadi mulai dari awal header, Anda lebih mungkin untuk mendapatkan alamat ip dari salah satu proxy yang dikembalikan, yang berpotensi masih memungkinkan pembajakan sesi karena banyak pengguna dapat datang melalui proxy itu.

Chris Withers
sumber
1
Setelah membaca halaman wikipedia pada HTTP_X_FORWARDED_FOR: en.wikipedia.org/wiki/X-Forwarded-For ... Saya melihat urutan yang disarankan, memang, dari kiri ke kanan seperti kode Anda. Namun, dari log kami, saya bisa melihat ada banyak kasus di mana ini tidak dihormati oleh proxy di alam dan alamat ip yang ingin Anda periksa bisa berada di kedua ujung daftar.
Chris Withers
1
Atau di tengah, seperti yang akan terjadi jika beberapa proksi menghormati urutan kiri-ke-kanan dan yang lain tidak.
Brilliand
0

Terima kasih untuk ini, sangat berguna.

Akan sangat membantu jika kode itu benar secara sintaksis. Karena ada {terlalu banyak di sekitar baris 20. Yang saya khawatir berarti tidak ada yang benar-benar mencoba ini.

Saya mungkin gila, tetapi setelah mencobanya pada beberapa alamat yang valid dan tidak valid, satu-satunya versi validate_ip () yang berfungsi adalah ini:

    public function validate_ip($ip)
    {
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) === false)
            return false;
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)
            return false;

        return true;
    }
Mark Boon
sumber
0

Ini adalah versi yang dimodifikasi jika Anda menggunakan Layanan lapisan caching CloudFlare

function getIP()
{
    $fields = array('HTTP_X_FORWARDED_FOR',
                    'REMOTE_ADDR',
                    'HTTP_CF_CONNECTING_IP',
                    'HTTP_X_CLUSTER_CLIENT_IP');

    foreach($fields as $f)
    {
        $tries = $_SERVER[$f];
        if (empty($tries))
            continue;
        $tries = explode(',',$tries);
        foreach($tries as $try)
        {
            $r = filter_var($try,
                            FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 |
                            FILTER_FLAG_NO_PRIV_RANGE |
                            FILTER_FLAG_NO_RES_RANGE);

            if ($r !== false)
            {
                return $try;
            }
        }
    }
    return false;
}
jmserra
sumber
0

Hanya versi VB.NET dari jawabannya:

Private Function GetRequestIpAddress() As IPAddress
    Dim serverVariables = HttpContext.Current.Request.ServerVariables
    Dim headersKeysToCheck = {"HTTP_CLIENT_IP", _
                              "HTTP_X_FORWARDED_FOR", _
                              "HTTP_X_FORWARDED", _
                              "HTTP_X_CLUSTER_CLIENT_IP", _
                              "HTTP_FORWARDED_FOR", _
                              "HTTP_FORWARDED", _
                              "REMOTE_ADDR"}
    For Each thisHeaderKey In headersKeysToCheck
        Dim thisValue = serverVariables.Item(thisHeaderKey)
        If thisValue IsNot Nothing Then
            Dim validAddress As IPAddress = Nothing
            If IPAddress.TryParse(thisValue, validAddress) Then
                Return validAddress
            End If
        End If
    Next
    Return Nothing
End Function
Abacus
sumber
3
Ada tag "PHP" dalam pertanyaan
luchaninov
0

Hanya cara bersih lainnya:

  function validateIp($var_ip){
    $ip = trim($var_ip);

    return (!empty($ip) &&
            $ip != '::1' &&
            $ip != '127.0.0.1' &&
            filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
            ? $ip : false;
  }

  function getClientIp() {
    $ip = @$this->validateIp($_SERVER['HTTP_CLIENT_IP']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_X_FORWARDED']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED_FOR']) ?:
          @$this->validateIp($_SERVER['HTTP_FORWARDED']) ?:
          @$this->validateIp($_SERVER['REMOTE_ADDR']) ?:
          'LOCAL OR UNKNOWN ACCESS';

    return $ip;
  }
Liko
sumber
0

Dari kelas Permintaan Symfony https://github.com/symfony/symfony/blob/1bd125ec4a01220878b3dbc3ec3156b073996af9/src/Symfony/Component/HttpFoundation/Request.php

const HEADER_FORWARDED = 'forwarded';
const HEADER_CLIENT_IP = 'client_ip';
const HEADER_CLIENT_HOST = 'client_host';
const HEADER_CLIENT_PROTO = 'client_proto';
const HEADER_CLIENT_PORT = 'client_port';

/**
 * Names for headers that can be trusted when
 * using trusted proxies.
 *
 * The FORWARDED header is the standard as of rfc7239.
 *
 * The other headers are non-standard, but widely used
 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
 */
protected static $trustedHeaders = array(
    self::HEADER_FORWARDED => 'FORWARDED',
    self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
    self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
    self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
    self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
);

/**
 * Returns the client IP addresses.
 *
 * In the returned array the most trusted IP address is first, and the
 * least trusted one last. The "real" client IP address is the last one,
 * but this is also the least trusted one. Trusted proxies are stripped.
 *
 * Use this method carefully; you should use getClientIp() instead.
 *
 * @return array The client IP addresses
 *
 * @see getClientIp()
 */
public function getClientIps()
{
    $clientIps = array();
    $ip = $this->server->get('REMOTE_ADDR');
    if (!$this->isFromTrustedProxy()) {
        return array($ip);
    }
    if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
        $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
        preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
        $clientIps = $matches[3];
    } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
        $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
    }
    $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
    $firstTrustedIp = null;
    foreach ($clientIps as $key => $clientIp) {
        // Remove port (unfortunately, it does happen)
        if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
            $clientIps[$key] = $clientIp = $match[1];
        }
        if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
            unset($clientIps[$key]);
        }
        if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
            unset($clientIps[$key]);
            // Fallback to this when the client IP falls into the range of trusted proxies
            if (null ===  $firstTrustedIp) {
                $firstTrustedIp = $clientIp;
            }
        }
    }
    // Now the IP chain contains only untrusted proxies and the client IP
    return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
}
luchaninov
sumber
Properti tidak terdefinisi: $ server
C47
0

Saya terkejut tidak ada yang menyebutkan filter_input, jadi inilah jawaban Alix Axel yang diringkas menjadi satu baris:

function get_ip_address(&$keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'])
{
    return empty($keys) || ($ip = filter_input(INPUT_SERVER, array_pop($keys), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))? $ip : get_ip_address($keys);
}
matt[
sumber
-1

Anda cukup banyak menjawab pertanyaan Anda sendiri! :)

function getRealIpAddr() {
    if(!empty($_SERVER['HTTP_CLIENT_IP']))   //Check IP address from shared Internet
    {
        $IPaddress = $_SERVER['HTTP_CLIENT_IP'];
    }
    elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))   //To check IP address is passed from the proxy
    {
        $IPaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
    else
    {
        $IPaddress = $_SERVER['REMOTE_ADDR'];
    }
    return $IPaddress;
}

Sumber

Alex Weber
sumber
-6
/**
 * Sanitizes IPv4 address according to Ilia Alshanetsky's book
 * "php|architect?s Guide to PHP Security", chapter 2, page 67.
 *
 * @param string $ip An IPv4 address
 */
public static function sanitizeIpAddress($ip = '')
{
if ($ip == '')
    {
    $rtnStr = '0.0.0.0';
    }
else
    {
    $rtnStr = long2ip(ip2long($ip));
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized HTTP_X_FORWARDED_FOR server variable.
 *
 */
public static function getXForwardedFor()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
elseif (isset($HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'];
    }
elseif (getenv('HTTP_X_FORWARDED_FOR'))
    {
    $rtnStr = getenv('HTTP_X_FORWARDED_FOR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized REMOTE_ADDR server variable.
 *
 */
public static function getRemoteAddr()
{
if (isset($_SERVER['REMOTE_ADDR']))
    {
    $rtnStr = $_SERVER['REMOTE_ADDR'];
    }
elseif (isset($HTTP_SERVER_VARS['REMOTE_ADDR']))
    {
    $rtnStr = $HTTP_SERVER_VARS['REMOTE_ADDR'];
    }
elseif (getenv('REMOTE_ADDR'))
    {
    $rtnStr = getenv('REMOTE_ADDR');
    }
else
    {
    $rtnStr = '';
    }

// Sanitize IPv4 address (Ilia Alshanetsky):
if ($rtnStr != '')
    {
    $rtnStr = explode(', ', $rtnStr);
    $rtnStr = self::sanitizeIpAddress($rtnStr[0]);
    }

return $rtnStr;
}

//---------------------------------------------------

/**
 * Returns the sanitized remote user and proxy IP addresses.
 *
 */
public static function getIpAndProxy()
{
$xForwarded = self::getXForwardedFor();
$remoteAddr = self::getRemoteAddr();

if ($xForwarded != '')
    {
    $ip    = $xForwarded;
    $proxy = $remoteAddr;
    }
else
    {
    $ip    = $remoteAddr;
    $proxy = '';
    }

return array($ip, $proxy);
}
Meketrefe
sumber