分類
Web

[Web] 強化 ASP.NET Web Form 網站安全性 Content Security Policy(CSP) 和 HTTP Strict Transport Security(HSTS)

Situation

因為公司有和 2330 合作,2330 偶爾會稽核公司底下的外部網站。

這次輪到小弟負責維護的網站被稽核,抓出兩項安全性缺失分別為 CSP(Content Security Policy) 和 HSTS(HTTP Strict Transport Security)。

關於這兩項安全性問題可以從 https://www.ithome.com.tw/voice/130302 或自行Google看看。

簡單的說這兩項都和 HTTP 的 Header 有關,而透過強化 HTTP Header 設定可以有效的提升網站安全性。

Action

首先我們必須找出如何偵測網站的安全性,可以很簡單的在 https://securityheaders.com/ 輸入要測試的網址來檢測。

以下是檢測網站修改前的狀態

修改前檢測結果

如上圖可以看到 Strict-Transport-Security / Content-Security-Policy / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy 都是不合格的。

如果你使用的剛好是 ASP.NET 的 Web Form,可以很簡單的透過設定 web.config 來強化安全性。

請打開 web.config 並在 <customHeaders></customHeaders> 輸入下列設定

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <httpProtocol>
            <customHeaders>
                <add name="Cache-Control" value="no-cache" />
                <!-- Try your URL in this Scen WebSite : https://securityheaders.com/  -->
                <!-- Good Solution ref https://blog.elmah.io/improving-security-in-asp-net-mvc-using-custom-headers/ -->
                <!-- 1. Enable HSTS (HTTP Strict Transport Security) -->
                <!-- ref : https://sdwh.dev/posts/2020/10/Cyber-Security-Web-Config-Configuration/#Contentt-Security-Policy-CSP  -->
                <!-- Hide Server Information -->
                <remove name="X-Powered-By" />
                <remove name="X-AspNetMvc-Version" />
                <!-- Referrer Policy -->
                <add name="Referrer-Policy" value="No-referrer-when-downgrade" />
                <!-- Anti-Clickjacking-->
                <add name="X-Frame-Options" value="DENY" />
                <!-- Anti-XSS for IE -->
                <add name="X-Xss-Protection" value="1; mode=block" />
                <!-- HSTS -->
                <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
                <!-- 1. Enable HSTS (HTTP Strict Transport Security) -->
                <!-- 2. Enable Http Header Content Security Policy(CSP) -->
                <!-- ref :https://stackoverflow.com/questions/35851651/content-security-policy-in-asp-net-webforms  -->
                <add name="Content-Security-Policy" value="default-src 'self'" />
                <!-- 2. Enable Http Header Content Security Policy(CSP) -->
                <!-- 3. Enable X-Content-Type-Options -->
                <!-- ref : https://stackoverflow.com/questions/18337630/what-is-x-content-type-options-nosniff -->
                <remove name="X-Content-Type-Options"/>
                <add name="X-Content-Type-Options" value="nosniff"/>
                <!-- 3. Enable X-Content-Type-Options -->
                <!-- 4. Enable Permissions Policy -->
                <!-- ref https://blog.elmah.io/improving-security-in-asp-net-mvc-using-custom-headers/ -->
                <add name="Permissions-Policy" value="accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()" />
                <!-- 4. Enable Permissions Policy -->
            </customHeaders>
        </httpProtocol>
    </system.webServer>
</configuration>


Result

更改完成後再到 https://securityheaders.com/ 重新檢測,結果如下,完成 Header 的安全性設定


分類
ASP.NET

[ASP.NET] 使用 Visual Studio 2019 編輯網頁(沒有sln檔)

Situation

