時間:2023-10-27 01:30:01 | 來源:網(wǎng)站運營
時間:2023-10-27 01:30:01 來源:網(wǎng)站運營
cdn怎么不暴露源站?:若是作為剛?cè)腴T的網(wǎng)站管理員,你或許知道使用 CDN 來避免暴露源站 IP。同時,你或許會采取一些常用措施,諸如:修改源站的 Hostname,對非法請求不返回或返回?zé)o關(guān)/迷惑類信息。server { listen 443 ssl; server_name localhost; ssl_certificate /root/cert.pem; ssl_certificate_key /root/cert.key; location / { return 444; }}
然而證書信息依舊會被返回:curl -k https://127.0.0.1 -v* Trying 127.0.0.1:443...* TCP_NODELAY set* Connected to 127.0.0.1 (127.0.0.1) port 443 (#0)* ALPN, offering h2* ALPN, offering http/1.1* successfully set certificate verify locations:* CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.2 (IN), TLS handshake, Certificate (11):* TLSv1.2 (IN), TLS handshake, Server key exchange (12):* TLSv1.2 (IN), TLS handshake, Server finished (14):* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):* TLSv1.2 (OUT), TLS handshake, Finished (20):* TLSv1.2 (IN), TLS handshake, Finished (20):* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384* ALPN, server accepted to use http/1.1* Server certificate:* subject: CN=127.0.0.1* start date: Feb 12 07:35:46 2020 GMT* expire date: Feb 13 07:35:46 2020 GMT* issuer: CN=127.0.0.1* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.> GET / HTTP/1.1> Host: 127.0.0.1> User-Agent: curl/7.58.0> Accept: */*> * Empty reply from server* Connection #0 to host 127.0.0.1 left intactcurl: (52) Empty reply from server
在開始回答前,首先要說明一下:這個回答我最初是以英文的形式發(fā)布在自己的博客中,同時也在 Stack Overflow 中做了存檔。你可能會覺得這個回答有點“翻譯腔”,但確實是原創(chuàng)回答,請不要誤會。如果你覺得本回答對你有所幫助,請幫忙 Upvote 一下,感謝。另外,如果你需要完全地保護你的服務(wù)器 IP 不被泄露,僅僅是做我在本文中提到的部分是遠遠不夠的。安全遵循短板理論[1]。任何疏忽都會導(dǎo)致不可預(yù)計的的損失。所以,你需要為自己的安全負責(zé)。我在此僅僅是介紹防止源站 IP 泄露的一些方法。如果出現(xiàn)諸如軟件設(shè)計錯誤導(dǎo)致的 IP 泄露,本文并不能幫你修正那些錯誤。
apt-get install iptables-persistent
return 444;
,并且這一小段中的其它部分你可以直接跳過,或僅僅時略讀即可。server { # 如果使用了錯誤的 Hostname,SSL 握手會被拒絕 listen 443 ssl; ssl_reject_handshake on;}server { # 對于攜帶正確 Hostname 的請求,服務(wù)器會繼續(xù)做后續(xù)處理 listen 443 ssl; server_name example.com; ssl_certificate example.com.crt; ssl_certificate_key example.com.key;}
這個方法僅適用于 Nginx 大于等于 1.19.4 的情況。否則要想達到阻止 SNI 信息泄露的目的,你需要安裝strict-sni 補丁。這個補丁是由來自南韓的 PHP 開發(fā)者 Hakase 開發(fā)的,該補丁可以使 Nginx 在 1.19.3 之前的實例針對非法請求真正地空返回。apt-get install git curl gcc libpcre3-dev software-properties-common /build-essential libssl-dev zlib1g-dev libxslt1-dev libgd-dev libperl-dev
然后,在 OpenSSL 的發(fā)布頁中下載你想使用的版本。 git clone https://git.hakase.app/Hakase/openssl-patch.git
基于你之前選擇的 OpenSSL 版本,先切換至 OpenSSL 源碼的目錄,然后為 OpenSSL 打上相應(yīng)版本的補?。?br>cd opensslpatch -p1 < ../openssl-patch/openssl-equal-1.1.1d_ciphers.patch
來自開發(fā)者的備注:OpenSSL 3.x 版本有很多 API 上的改動,對于這些 OpenSSL 版本來說,這個補丁不再有用。(特指:Chacha20 和 Equal Preference 補?。┰跅l件允許的情況下,推薦使用 OpenSSL 1.1.x。下載你所需版本的 Nginx 安裝包。
cd nginx/curl https://raw.githubusercontent.com/hakasenyang/openssl-patch/master/nginx_strict-sni_1.15.10.patch | patch -p1
在 Nginx 的配置指令中指定 OpenSSL 的目錄: ./configure --with-http_ssl_module --with-openssl=/root/openssl
重要:在實際的實踐中,僅使用這些參數(shù)并不能真正使網(wǎng)站如預(yù)期一樣運行,你需要同時添加你想要和你需要的參數(shù)。例如:如果你想要你的網(wǎng)站支持 http/2 協(xié)議,則需要添加 --with-http_v2_module
參數(shù)。它不會主動自己把自己編譯進去。 ./configure --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_ssl_module --with-openssl=/root/openssl
注:這部分是指代概要部分中「通過偽造成其它真實存在的網(wǎng)站/CDN 節(jié)點來混淆視聽」的這一節(jié),目的僅僅是給予發(fā)起非指向性掃描的人假信息。對于指向性掃描,它很難很好地達到目的。如果你只是想要對非授權(quán)客戶端返回一個假網(wǎng)站,例如:手工制作的假網(wǎng)站、設(shè)置反向代理等等(同時也對非指向性掃描器返回空結(jié)果),你應(yīng)當跳過這個部分,或者僅僅將這些參數(shù)視作「以待后用」添加。 make && make install
ln -s /usr/lib/nginx/modules/ /usr/share/nginxln -s /usr/share/nginx/sbin/nginx /usr/sbincat > /lib/systemd/system/nginx.service <<-EOF[Unit]Description=The NGINX HTTP and reverse proxy serverAfter=syslog.target network.target remote-fs.target nss-lookup.target[Service]Type=forkingPIDFile=/run/nginx.pidExecStartPre=/usr/sbin/nginx -tExecStart=/usr/sbin/nginxExecReload=/bin/kill -s HUP $MAINPIDExecStop=/bin/kill -s QUIT $MAINPIDPrivateTmp=true[Install]WantedBy=multi-user.targetEOFsystemctl enable nginx
http { # 控制開關(guān) strict_sni on; strict_sni_header on; # 假的(默認)server 塊 server { server_name localhost; listen 80; listen 443 ssl default_server; # "default_server" 需要被寫在這里 ssl_certificate /root/cert.crt; # 可以為任意證書 ssl_certificate_key /root/cert.key; # 可以為任意證書 location / { return 444; } } # 常規(guī) server 塊 server { server_name normal_domain.tld; listen 80; listen 443 ssl; ssl_certificate /root/cert.crt; # 你的真實證書 ssl_certificate_key /root/cert/cert.key; # 你的真實證書 location / { echo "Hello World!"; } }}
現(xiàn)在,非指向性掃描器不再能獲知你在這臺服務(wù)器上運行什么網(wǎng)站了,除非是被指向性掃描,也就是對方知道你的 Hostname 的情況。 return 444;
意味著在返回 HTTP(并非 HTTPS)請求時就是字面意義的什么都不返回。如果沒有打上 openssl-patch 補丁,當客戶端嘗試建立 TLS 鏈接時,證書信息仍會被返回。strict_sni on;
后,CDN 節(jié)點若在請求源站時不攜帶 SNI 信息,將會導(dǎo)致請求失敗。參見 proxy_ssl_name. curl -v -k https://35.186.1.1* Rebuilt URL to: https://35.186.1.1/* Trying 35.186.1.1...* TCP_NODELAY set* Connected to 35.186.1.1 (35.186.1.1) port 443 (#0)* ALPN, offering h2* ALPN, offering http/1.1* successfully set certificate verify locations:* CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.2 (IN), TLS handshake, Certificate (11):* TLSv1.2 (IN), TLS handshake, Server key exchange (12):* TLSv1.2 (IN), TLS handshake, Server finished (14):* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):* TLSv1.2 (OUT), TLS change cipher, Client hello (1):* TLSv1.2 (OUT), TLS handshake, Finished (20):* TLSv1.2 (IN), TLS handshake, Finished (20):* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384* ALPN, server accepted to use http/1.1* Server certificate:* subject: CN=normal_domain.tld* start date: Nov 15 05:41:39 2019 GMT* expire date: Nov 14 05:41:39 2020 GMT* issuer: CN=normal_domain.tld* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.> GET / HTTP/1.1> Host: 35.186.1.1> User-Agent: curl/7.58.0> Accept: */*> * Empty reply from server* Connection #0 to host 35.186.1.1 left intactcurl: (52) Empty reply from server
啟用后:curl -v -k https://35.186.1.1* Rebuilt URL to: https://35.186.1.1/* Trying 35.186.1.1...* TCP_NODELAY set* Connected to 35.186.1.1 (35.186.1.1) port 443 (#0)* ALPN, offering h2* ALPN, offering http/1.1* successfully set certificate verify locations:* CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS alert, Server hello (2):* error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name* stopped the pause stream!* Closing connection 0curl: (35) error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name
謹防不知道,你應(yīng)當了解:無論你后續(xù)怎么配置客戶端校驗規(guī)則(如 HTTP 頭信息校驗等),客戶端在請求時攜帶目標/部署在服務(wù)端的 Hostname 時,證書信息仍會被返回。這是因為它的作用僅僅是預(yù)防非指向性掃描:這是建立在攻擊者保護知道這臺服務(wù)器上運行著什么網(wǎng)站的前提上的。如需應(yīng)對指向性掃描,我強烈建議在條件允許的情況下修改在源站服務(wù)器上部署的 Hostname。 curl -v -k --resolve wrong_domain.tld:443:35.186.1.1 https://wrong_domain.tld* Added wrong_domain.tld:443:35.186.1.1 to DNS cache* Rebuilt URL to: https://wrong_domain.tld/* Hostname wrong_domain.tld was found in DNS cache* Trying 35.186.1.1...* TCP_NODELAY set* Connected to wrong_domain.tld (35.186.1.1) port 443 (#0)* ALPN, offering h2* ALPN, offering http/1.1* successfully set certificate verify locations:* CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS alert, Server hello (2):* error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name* stopped the pause stream!* Closing connection 0curl: (35) error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name
使用正確的 Hostname 請求時的結(jié)果:(僅在 Hostname 正確的情況下,證書信息才會被返回) curl -v -k --resolve normal_domain.tld:443:35.186.1.1 https://normal_domain.tld* Added normal_domain.tld:443:35.186.1.1 to DNS cache* Rebuilt URL to: https://normal_domain.tld/* Hostname normal_domain.tld was found in DNS cache* Trying 35.186.1.1...* TCP_NODELAY set* Connected to normal_domain.tld (35.186.1.1) port 443 (#0)* ALPN, offering h2* ALPN, offering http/1.1* successfully set certificate verify locations:* CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.2 (IN), TLS handshake, Certificate (11):* TLSv1.2 (IN), TLS handshake, Server key exchange (12):* TLSv1.2 (IN), TLS handshake, Server finished (14):* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):* TLSv1.2 (OUT), TLS change cipher, Client hello (1):* TLSv1.2 (OUT), TLS handshake, Finished (20):* TLSv1.2 (IN), TLS handshake, Finished (20):* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384* ALPN, server accepted to use http/1.1* Server certificate:* subject: CN=normal_domain.tld* start date: Nov 15 05:41:39 2019 GMT* expire date: Nov 14 05:41:39 2020 GMT* issuer: CN=normal_domain.tld* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.> GET / HTTP/1.1> Host: normal_domain.tld> User-Agent: curl/7.58.0> Accept: */*> < HTTP/1.1 200 OK< Server: nginx/1.17.5< Date: Fri, 15 Nov 2019 05:53:19 GMT< Content-Type: text/plain< Transfer-Encoding: chunked< Connection: keep-alive< abc* Connection #0 to host normal_domain.tld left intact
注:如果你知道已知的非指向性掃描器的 IP 范圍,你可以將它們?nèi)繑r截,權(quán)當是再上一層保險。這里給出 Censys 掃描器的 IP 范圍:192.35.168.0/2374.120.14.0/24167.248.133.0/24162.142.125.0/24
load_module "modules/ngx_stream_module.so";http{ # 給自己 DIY 下 http 塊 server { listen 80 default_server; server_name localhost; location / { proxy_pass http://104.27.184.146:80; # 偽裝成 Cloudflare CDN 節(jié)點服務(wù)器 proxy_set_header Host $host; } } server { listen 80; server_name yourwebsite.com; # 如果你設(shè)置 https 作為唯一的回源協(xié)議,你不應(yīng)該在 http{} 塊中配置關(guān)于你真實域名的塊,就像這里(除非你是監(jiān)聽在 localhost 而不是公網(wǎng) IP) location / { proxy_pass http://127.0.0.1:8080; # 你的后端地址 proxy_set_header Host $host; } }}stream{ map $ssl_preread_server_name $name { yourwebsite.com website-upstream; # 你的真實網(wǎng)站路由 default cloudflare; # 默認路由 } upstream cloudflare { server 104.27.184.146:443; # Cloudflare 的 IP } upstream website-upstream {server 127.0.0.1:8080;} # 你的真實網(wǎng)站后端 server { listen 443; proxy_pass $name; proxy_ssl_name $ssl_preread_server_name; proxy_ssl_protocols TLSv1.2 TLSv1.3; ssl_preread on; }}
curl -I -v --resolve www.cloudflare.com:443:127.0.0.1 https://www.cloudflare.com/* Expire in 0 ms for 6 (transfer 0x55f3f0ae0f50)* Added www.cloudflare.com:443:127.0.0.1 to DNS cache* Hostname www.cloudflare.com was found in DNS cache* Trying 127.0.0.1...* TCP_NODELAY set* Expire in 200 ms for 4 (transfer 0x55f3f0ae0f50)* Connected to www.cloudflare.com (127.0.0.1) port 443 (#0)* ALPN, offering h2* ALPN, offering http/1.1* successfully set certificate verify locations:* CAfile: none CApath: /etc/ssl/certs* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):* TLSv1.3 (IN), TLS handshake, Certificate (11):* TLSv1.3 (IN), TLS handshake, CERT verify (15):* TLSv1.3 (IN), TLS handshake, Finished (20):* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):* TLSv1.3 (OUT), TLS handshake, Finished (20):* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384* ALPN, server accepted to use h2* ALPN, offering http/1.1* successfully set certificate verify locations:* CAfile: none CApath: /etc/ssl/certs* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):* TLSv1.3 (IN), TLS handshake, Certificate (11):* TLSv1.3 (IN), TLS handshake, CERT verify (15):* TLSv1.3 (IN), TLS handshake, Finished (20):* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):* TLSv1.3 (OUT), TLS handshake, Finished (20):* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384* ALPN, server accepted to use h2* Server certificate:* subject: businessCategory=Private Organization; jurisdictionC=US; jurisdictionST=Delaware; serialNumber=4710875; C=US; ST=California; L=San Francisco; O=Cloudflare, Inc.; CN=cloudflare.com* start date: Oct 30 00:00:00 2018 GMT* expire date: Nov 3 12:00:00 2020 GMT* subjectAltName: host "www.cloudflare.com" matched cert's "www.cloudflare.com"* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert ECC Extended Validation Server CA* SSL certificate verify ok.* Using HTTP2, server supports multi-use* Connection state changed (HTTP/2 confirmed)* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0* Using Stream ID: 1 (easy handle 0x55f3f0ae0f50)> HEAD / HTTP/2> Host: www.cloudflare.com> User-Agent: curl/7.64.0> Accept: */*> * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):* old SSL session ID is stale, removing* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!< HTTP/2 200 HTTP/2 200 < date: Tue, 06 Oct 2020 06:26:50 GMT* Connection #0 to host www.cloudflare.com left intact
(成功偽裝成其它網(wǎng)站,部分結(jié)果已省略) server { listen 80; server_name yourdomain.com; if ($http_auth_tag != "here_is_the_credential") { return 444; } location / { echo "Hello World!"; }}
若想返回虛假的網(wǎng)站/后端時的配置文件: server { listen 80; server_name yourdomain.com; if ($http_auth_tag != "here_is_the_credential") { return @fake; } location / { echo "Hello World!"; } location @fake { root /var/www/fakewebsite/; # 強烈建議自己 DIY 一個假站點 }}
注:如果你傾向于在 https/443 端口上配置這些,我推薦你使用未知域名自簽證書。使用真實且域名為公共暴露的域名可能會讓攻擊者更容易找到你的源站。Nginx 允許你在 SNI 信息不匹配 server_name 的情況下使用證書。cat > csrconfig.txt <<-EOF[ req ]default_md = sha256prompt = noreq_extensions = req_extdistinguished_name = req_distinguished_name[ req_distinguished_name ]commonName = yeet.comcountryName = SG[ req_ext ]keyUsage=critical,digitalSignature,keyEnciphermentextendedKeyUsage=critical,serverAuth,clientAuthsubjectAltName = @alt_names[ alt_names ]DNS.0 = yeet.comEOFcat > certconfig.txt <<-EOF[ req ]default_md = sha256prompt = noreq_extensions = req_extdistinguished_name = req_distinguished_name[ req_distinguished_name ]commonName = yeet.comcountryName = SG[ req_ext ]subjectKeyIdentifier = hashauthorityKeyIdentifier = keyid:always,issuerkeyUsage=critical,digitalSignature,keyEnciphermentextendedKeyUsage=critical,serverAuth,clientAuthsubjectAltName = @alt_names[ alt_names ]DNS.0 = yeet.comEOFopenssl genpkey -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out cert.keyopenssl req -new -nodes -key cert.key -config csrconfig.txt -out cert.csropenssl req -x509 -nodes -in cert.csr -days 365 -key cert.key -config certconfig.txt -extensions req_ext -out cert.pem
考慮到有人可能會拿這些指令生成 csr 用于申請真實證書,我保留了國家的字段(部分 CA 在接收 csr 文件時要求這個字段需要存在),如果不需要可以自行刪除。server { listen 443; ssl_certificate /etc/nginx/certs/cert.crt; ssl_certificate_key /etc/nginx/certs/cert.key; server_name yourdomain.com; ssl_client_certificate /etc/nginx/certs/cloudflare.crt; ssl_verify_client on; error_page 495 496 = @444; # 用于在出現(xiàn)客戶端證書校驗相關(guān)錯誤時,用自行指定的內(nèi)容替代默認的錯誤返回信息 location @444 {return 444;} location / { echo "Hello World!"; }}
此配置將會在遇到客戶端證書錯誤時不作返回 server { listen 80 default_server; listen 443 ssl default_server; ssl_certificate /etc/nginx/certs/cert.crt; ssl_certificate_key /etc/nginx/certs/cert.key; server_name localhost; location / { return 444; }}
curl http://127.0.0.1:80curl: (52) Empty reply from servercurl -k https://127.0.0.1:443 curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)
關(guān)鍵詞:暴露
微信公眾號
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。