PHP[快問快答系列]

豎橫山發表於2023-02-24

怎麼解決防盜鏈?

  • Referrer白名單:設定Nginx只允許來自指定域名的請求,其他來源的請求將被拒絕。

    location ~* \.(jpg|jpeg|png|gif)$ {
      valid_referers none blocked example.com;
      if ($invalid_referer) {
          return 403;
      }
    }
  • Referer黑名單:設定Nginx只允許來自指定域名以外的請求,其他來源的請求將被拒絕。

    location ~* \.(jpg|jpeg|png|gif)$ {
      valid_referers none blocked;
      if ($invalid_referer) {
          return 403;
      }
      if ($http_referer ~* (badsite1.com|badsite2.com)) {
          return 403;
      }
    }
  • 設定圖片的時效性:可以透過Nginx的add_header指令設定圖片的過期時間,從而防止其他網站永久使用你的圖片。

    location ~* \.(jpg|jpeg|png|gif)$ {
      expires 30d; //30天后快取過期
      add_header Cache-Control "public";
    }
  • Rewrite防盜鏈:使用Nginx的rewrite模組,將請求的URL中的來源進行判斷,如果來源不正確,就跳轉到錯誤頁面。

    location /images/ {
      if ($http_referer !~ "^https?://(www\.)?example\.com") {
          rewrite ^/images/(.*)$ /error.png last;
      }
    }

PHP的垃圾回收機制?

PHP的垃圾回收機制主要是基於引用計數(reference counting)演演算法實現的。每當有一個變數引用一個物件時,物件的引用計數就會加一。每當一個變數不再引用該物件時,物件的引用計數就會減一。當物件的引用計數降為零時,物件就會被標記為垃圾,等待垃圾收集器的回收。
PHP 7.3之後,引入了一種新的垃圾收集器——“Zend GC”,它是一種全域性垃圾回收機制,可以更快地檢測和回收垃圾。

如何獲取客戶端真實IP?

if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
    $client_ip = $_SERVER['HTTP_CLIENT_IP'];
} else {
    $client_ip = $_SERVER['REMOTE_ADDR'];
}

需要注意的是,$_SERVER['HTTP_X_FORWARDED_FOR']$_SERVER['HTTP_CLIENT_IP'] 的值可能是一個逗號分隔的 IP 地址列表,其中第一個 IP 地址是客戶端的真實 IP 地址。

如何防範CSRF攻擊

  • 使用 CSRF Token

    #client
    <form action="submit.php" method="post">
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
    <!-- 其他表單元素 -->
    <button type="submit">提交</button>
    </form>
    #server
    session_start();
    if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
      die('CSRF 驗證失敗');
    }
  • 檢查 Referer

    if (strpos($_SERVER['HTTP_REFERER'], 'example.com') === false) {
      die('CSRF 驗證失敗');
    }
  • 新增驗證碼

PHP如何防範XSS攻擊

  • 對使用者輸入進行過濾和轉義

    $raw_input = '<script>alert("惡意程式碼");</script>';
    $filtered_input = htmlspecialchars($raw_input, ENT_QUOTES, 'UTF-8');
    echo $filtered_input;
    //&lt;script&gt;alert(&quot;惡意程式碼&quot;);&lt;/script&gt;

    htmlspecialchars() 函式只能防止 HTML 注入,對於其他型別的注入攻擊(例如 JavaScript、CSS、SQL 注入等),需要使用相應的轉義函式或過濾器。

  • 使用 Content Security Policy

    header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'");
  • 對輸出內容進行過濾和轉義

    在輸出內容之前,使用 htmlspecialchars() 函式對內容進行 HTML 轉義。

如果兩個檔案大小都超過了 1G,而 PHP 最大允許的記憶體只有 255M,那麼不能將整個檔案讀入記憶體中進行比較。一種可行的做法是對兩個檔案進行分塊處理,將每個塊讀入記憶體中進行比較,以此找到相同的行。

對兩個檔案進行分塊處理,將每個塊讀入記憶體中進行比較,以此找到相同的行

<?php

// 定義塊大小
define('BLOCK_SIZE', 1024 * 1024 * 256); // 256M

// 開啟檔案
$fp1 = fopen('file1.txt', 'r');
$fp2 = fopen('file2.txt', 'r');
$fp3 = fopen('result.txt', 'w');

// 初始化變數
$buffer1 = '';
$buffer2 = '';
$pos1 = 0;
$pos2 = 0;