需要修改網頁(http://www.somewhere.com)且已知該網頁對應的網路空間(\\192.168.1.1\websitefolder),其網路空間看起來也有對應的Source Code,如.html , web.config等等,但沒有.sln 等等的解決方案檔。

Action

首先使用編輯器修改 index.html檔並觀察網頁是否出現對應的修改內容,結果網頁確實發生預期的改變。
開啟 VS2019 -> 不使用程式碼繼續 -> 在主畫面選擇”檔案” -> 開啟 -> 網站 -> 左方選擇檔案系統 -> 下方的資料夾填入對應的網路空間路徑(\\192.168.1.1\websitefolder)
VS2019 便可讀取該路徑內的所有檔案

Result

透過上方的Action便可使用 VS2019 編輯網頁 Source Code,可以一邊開啟網頁一邊確認編輯內容是否正確。
離開前VS2019會詢問是否儲存.sln檔。若選是,該.sln 便會儲存在本機端,之後只要點擊.sln檔便可直接編輯網頁內容。
關於版控的部分,可以直接使用檔案總管開啟對應的網路空間(\\192.168.1.1\websitefolder),接著右鍵點擊該目錄 -> git bash -> 可直接在該目錄使用 git 指令(有時候command line 反應會比較慢,不過基本上是沒問題的)
 

分類
ASP.NET

[ASP.NET] 判斷使用者IP為內網或外網

Situation

需要判斷使用者的IP 為內網或外網。

Action

使用者 IP 可以透過 Request.UserHostAddress 取得
“判斷內外網邏輯” 首先從重用性考慮,該邏輯之後可以在其他專案或模組重複使用,看起來應該提取到新類別中,如下

public class NetworkManager
{
    public enum NetType
    {
        INTERNET, INTRANET
    }
    public static NetType CheckIntranetOrInternet(string targetIP)
    {
        string[] targetIPSections = targetIP.Split('.');
        /// IP地址中,有三段地址專門用於私網的規劃,不能被用於網際網路上的連線如下
        /// Class A:10.0.0.0-10.255.255.255
        if (targetIPSections[0] == "10") {
            return NetType.INTRANET;
        }
        /// Class B:172.16.0.0-172.31.255.255
        if (targetIPSections[0] == "172")
        {
            int targetIPSecondSection = Convert.ToInt16(targetIPSections[1]);
            if (targetIPSecondSection >= 16 && targetIPSecondSection <= 31)
            {
                return NetType.INTRANET;
            }
        }
        /// Class C:192.168.0.0-192.168.255.255
        if (targetIPSections[0] == "192" && targetIPSections[1] == "168")
        {
            return NetType.INTRANET;
        }
        return NetType.INTERNET;
    }
}

CheckIntranetOrInternet 方法的參數型態其實有兩個選項為 Request 或 string,因為我習慣寫單元測試來覆蓋邏輯,string 會是比較好的選擇。
另一個為列舉,因為CheckIntranetOrInternet 方法的回傳值為內網或外網。
很多人會使用常數但我通常使用列舉來取代常數,特別是列舉可以清楚的規範且說明本身的意義

Result

這邊使用單元測試來測試邏輯,注意這邊並沒有覆蓋到所有邏輯。

[TestMethod]
public void CheckIntranetOrInternet_CheckInternet()
{
    Assert.AreEqual(NetworkManager.NetType.INTERNET, NetworkManager.CheckIntranetOrInternet("9.0.0.0"));
    Assert.AreEqual(NetworkManager.NetType.INTERNET, NetworkManager.CheckIntranetOrInternet("11.0.0.0"));
    Assert.AreEqual(NetworkManager.NetType.INTERNET, NetworkManager.CheckIntranetOrInternet("172.15.0.0"));
    Assert.AreEqual(NetworkManager.NetType.INTERNET, NetworkManager.CheckIntranetOrInternet("172.32.255.255"));
    Assert.AreEqual(NetworkManager.NetType.INTERNET, NetworkManager.CheckIntranetOrInternet("192.167.0.0"));
    Assert.AreEqual(NetworkManager.NetType.INTERNET, NetworkManager.CheckIntranetOrInternet("192.169.255.255"));
}
[TestMethod]
public void CheckIntranetOrInternet_CheckIntranet()
{
    Assert.AreEqual(NetworkManager.NetType.INTRANET, NetworkManager.CheckIntranetOrInternet("10.0.0.0"));
    Assert.AreEqual(NetworkManager.NetType.INTRANET, NetworkManager.CheckIntranetOrInternet("10.255.255.255"));
    Assert.AreEqual(NetworkManager.NetType.INTRANET, NetworkManager.CheckIntranetOrInternet("172.16.0.0"));
    Assert.AreEqual(NetworkManager.NetType.INTRANET, NetworkManager.CheckIntranetOrInternet("172.31.255.255"));
    Assert.AreEqual(NetworkManager.NetType.INTRANET, NetworkManager.CheckIntranetOrInternet("192.168.0.0"));
    Assert.AreEqual(NetworkManager.NetType.INTRANET, NetworkManager.CheckIntranetOrInternet("192.168.255.255"));
}