切換語言為:簡體

加固Nginx的十二種技巧,讓Nginx更安全!

  • 爱糖宝
  • 2024-10-28
  • 2034
  • 0
  • 0

一 使用最新版

建議使用最新版的 Nginx,對於已經部署的 Nginx,要及時更新到最新版本,以確保所有已知的安全漏洞都已修補。

Nginx 下載地址:nginx.org/en/download…

二 限制連線數量

Nginx 可以透過 limit_conn_zone 和 limit_conn 兩個元件來對客戶端訪問目錄和檔案的訪問頻率和次數進行限制,兩個模組都能夠對客戶端訪問進行限制,具體如何使用要結合公司業務環境進行配置。

舉個簡單的例子:

http {
  limit_conn_zone $binary_remote_addr zone=ops:10m;

  # ...

  server {
    listen       80;
    server_name  www.javaboy.org;
    location / {
      limit_conn ops 1;  #這將指定一個地址只能同時存在一個連線。“one” 與上面的對應,也可以自定義命名
      limit_rate 300k;
  }

}


這裏涉及到三個配置項:

  • limit_zone: 是針對每個 IP 定義一個儲存 session 狀態的容器,這個示例中定義了一個 10m 的容器,假設每個 session 的大小是 32bytes,那麼可以處理 327680 個 session。

  • limit_conn ops 1:限制每個 IP 只能發起一個併發連線。

  • limit_rate 300k: 對每個連線限速 300k. 注意,這裏是對連線限速,而不是對 IP 限速。如果一個 IP 允許兩個併發連線,那麼這個 IP 就是限速 limit_rate × 2

三 限制請求頻率

Nginx 可以透過限制請求頻率來防止伺服器過載,最常見的場景就是登入請求,可以透過限制請求頻率防止賬號暴力破解。

Nginx 官方版本限制 IP 的連線和併發分別有兩個模組:

  • limit_req_zone:用來限制單位時間內的請求數,即速率限制,採用的漏桶演算法 "leaky bucket"。

  • limit_req_conn 用來限制同一時間連線數,即併發限制。

舉個栗子

http {
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;

    server {
        listen 80;

        location / {
            limit_req zone=mylimit burst=5 nodelay;;
            proxy_pass http://javaboy.org;
        }
    }
}


limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;

  • 第一個引數:$binary_remote_addr 表示透過 remote_addr 這個標識來做限制,“binary_” 的目的是縮寫記憶體佔用量,是限制同一客戶端 ip 地址。

  • 第二個引數:zone=mylimit:10m 表示生成一個大小為 10M,名字為 mylimit 的記憶體區域,用來儲存訪問的頻次資訊。

  • 第三個引數:rate=1r/s 表示允許相同標識的客戶端的訪問頻次,這裏限制的是每秒 1 次,還可以有比如 30r/m 的。

limit_req zone=mylimit burst=5 nodelay;

  • 第一個引數:zone=one 設定使用哪個配置區域來做限制,與上面 limit_req_zone 裡的 name 對應。

  • 第二個引數:burst=5,重點說明一下這個配置,burst 爆發的意思,這個配置的意思是設定一個大小為 5 的緩衝區,當有大量請求(爆發)過來時,超過了訪問頻次限制的請求可以先放到這個緩衝區內。

  • 第三個引數:nodelay,如果設定,超過訪問頻次而且緩衝區也滿了的時候就會直接返回 503,如果沒有設定,則所有請求會等待排隊。

四 防止目錄遍歷

在 Nginx 配置中設定 autoindex off 來防止目錄遍歷攻擊。

這個一般是如果你要做檔案伺服器,根據自己的實際需求,有需要的話這個功能可以開啟,否則將之關閉即可。

location / {
    autoindex off;
}


五 隱藏 Nginx 版本號

攻擊者如果能夠確定伺服器使用的 Nginx 版本,可能會利用這個資訊來尋找和利用已知的漏洞進行攻擊。因此,隱藏版本資訊可以提高伺服器的安全性,使攻擊者難以透過版本資訊推斷出伺服器可能存在的安全漏洞。

要隱藏 Nginx 版本號,有三個辦法,一般來說我們使用第一種方式就可以了。

修改配置檔案

在 Nginx 的配置檔案中,在 http 塊中新增以下配置:

server_tokens off;


這樣設定後,Nginx 將不會在錯誤頁面上顯示版本號。

配置完成之後,儲存配置檔案並重新載入 Nginx 以應用更改:

nginx -t   # 測試配置檔案是否正確 
nginx -s reload   # 重新載入Nginx配置


這種方法可以隱藏錯誤頁面上的版本資訊,但可能無法完全隱藏所有響應頭中的版本資訊 。

修改 Nginx 原始碼

如果想要從根源上修改 Nginx 版本資訊,需要重新編譯 Nginx,步驟如下:

  • 修改 src/core/nginx.h 檔案中的版本定義。

  • 修改 src/http/ngx_http_header_filter_module.c 檔案中的伺服器字串。

  • 修改 src/http/ngx_http_special_response.c 檔案中的錯誤頁面底部資訊。

修改完這些檔案後,需要重新編譯 Nginx。這樣編譯安裝後,Nginx 的版本資訊將被徹底修改 。

使用第三方模組

如果需要動態修改響應頭中的版本資訊,可以使用如 headers-more-nginx-module 模組。這個模組允許你動態地新增、修改或刪除 Nginx 的響應頭。透過這個模組,可以完全控制 Server 響應頭的內容 。

選擇哪種方法取決於你的具體需求和環境。

如果你只是想簡單地隱藏版本資訊,修改配置檔案可能是最簡單的方法。如果你需要更徹底地控制版本資訊,可能需要考慮修改原始碼並重新編譯 Nginx。

六 設定超時時間

設定 Nginx 的超時配置是非常重要的,因為它可以影響伺服器的效能和資源的有效利用。

比較常見的超時配置有四個:

  1. keepalive_timeout:這個指令設定了與客戶端的 keep-alive 連線超時時間。如果連線在指定時間內沒有數據傳輸,Nginx 將關閉該連線。預設值通常是 75 秒。這個設定對於頻繁訪問的站點尤其重要,因為它減少了連線建立和斷開的開銷。

  2. client_body_timeout:這個指令指定了客戶端與服務端建立連線後傳送 request body 的超時時間。如果客戶端在指定時間內沒有傳送任何內容,Nginx 返回 HTTP 408(Request Timed Out)。預設值通常是 60 秒。

  3. client_header_timeout:這個指令指定了客戶端向服務端傳送一個完整的 request header 的超時時間。如果在指定時間內沒有傳送一個完整的 request header,Nginx 返回 HTTP 408(Request Timed Out)。預設值通常是 60 秒。

  4. send_timeout:這個指令設定了服務端向客戶端傳輸資料的超時時間。如果在指定時間內客戶端沒有接收到任何資料,連線將被關閉。預設值通常是 60 秒。

針對這四個比較常見的超時配置,鬆哥這裏也給大家一個配置案例。

keepalive_timeout

這個指令控制了客戶端與伺服器之間的連線保持活動狀態的時間。這對於減少 TCP 連線的開銷非常有用,特別是在高流量的網站上。

http {
    keepalive_timeout 60s;

    server {
        listen 80;
        server_name javaboy.org;

        location / {
            proxy_pass http://javaboy.org;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
        }
    }
}


在這個配置中,keepalive_timeout 被設定為 60 秒,意味著如果 60 秒內沒有數據傳輸,連線將被關閉。

client_body_timeout

這個指令設定了客戶端傳送請求體到伺服器的超時時間。

http {
    client_body_timeout 10s;

    server {
        listen 80;
        server_name javaboy.org;

        location /upload {
            client_max_body_size 100M;
            client_body_timeout 30s;
        }
    }
}


在這個配置中,client_body_timeout 被設定為 10 秒,適用於上傳大檔案的場景,確保如果客戶端在 30 秒內沒有完成檔案上傳,請求將被終止。

client_header_timeout

這個指令控制了客戶端傳送完整的 HTTP 請求頭到伺服器的超時時間。

http {
    client_header_timeout 5s;

    server {
        listen 80;
        server_name javaboy.org;

        location / {
            proxy_pass http://javaboy.org;
        }
    }
}


在這個配置中,client_header_timeout 被設定為 5 秒,意味著如果客戶端在 5 秒內沒有傳送完整的 HTTP 請求頭,伺服器將終止連線。

send_timeout

這個指令設定了伺服器傳送響應到客戶端的超時時間。

http {
    send_timeout 10s;

    server {
        listen 80;
        server_name javaboy.org;

        location / {
            proxy_pass http://javaboy.org;
            proxy_read_timeout 10s;
        }
    }
}


在這個配置中,send_timeout 被設定為 10 秒,適用於後端服務響應慢的場景,確保如果後端服務在 10 秒內沒有傳送資料,客戶端將收到超時響應。

七 僅允許域名訪問

限制僅允許域名訪問可以防止未授權的 IP 直接訪問伺服器,減少未備案域名解析到伺服器 IP 導致的安全風險。

這個也有三種不同的配置方式,我們逐一來看。

使用兩個 server

在 Nginx 配置檔案中,你可以設定一個預設的 server 塊,它將捕獲所有不明確的域名請求,並返回 403 錯誤。然後,為特定的域名設定 server 塊。

server {
    listen 80 default_server;
    server_name _;
    return 403;
}

server {
    listen 80;
    server_name www.javaboy.org;
    location / {
        # 你的配置
    }
}


在這個配置中,第一個 server 塊會攔截所有不明確域名的請求,並返回 403 錯誤。第二個 server 塊則是為特定域名 www.javaboy.org 提供服務的配置。

這個配置有兩點需要注意:

  1. 如果沒有顯式宣告 default server 則第一個 server 會被隱式的設為 default server。

  2. server_name 中的 _;,並不是重點 __ 也可以, ___ 也可以。

使用 if 語句

還可以在特定的 server 塊中使用 if 語句來檢查 $host 變數,如果它不匹配你的域名,則返回 403 錯誤。

server {
    listen 80;
    server_name javaboy.org;
    location / {
        if ($host != 'www.javaboy.org') {
            return 403;
        }
        # 你的配置
    }
}


這種方法允許你在特定域名的 server 塊中直接控制訪問許可權,只有當 $host 變數與你的域名匹配時,纔會允許訪問。

直接禁止 IP

http {
    server {
        listen 80;
        server_name www.javaboy.org;
        ...
    }
    
    server {
        listen 80;
        server_name www.itboyhub.com;
        ...
    }
    
    # 直接指定 ip server_name
    server {
        listen 80;
        server_name 11.11.11.11;
        return 403; # 403 forbidden
    }
    
}


這樣配置後,只有透過指定的域名才能訪問網站,直接透過 IP 地址訪問將會受到限制。

八 限制 Nginx 請求方法

透過限制特定的 HTTP 請求方法,可以減少伺服器受到自動化攻擊的風險,並且可以防止某些型別的 Web 漏洞,如 SQL 注入或跨站指令碼(XSS)攻擊。

有兩種配置方式,鬆哥來和大家逐一說明。

只允許 GET 和 POST

serverlocation 塊中,使用 if 語句來檢查請求方法,並返回 403 錯誤碼以拒絕其他方法。

server {
    listen 80;
    server_name javaboy.org;

    location / {
        if ($request_method !~* (GET|POST)) {
            return 403;
        }
        # 其他配置...
    }
}


這種方法會拒絕所有非 GET 和 POST 的請求方法。

使用 map 模組

對於更復雜的限制邏輯,可以使用 Nginx 的 map 模組來動態設定請求方法的限制。

http {
    map $request_method $block_request {
        default 0;
        POST 1;
        PUT 1;
    }

    server {
        listen 80;
        server_name javaboy.org;

        location / {
            if ($block_request) {
                return 403;
            }
            # 其他配置...
        }
    }
}


在這個例子中,所有 POST 和 PUT 請求都會被拒絕。

九 錯誤頁面重定向

在 Nginx 中配置錯誤頁面重定向,除了安全因素之外,還有很多好處,比如:

  1. 提升使用者體驗:透過提供更友好的錯誤頁面,可以減少使用者在遇到錯誤時的困惑和挫敗感。

  2. 增強 SEO 效果:自定義錯誤頁面可以幫助搜索引擎更好地理解網站結構,避免因錯誤頁面導致的 SEO 問題。

  3. 維護品牌形象:錯誤頁面是網站的一部分,透過自定義錯誤頁面,可以保持品牌一致性,提升專業形象。

  4. 提供錯誤資訊:自定義錯誤頁面可以提供有用的錯誤資訊或解決方案,幫助使用者理解問題所在。

在 Nginx 配置檔案中,可以使用 error_page 指令來定義特定錯誤程式碼的重定向頁面。例如,將 404 錯誤重定向到自定義的 404 頁面:

server {
    listen 80;
    server_name javaboy.org;

    error_page 404 /404.html;
    location = /404.html {
        root /path/to/error/pages;
        internal;
    }
}


在這個配置中,當 Nginx 返回 404 錯誤時,它會顯示位於 /path/to/error/pages/404.html 的自定義錯誤頁面,而不是預設的錯誤頁面。internal 指令確保這個頁面只對 Nginx 內部請求可見,不會被外部直接訪問 。

當然,上面這個配置也可以同時列舉多個錯誤狀態碼:

error_page 500 502 503 504 /50x.html;
location = /50x.html {
    root /usr/share/nginx/html;
}


這個配置會將所有 500 系列錯誤重定向到 /50x.html,並顯示位於 /usr/share/nginx/html/50x.html 的自定義錯誤頁面 。

十 日誌保留半年

保留 Nginx 日誌半年的原因有很多,比如:

  1. 安全審計:日誌檔案可以用於安全審計,幫助分析和追蹤潛在的攻擊或異常行為。

  2. 故障排查:在系統出現故障時,日誌檔案是診斷問題的重要工具,可以幫助快速定位問題原因。

  3. 效能監控:透過分析日誌,可以瞭解網站的訪問情況和效能瓶頸,從而進行相應的最佳化。

  4. 合規性要求:某些行業法規可能要求保留一定期限的日誌記錄,以滿足合規性檢查。

要配置 Nginx 日誌保留半年,通常需要使用 logrotate 工具來實現日誌檔案的定期輪換和壓縮。

這個工具配置並不難,鬆哥給大家舉個栗子。

/etc/logrotate.d/ 目錄下建立一個名為 nginx 的配置檔案,內容如下:

/var/log/nginx/*.log {
    daily
    rotate 180
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}


這個配置會每天檢查 Nginx 日誌檔案,並將它們保留 180 天(約 6 個月),然後自動壓縮舊的日誌檔案。postrotate 部分的命令會在日誌輪換後重新開啟 Nginx 日誌檔案,以便繼續記錄新的日誌資訊。

透過這些配置,我們可以確保 Nginx 的日誌檔案被保留半年,同時舊的日誌檔案會被壓縮以節省磁碟空間。

十一 設定緩衝區

Nginx 的緩衝區溢位攻擊是一種常見的安全漏洞,它發生在程式試圖向一個緩衝區寫入超出其預分配大小的資料時。

這種攻擊可能導致資料覆蓋了相鄰的記憶體區域,可能破壞程式的執行流程,甚至可以被惡意攻擊者利用來執行惡意代碼。

爲了防止緩衝區溢位類攻擊事件,可以設定客戶端請求體、請求頭和客戶端最大請求體的緩衝區大小。

配置方式如下:

client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 1k;


這四行配置含義如下:

  • client_body_buffer_size 1K;:這條指令設定了 Nginx 用來讀取客戶端請求體(比如 POST 請求中的資料)的緩衝區大小。在這個例子中,緩衝區大小被設定為 1KB。如果請求體的大小超過了這個緩衝區的大小,Nginx 會使用磁碟來暫存超出部分的資料。

  • client_header_buffer_size 1k;:這條指令定義了 Nginx 用來讀取客戶端 HTTP 請求頭部的緩衝區大小。這裏設定的大小是 1KB。如果請求頭部的大小超過了這個緩衝區的大小,Nginx 會使用 large_client_header_buffers 定義的緩衝區。

  • client_max_body_size 1k;:這條指令限制了 Nginx 伺服器願意接收的最大請求體大小。如果客戶端傳送的請求體超過了這個大小(在這個例子中是 1KB),Nginx 將返回一個 413(Request Entity Too Large)錯誤。

  • large_client_header_buffers 2 1k;:這條指令定義了 Nginx 用於處理大於 client_header_buffer_size 指定大小的請求頭的緩衝區數量和大小。這裏配置了 2 個大小為 1KB 的緩衝區。當請求頭的大小超過了 client_header_buffer_size 定義的緩衝區大小時,Nginx 會使用這兩個額外的緩衝區來處理請求頭。

這些配置對於防止緩衝區溢位攻擊和處理大請求都是非常重要的。

十二 使用普通使用者啟動

在 Linux 系統中,只有 root 使用者或者具有特定許可權的使用者才能繫結 1024 以下的埠,如 80 埠(HTTP)和 443 埠(HTTPS)。

如果 Nginx 以 root 使用者執行,它將擁有過高的許可權,這可能會帶來安全風險。因此,爲了最小化許可權,通常會建立一個普通使用者來執行 Nginx,以減少潛在的安全漏洞。

配置方式如下:

  1. 建立使用者

首先,你需要建立一個普通使用者和使用者組,例如 nginx

groupadd nginx 
useradd -g nginx -d /usr/local/nginx nginx


這裏建立了一個名為 nginx 的使用者和組,並設定了使用者的家目錄。

  1. 授權訪問

確保新使用者有權訪問 Nginx 的配置檔案、日誌檔案和伺服器檔案。

chown -R nginx:nginx /usr/local/nginx/


這條命令將 Nginx 目錄及其所有子目錄和檔案的所有權更改為新建立的 nginx 使用者和組。

  1. 配置Nginx

編輯 Nginx 配置檔案(通常位於 /etc/nginx/nginx.conf),設定 user 指令以指定 Nginx 工作程序的使用者。

user nginx;


這行配置指定 Nginx 應該以 nginx 使用者的身份執行。

  1. 設定許可權

如果需要,可以使用 setcap 命令賦予 Nginx 監聽 1024 以下埠的能力,而不需要以 root 使用者執行。

setcap cap_net_bind_service=+ep /usr/local/nginx/sbin/nginx


這個命令允許 Nginx 以普通使用者身份繫結到 80 和 443 埠。

  1. 啟動Nginx

使用普通使用者啟動 Nginx。

su - nginx 
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf


這裏首先切換到 nginx 使用者,然後啟動 Nginx 服務。

  1. 驗證

檢查 Nginx 是否以普通使用者啟動。

ps -ef | grep nginx


這條命令將顯示 Nginx 的程序資訊,你可以驗證它是否以 nginx 使用者執行。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.