// 讀取檔案塊並比較
while (!feof($fp1) || !feof($fp2)) {
    // 讀取檔案塊
    $buffer1 = fread($fp1, BLOCK_SIZE);
    $buffer2 = fread($fp2, BLOCK_SIZE);

    // 比較檔案塊
    $lines1 = explode("\n", $buffer1);
    $lines2 = explode("\n", $buffer2);
    $count1 = count($lines1);
    $count2 = count($lines2);

    // 如果是塊的第一行,則修正當前位置
    if ($pos1 == 0) {
        $pos1 += strlen($lines1[0]) + 1;
    }
    if ($pos2 == 0) {
        $pos2 += strlen($lines2[0]) + 1;
    }

    // 比較行資料
    for ($i = 0; $i < min($count1, $count2); $i++) {
        if ($lines1[$i] == $lines2[$i]) {
            // 如果行資料相同,則將該行寫入新檔案
            fwrite($fp3, $lines1[$i] . "\n");
        }
        // 更新位置資訊
        $pos1 += strlen($lines1[$i]) + 1;
        $pos2 += strlen($lines2[$i]) + 1;
    }

    // 如果檔案1比檔案2多出一行,需要將最後一行重新拼接到下一個塊的開頭
    if ($count1 > $count2) {
        $buffer1 = $lines1[$count2] . "\n";
        $pos1 -= strlen($buffer1);
    }
    // 如果檔案2比檔案1多出一行,需要將最後一行重新拼接到下一個塊的開頭
    if ($count2 > $count1) {
        $buffer2 = $lines2[$count1] . "\n";
        $pos2 -= strlen($buffer2);
    }

    // 定位檔案指標
    fseek($fp1, $pos1);
    fseek($fp2, $pos2);
}

// 關閉檔案控制程式碼
fclose($fp1);
fclose($fp2);
fclose($fp3);

10g 檔案,用 php 檢視它的行數

處理大檔案時,使用 PHP 的記憶體佔用可能會非常高,甚至可能導致指令碼執行時間過長或崩潰。為了避免這種情況,可以使用 PHP 的 SplFileObject 類,它支援迭代器模式,可以逐行讀取檔案,不會一次性將整個檔案讀入記憶體。

<?php
// 開啟檔案
$file = new SplFileObject('large_file.txt');

// 初始化行數為 0
$lineCount = 0;

// 迭代檔案中的每一行
foreach ($file as $line) {
    // 每讀取一行,行數加一
    $lineCount++;
}

// 輸出行數
echo "The file has $lineCount lines.\n";
?>

php7 效能為什麼提升這麼高?

PHP7 的效能提升主要來自於引擎最佳化和程式碼最佳化。引入 JIT 編譯器和改進垃圾回收機制,使得 PHP7 可以更快地執行程式碼。在標準庫和函式方面進行最佳化,也有助於提高 PHP7 的執行效率。

有一個 1G 大小的一個檔案,裡面每一行是一個詞,詞的大小不超過 16 個位元組,記憶體限制大小是 1M。返回頻數最高的 100 個詞

可以使用 PHP 的 SplFileObject 類進行逐行讀取,並將詞頻統計到一個陣列中。

<?php
$file = new SplFileObject('large_file.txt'); // 開啟檔案

$wordCount = array(); // 初始化詞頻統計陣列

while (!$file->eof()) { // 逐行讀取檔案內容
    $line = $file->fgets(); // 讀取一行內容
    $words = explode(' ', trim($line)); // 將一行內容按空格分割成詞

    foreach ($words as $word) {
        if (isset($wordCount[$word])) { // 如果詞已經存在,增加頻率
            $wordCount[$word]++;
        } else { // 否則,新增新的詞並初始化頻率為 1
            $wordCount[$word] = 1;
        }
    }
}

arsort($wordCount); // 對詞頻統計陣列按值(頻率)進行降序排序

$i = 0;
foreach ($wordCount as $word => $count) { // 輸出頻率最高的 100 個詞
    echo "$word: $count\n";
    $i++;
    if ($i >= 100) break;
}
?>

OOP物件導向是什麼?

怎麼防止重複下單?

  • 前端校驗:在使用者點選“提交訂單”按鈕後,可以禁用該按鈕,避免使用者多次點選。
  • 後端校驗:hash加密 使用者ID+商品ID+數量 作為依據,存在redis,設定過期時間,下單之前查詢是否有重複的key
  • token:前端在提交訂單時,需要先向後端發起請求,獲取一個唯一的 token 或者 session ID,然後將該 token 或者 session ID 一起提交給後端。後端在處理訂單請求時,會先根據該 token 或者 session ID 判斷是否是重複提交的訂單。如果是網頁端還可以避免crsf攻擊

如果微信支付回撥出現故障,怎麼解決?

本作品採用《CC 協議》,轉載必須註明作者和本文連結
遇強則強,太強另說

相關文章