﻿# 開發與部署網站時需注意不要使用到 ERR_UNSAFE_PORT 不安全的埠號

我昨天在授課的時候，突然遇到 Chrome 瀏覽器無法瀏覽網站的情況(`ERR_UNSAFE_PORT`)，我踩到一個不知道算不算罕見的地雷，在深入瞭解之後，覺得有必要寫成文章提醒大家，因為你還真的很有可能會在不久的將來遇到這個問題，看完文章才不會讓你日後鬼打牆太久，讓我們繼續看下去。

### 問題發生的過程

我昨天在示範如何將 ASP.NET Core 部署到 IIS 站台，我很隨意的挑選了一個吉利的數字 `10080` 當成網站的 HTTP 埠號，結果用 Google Chrome 開啟網頁後，竟然出現以下錯誤：

![ERR\_UNSAFE\_PORT](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/cec1c80b594f44073d53-166195717-67e116da-0ef0-4fa0-8a60-c2be8c5ac943.jpg)

由於之前並沒有遇到過這種狀況，什麼叫 `ERR_UNSAFE_PORT` 呢？我當下先判斷可能跟 HSTS ([HTTP Strict Transport Security](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)) ([HTTP強制安全傳輸技術](https://zh.wikipedia.org/wiki/HTTP%E4%B8%A5%E6%A0%BC%E4%BC%A0%E8%BE%93%E5%AE%89%E5%85%A8)) 有關，但調整了一下 Chrome 的 HSTS 設定，還是連不上。狀況很詭異，我甚至一度懷疑 Google Chrome 是不是把 HTTP (Port 80) 都給封鎖了？應該不太可能吧！結果我用 `curl` 呼叫這個網址，發現是可以連線的，只有 Google Chrome / Microsoft Edge 連不上而已。我最後換了一個 Port，並設定 HTTPS 安全連線，然後就可以運作了，真的相當詭異！

### Google Chrome 有一組黑名單的埠號清單

我今天抽空研究了一下，發現在 [Chromium 的原始碼](https://chromium.googlesource.com/chromium/src.git/+/refs/heads/master/net/base/port_util.cc#52)中有定義一系列被列入**黑名單的埠號清單** (blocked ports)：

> [net/base/port\_util.cc - chromium/src.git - Git at Google](https://chromium.googlesource.com/chromium/src.git/+/refs/heads/master/net/base/port_util.cc#52)

```csharp
// The general list of blocked ports. Will be blocked unless a specific
// protocol overrides it. (Ex: ftp can use port 21)
// When adding a port to the list, consider also adding it to kAllowablePorts,
// below.
const int kRestrictedPorts[] = {
    1,      // tcpmux
    7,      // echo
    9,      // discard
    11,     // systat
    13,     // daytime
    15,     // netstat
    17,     // qotd
    19,     // chargen
    20,     // ftp data
    21,     // ftp access
    22,     // ssh
    23,     // telnet
    25,     // smtp
    37,     // time
    42,     // name
    43,     // nicname
    53,     // domain
    69,     // tftp
    77,     // priv-rjs
    79,     // finger
    87,     // ttylink
    95,     // supdup
    101,    // hostriame
    102,    // iso-tsap
    103,    // gppitnp
    104,    // acr-nema
    109,    // pop2
    110,    // pop3
    111,    // sunrpc
    113,    // auth
    115,    // sftp
    117,    // uucp-path
    119,    // nntp
    123,    // NTP
    135,    // loc-srv /epmap
    137,    // netbios
    139,    // netbios
    143,    // imap2
    161,    // snmp
    179,    // BGP
    389,    // ldap
    427,    // SLP (Also used by Apple Filing Protocol)
    465,    // smtp+ssl
    512,    // print / exec
    513,    // login
    514,    // shell
    515,    // printer
    526,    // tempo
    530,    // courier
    531,    // chat
    532,    // netnews
    540,    // uucp
    548,    // AFP (Apple Filing Protocol)
    554,    // rtsp
    556,    // remotefs
    563,    // nntp+ssl
    587,    // smtp (rfc6409)
    601,    // syslog-conn (rfc3195)
    636,    // ldap+ssl
    989,    // ftps-data
    990,    // ftps
    993,    // ldap+ssl
    995,    // pop3+ssl
    1719,   // h323gatestat
    1720,   // h323hostcall
    1723,   // pptp
    2049,   // nfs
    3659,   // apple-sasl / PasswordServer
    4045,   // lockd
    5060,   // sip
    5061,   // sips
    6000,   // X11
    6566,   // sane-port
    6665,   // Alternate IRC [Apple addition]
    6666,   // Alternate IRC [Apple addition]
    6667,   // Standard IRC [Apple addition]
    6668,   // Alternate IRC [Apple addition]
    6669,   // Alternate IRC [Apple addition]
    6697,   // IRC + TLS
    10080,  // Amanda
};
```

> 由上述清單可以得知，我授課時所用到的 `10080` 正好就是黑名單之一！ How Lucky!!! 😄

事實上，無論你走 HTTP 或 HTTPS，只要用到這幾個 Ports，就會讓所有 Chromium-based 的瀏覽器都連不上，直接從用戶端就封鎖了連線，完全連不上！ 🔥

![ERR\_UNSAFE\_PORT](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/9240c1f054ffd90a3ec9-166195719-145c3a92-2db6-4b4c-8ea3-9b4b35f3d16e.jpg)

若要讓 Google Chrome 可以開放這些限制的 Ports 清單，需要在啟動 Chrome 的時候加入 `--explicitly-allowed-ports=10080` 這樣的啟動參數。例如：

```batch
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory=Default --explicitly-allowed-ports=10080
```

> 詳情請見 [How to fix ERR\_UNSAFE\_PORT error on Chrome when browsing to unsafe ports](https://superuser.com/questions/188006/how-to-fix-err-unsafe-port-error-on-chrome-when-browsing-to-unsafe-ports) 的各種回覆答案。

### Mozilla Firefox 也有一組黑名單的埠號清單

沒錯，Mozilla Firefox 也有一組黑名單的埠號清單，我用 Firefox 開啟 Port 10080 的網站，會出現以下畫面：

![This address is restricted](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/941696199e6d81a113ee-166216430-1713f64a-0f26-439e-9c9a-52a5049cddd9.jpg)

我從[網路上](https://superuser.com/questions/188058/which-ports-are-considered-unsafe-by-chrome#comment512837_188070)只能找到一份[相當古老的歷史文件](https://www-archive.mozilla.org/projects/netlib/portbanning#portlist) (April 21, 2008)，基本上沒有什麼公信力，文件所列的 Ports 清單也跟 Chromium 不太一樣。

我最後則是從 Firefox 的[原始碼](https://firefox-source-docs.mozilla.org/contributing/directory_structure.html)找到一份[測試案例](https://github.dev/mozilla/gecko-dev/blob/master/testing/web-platform/tests/fetch/api/request/request-bad-port.any.js#L5-L86)，裡面有提到 [WHATWG community](https://whatwg.org/) 的 [Fetch Standard](https://fetch.spec.whatwg.org/) 規格中，有明確提到 [2.9. Port blocking](https://fetch.spec.whatwg.org/#port-blocking) 清單！ 🔥

Bingo! 我原本還以為不同瀏覽器可能會有不同的實作，有標準就好辦了，大家都可以清楚的知道哪些 Ports 絕對不能拿來用瀏覽器開啟了！ 👍

> 如果你想要設定 Firefox 開放受限制的 Ports，可以參考 [How to create a preference/string to override access to a restricted address/port. | Knowledge Base discussions | Forums | Mozilla Support](https://support.mozilla.org/en-US/forums/knowledge-base-articles/711533?last=66743) 這份文件。([影片教學](https://www.youtube.com/watch?v=h6JChX2D_EE))

### 使用 .NET CLI 或 Visual Studio 2022 建立專案的注意事項

事實上，從 ASP.NET Core 6.0 開始，透過 .NET CLI 或 Visual Studio 2022 建立的全新專案，都會預設指派一個亂數的 Ports，我們可以從 [Configure endpoints for the ASP.NET Core Kestrel web server](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints) 得知，新專案的 HTTP Ports 會介於 `5000`\-`5300` 之間，而 HTTPS Ports 會介於 `7000`\-`7300` 之間，而這個設定預設會放在 `Properties/launchSettings.json` 設定檔中。

是的，你沒想錯，你有 300 分之 2 的機率 (`0.66%`) 會選到 `5060` 與 `5061` 這兩個**黑名單的埠號**，請大家務必小心！

> 我有看了 ASP.NET Core 6.0 原始碼，他們並沒有對這兩個 Ports 做排除，我想應該「很少人這麼好運」會選中這兩個 Ports，但中獎率確實比中樂透還高阿！ 😅

### 後記

我們軟體開發這一行，有太多的鬼故事，我們常常會遇到無法解釋的問題，我們都知道解決問題的方法很簡單，你可能會想：「就換個 Port 就好啦，幹嘛這麼麻煩？」但我認為，每一次的鬼打牆就是最好的學習機會，找尋每一個幽靈背後的科學原理，才是持續進步的不二法門！

### 相關連結

-   [Which ports are considered unsafe by Chrome? - Super User](https://superuser.com/a/188070/95798)
-   [all that jazz - Why does Chrome consider some ports unsafe?](https://jazzy.id.au/2012/08/23/why_does_chrome_consider_some_ports_unsafe.html)
-   [Fetch Standard - 2.9. Port Blocking](https://fetch.spec.whatwg.org/#port-blocking)
-   [Configure endpoints for the ASP.NET Core Kestrel web server](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints)
    -   [New projects all use the same ports by default resulting in error when running more than one at a time · Issue #34363 · dotnet/aspnetcore · GitHub](https://github.com/dotnet/aspnetcore/issues/34363)
-   [How to automatically choose a free port in ASP.NET Core 3.0](https://andrewlock.net/how-to-automatically-choose-a-free-port-in-asp-net-core/)
