---
# System prepended metadata

title: 資訊安全處理紀錄 (DotNet Security)

---

# 資訊安全處理紀錄 (DotNet Security)

## SQL Injection

>注入攻擊實作：
>在 Textbox 內輸入字串內容 ` ' OR '1'='1 `
>或是 ` '; DROP TABLE Employees; `

**防範措施：**

- 參數化查詢。而不是使用「組字串」的方式來執行SQL。
- 對輸入的數據進行驗證，以確保數據符合預期的格式或範圍。

>[什麼是 SQL Injection？該如何避免？](https://www.explainthis.io/zh-hant/swe/sql-injection)

## Poor Error Handling: Unhandled Exception

在程式碼中，應該改善錯誤處理，以避免未處理的例外情況，並提供更友好和有用的錯誤訊息。

**防範措施：**

- 在程式碼中使用 try-catch 區塊，以捕獲可能發生的例外情況。
- 在 catch 區塊中，應該適當地記錄錯誤訊息，並向使用者提供有用的錯誤訊息，以便他們理解問題所在。
- 可以使用 ASP.NET 的全局錯誤處理事件（Application_Error）來捕獲未處理的例外情況，並將它們記錄下來或顯示自定義的錯誤頁面。

這些修改將幫助改善網頁伺服器的配置安全性和錯誤處理，使程式碼更堅固和安全。請按照上述建議進行修改後，重新審查程式碼並測試確保正常運作。

## Cross-Site Scripting: Reflected

跨站腳本攻擊(Cross-Site Scripting, XSS)：反射型

>黑箱掃描工具的攻擊實作：
>
>- `https://xxx.com/InventoryMgmt/Home/(A(WeBiNsPeCt))/Index`
>- `https://xxx.com/InventoryMgmt/Home/(A())/Index`
>
>正常的網址應該是
>
>- `https://xxx.com/InventoryMgmt/Home/Index`

**防範措施：**
在 Global.asax.cs 內加入程式嗎

```csharp=
protected void Application_BeginRequest(object sender, EventArgs e)
{
    // 到這時，Url 已被改成沒包含 SessionId
    // 但它的值會放在 Response._appPathModifier 變數之中
    FieldInfo appPathModifierFieldInfo = Context.Response.GetType().GetField("_appPathModifier", BindingFlags.NonPublic | BindingFlags.Instance);
    object appPathModifier = appPathModifierFieldInfo.GetValue(Context.Response);
    if (appPathModifier != null)
    {
        // Url 中有 SessionId
        throw new HttpException(404, "Not found");
    }

    // deny HTTP GET with request body
    if (Request.HttpMethod == "GET" && Request.ContentLength > 0)
    {
        throw new HttpException(403, "Forbidden");
    }
}
```

>- [為什麼 Url 中有 (A(XXXX)) 在 ASP.NET 中，卻不會噴 404 或是錯誤，反而回正常的頁面(200)呢?](https://rainmakerho.github.io/2020/11/11/aspnet-cookieless-url/)

## Reflected XSS All Clients

主要透過用戶發出惡意的請求，倘若後端沒有過濾而直接將結果回傳前端的話，就有可能執行到惡意的程式碼。

**防範措施：**

```csharp=
using Ganss.Xss; // NuGet 加入 HtmlSanitizer 套件

#region XSS 的第一道防線：Sanitization (AntiXSS，HtmlSanitizer)

/// <summary>
/// 通過使用 HtmlSanitizer 來過濾輸入字串，再 HtmlEncode 以防止跨站腳本（XSS）攻擊。
/// </summary>
public static string SanitizeHtmlEncode(string inputStr)
{
    if (string.IsNullOrWhiteSpace(inputStr))
        return string.Empty;

    var sanitizer = new HtmlSanitizer();
    //sanitizer.AllowedAttributes.Add("class");
    //sanitizer.AllowedAttributes.Add("id");
    //sanitizer.AllowedSchemes.Add("mailto"); // 允許 <a href="mailto:"

    // 使用 HtmlSanitizer 進行消毒處理 (https://github.com/mganss/HtmlSanitizer)
    string sanitized = sanitizer.Sanitize(inputStr);
    string encoded = HttpUtility.HtmlEncode(sanitized);

    return encoded;
}

/// <summary>
/// 在數據輸出到網頁時進行欄位內容過濾，以防止儲存型跨網站指令碼（Stored XSS）攻擊。
/// </summary>
public static DataTable SanitizeHtmlEncodeInDataTable(DataTable dataTable)
{
    foreach (DataRow row in dataTable.Rows)
    {
        foreach (DataColumn column in dataTable.Columns)
        {
            if (row[column] != DBNull.Value && !column.ReadOnly)
            {
                row[column] = SanitizeHtmlEncode(row[column].ToString());
            }
        }
    }
    return dataTable;
}

#endregion XSS 的第一道防線：Sanitization (AntiXSS，HtmlSanitizer)
```

## Reflected XSS Specific Clients

**防範措施：**
同 Reflected XSS All Clients

## Stored XSS

被保存在資料庫中的 Javascript 引起的攻擊稱為 Stored XSS。
最常見的就是文章、留言等，因為用戶可以任意輸入內容，若沒有檢查，則 `<script>` 等標籤就會被視為正常的 HTML 做執行。

**防範措施：**
Reflected XSS All Clients 的 `SanitizeHtmlEncodeInDataTable` 方法

## Code Injection

>漏洞描述：
>用 Invoke 動態執行 Web service URL

**防範措施：**
透過加入 Web 參考，以靜態的方式連線 Web service

## Cross-Frame Scripting

>攻擊實作：
>[Clickjacking 點擊劫持攻擊](https://blog.huli.tw/2021/09/26/what-is-clickjacking/)

**防範措施：**

- [X-Frame-Options](https://a42033.gitbooks.io/system/content/security/user/X_Frame_Options.html)

```xml
<add name="X-Frame-Options" value="SAMEORIGIN" />
```

- 內容安全策略(Content-Security-Policy, CSP)

```xml
<add name="Content-Security-Policy" value="child-src 'self';" />
```

```xml!
<add name="Content-Security-Policy" value="frame-ancestors 'self' tw.yahoo.com www.google.com;" />
```

iframe_test.html 舉證、驗證

```html=
<!DOCTYPE html>
<html>
<head>
    <title>iframe test</title>
</head>
<body>
    <h1>iframe test</h1>
    <iframe src="要嵌入的網頁網址" width="600px" height="400px" frameborder="0" scrolling="no"></iframe>
</body>  
</html>
```

## HTML5: Missing Content Security Policy

HTML5 引入了 Content-Security-Policy（CSP），這是一種用於增強網站安全性的機制。
當網站沒有設定 Content-Security-Policy 時，就稱為 Missing Content Security Policy。

**防範措施：**

- [內容安全策略(Content-Security-Policy, CSP)](https://content-security-policy.com/)

>Google 提供的網站：[CSP Evaluator](https://csp-evaluator.withgoogle.com/)，它會偵測你的 CSP 是否有錯誤，以及是不是安全。
>
>[XSS 防禦 - CSP script-src 設定](https://blog.darkthread.net/blog/csp-script-src/)

## HTML5: Overly Permissive Message Posting Policy

HTML5 的新機制 Message Posting Policy 主要是指跨文件通訊的設定規則。這個機制允許使用 Script 腳本在不同窗口或框架之間傳送消息。在設定這個機制時，使用者可以指定目標窗口的來源，以確保只有特定合法來源的窗口才能接收消息。
當 Message Posting Policy 過於寬鬆時，也就是認為了使用不當而導致安全風險的情況下，就稱為 Overly Permissive Message Posting Policy。

JavaScript/TypeScript:

```javascript
o.contentWindow.postMessage(message, '*');
```

使用 `*` 做為目標來源值，代表 Script 會傳送訊息至視窗而不論其來源。

**防範措施：**

- 不要使用萬用字元 `*`，改為指定目標窗口的來源。

## HTML5: CORS Functionality Abuse

同 Cache Management: Headers

## Web Server Misconfiguration: Insecure Content-Type Setting

**防範措施：**

```xml
<add name="X-Content-Type-Options" value="nosniff" />
```

## Insecure Transport: HSTS not Set

HTTP Strict-Transport-Security 回應標頭 (HSTS) 告知瀏覽器該站點應僅使用 HTTPS 訪問，並且所有將來的 HTTP 訪問應自動轉換為 HTTPS。

**防範措施：**

```xml
<add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
```

## ASP.NET Misconfiguration: Missing Error Handling

Web.config 檢測到

```xml
<customErrors mode="Off" />
```

**防範措施：**

```xml
<customErrors mode="On" defaultRedirect="Errors/ErrorPage.aspx">
  <error statusCode="404" redirect="Errors/NotFound.aspx" />
  <error statusCode="500" redirect="Errors/ServerError.aspx" />
</customErrors>
```

## Compliance Failure: Missing Privacy Policy

對掃描範圍內可存取的所有網頁進行採樣，以取得通常構成隱私權政策聲明的文字內容。
**解決方法：**

- 新增隱私權政策（Privacy Policy）的靜態頁面。

## Cache Management: Headers

```xml
<!-- CORS_Origins 因應資訊安全「同源政策」，請加入主機網域網址(含開發機, 測試機, 正式機)。若未加入會顯示 HTTP ERROR 403 -->
<add key="CORS_Origins" value="http://主機網域網址1, https://主機網域網址1, http://主機網域網址2, https://主機網域網址2" />

```

```csharp=
protected void Application_BeginRequest(object sender, EventArgs e)
{
    /** 修復資訊安全漏洞 
     * Cache Management: Headers
     * HTML5: CORS Functionality Abuse
     */
    string cors_origins = ConfigurationManager.AppSettings["CORS_Origins"] ?? "";

    // 允許的來源列表
    var allowedOrigins = new List<string>(cors_origins.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
    // 去除字串前後的空白
    allowedOrigins = allowedOrigins.ConvertAll(o => o.Trim());

    HttpContext context = HttpContext.Current;
    string origin = context.Request.Headers["Origin"];

    // 如果是非跨域請求（沒有 Origin），跳過 CORS 驗證
    if (string.IsNullOrEmpty(origin))
    {
        return; // 直接返回，不執行 CORS 邏輯
    }

    // 檢查來源
    if (allowedOrigins.Contains(origin))
    {
        // 設置 CORS 標頭
        context.Response.AddHeader("Access-Control-Allow-Origin", origin);
        context.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
        context.Response.AddHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
    }
    else
    {
        context.Response.StatusCode = 403; // 禁止訪問
        //Logger.Warn($"未授權的 CORS 來源: {origin}");
        context.Response.End();
        return;
    }

    // 處理 OPTIONS 方法
    if (context.Request.HttpMethod == "OPTIONS")
    {
        context.Response.End();
    }           
}
```

## Cache Management: Insecure Policy

不安全的快取策略可能允許攻擊者進行內容欺騙或資訊竊取攻擊。

>瀏覽器 F12 進入開發人員畫面 => 選擇 Network => 選擇網頁名稱
>可看到 Response Headers 的 Cache-Control: 內容為 no-cache, no-store

**防範措施：**

- ASP.NET MVC 在 Global.asax.cs 內加入

```csharp=
protected void Application_PreSendRequestHeaders(object sender, EventArgs e)
{
    HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    HttpContext.Current.Response.Cache.SetNoStore();
}
```

- ASP.NET WebForms 在 Web.config 內加入

```xml=
<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Cache-Control" value="no-cache, no-store, must-revalidate" />
      <add name="Pragma" value="no-cache" />
      <add name="Expires" value="0" />
    </customHeaders>
  </httpProtocol>
</system.webServer>
```

## HTML5: Cross-Site Scripting Protection

**防範措施：**

```xml
<add name="X-XSS-Protection" value="1; mode=block" />
```

## Web Server Misconfiguration: Server Error Message

Web 伺服器配置不當（Web Server Misconfiguration）是指在設置和管理 Web 伺服器（如 IIS）時出現的錯誤或不正確的配置。
比如 HTTP Response Headers 配置。

## CSRF

>[零基礎資安系列（一）-認識 CSRF（Cross Site Request Forgery）](https://tech-blog.cymetrics.io/posts/jo/zerobased-cross-site-request-forgery/)
>想像你到一家餐廳吃飯，陌生人`(駭客)`拿了一張有你桌號的菜單`(Request)`點餐之後給老闆`(Server)`，結果老闆問也不問便收了菜單並將帳記到了你的身上，這就是 CSRF 的基礎概念。

>[如何防範？](https://www.explainthis.io/zh-hant/swe/what-is-csrf#csrf-%E9%98%B2%E7%A6%A6%E6%96%B9%E6%B3%95)
>1. 加上驗證
>2. 不要用 `GET` 請求來做關鍵操作
>3. 檢查 Referrer
>4. 使用 Anti-XSRF Token
>5. 瀏覽器本身防護 - SameSite cookies

**實際的防範措施：**

- 不要使用 `GET` 請求來做關鍵操作

```csharp=
if (!IsPostBack)
{
}
else
{
    // CSRF 防護：限制僅接受 POST 請求，防止 GET 請求觸發狀態變更。
    if (Request.HttpMethod != "POST")
    {
        throw new InvalidOperationException($"資訊安全：{Request.HttpMethod} 為不允許的請求方法。僅接受 POST 請求。");
    }
}
```

- [使用 Anti-XSRF Token](https://cheatsheetseries.owasp.org/cheatsheets/DotNet_Security_Cheat_Sheet.html#asp-net-web-forms-guidance)

```csharp==
private const string AntiXsrfTokenKey = "__AntiXsrfToken";
private const string AntiXsrfUserNameKey = "__AntiXsrfUserName";
private string _antiXsrfTokenValue;

protected void Page_Init(object sender, EventArgs e)
{
	// 從請求的 Cookie 中獲取 Anti-XSRF Token
	var requestCookie = Request.Cookies[AntiXsrfTokenKey];
	if (requestCookie != null && Guid.TryParse(requestCookie.Value, out Guid requestCookieGuidValue))
	{
		// 如果 Cookie 中存在有效的 Anti-XSRF Token，則使用該 Token
		_antiXsrfTokenValue = requestCookie.Value;
		Page.ViewStateUserKey = _antiXsrfTokenValue;
	}
	else
	{
		// 如果 Cookie 中不存在 Token，則生成新的 Anti-XSRF Token 並保存到 Cookie
		_antiXsrfTokenValue = Guid.NewGuid().ToString("N");
		Page.ViewStateUserKey = _antiXsrfTokenValue;
		var responseCookie = new HttpCookie(AntiXsrfTokenKey)
		{
			HttpOnly = true, // 設置 HttpOnly 屬性以防止客戶端腳本訪問 Cookie
			Value = _antiXsrfTokenValue
		};
		// 如果需要 SSL 且當前連接是安全的，則設置 Cookie 的 Secure 屬性
		if (FormsAuthentication.RequireSSL && Request.IsSecureConnection)
		{
			responseCookie.Secure = true;
		}
		// 將新的 Cookie 添加到回應中
		Response.Cookies.Set(responseCookie);
	}
}

protected void Page_PreLoad(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		// 設置 ViewState 中的 Anti-XSRF Token 和使用者名稱
		ViewState[AntiXsrfTokenKey] = Page.ViewStateUserKey;
		ViewState[AntiXsrfUserNameKey] = Context.User.Identity.Name ?? string.Empty;
	}
	else
	{
		// 驗證 ViewState 中的 Anti-XSRF Token 和使用者名稱
		if ((string)ViewState[AntiXsrfTokenKey] != _antiXsrfTokenValue ||
		   (string)ViewState[AntiXsrfUserNameKey] != (Context.User.Identity.Name ?? string.Empty))
		{
			throw new InvalidOperationException("Validation of Anti-XSRF token failed.");
		}
	}
}
```

- 調整程式碼執行流程

```csharp=
//string logonid = HttpUtility.HtmlEncode(Request.QueryString["logonid"] ?? "");
if (!IsPostBack)
{
    string logonid = HttpUtility.HtmlEncode(Request.QueryString["logonid"] ?? "");
    string NTUser = Utility.DecryptNTUser(logonid);
```

## Data Filter Injection

使用 DataTable.Select 方法進行查詢時，當查詢條件是通過**字符串拼接**產生的（如 "ID='" + id + "'"），就存在 Data Filter Injection 的風險。

>攻擊場景：
>假設攻擊者將 id 設置為 `1' OR '1'='1`.
>查詢語句將被解析為：ID='1' OR '1'='1'，這樣會返回所有行，從而泄露不應該訪問的數據。

```csharp=
// 資訊安全漏洞：Data Filter Injection
//DataRow[] dr = dt.Select("ID='" + id + "'");
// 修復：改用 LINQ 查詢
DataRow[] dr = dt.AsEnumerable()
    .Where(r => r.Field<decimal>("ID").ToString() == id)
    .ToArray();
```

## Persistent Connection String

## Client Potential XSS

**防範措施：**

- 前端使用 [DOMPurify](https://github.com/cure53/DOMPurify) 避免 XSS 攻擊

```javascript=
$('.inject-defense').each(function () {
    var inputVal = $(this).val();
    if (inputVal) {
        var cleanHTML = DOMPurify.sanitize(inputVal, {
            USE_PROFILES: {
                html: false
            }
        });
        $(this).val(cleanHTML);
    }
});
```

## Privacy Violation: Autocomplete

**防範措施：**
`<asp:TextBox>` 和 `<input type="text">` 的元素都加上 `autocomplete="off"`

## Privacy Violation

>[Checkmarx | 使用 DefaultRequestHeaders.Authorization 卻被 Checkmarx 判斷有 Privacy Violation 的 Issue](https://rainmakerho.github.io/2022/11/25/checkmarx-headers-authorization-privacy-violation/)

**防範措施：**
將變數名稱 `string userAccount` 調整成 `string user`。

## Insufficient Connection String Encryption

## Path Traversal/Stored Path Traversal

```csharp=
XmlReaderSettings settings = new XmlReaderSettings
{
    DtdProcessing = DtdProcessing.Prohibit, // 禁用 DTD
    XmlResolver = null, // 禁用外部資源解析
    IgnoreWhitespace = true, // 忽略空白
    IgnoreComments = true // 忽略註解
};
XmlDocument xmlDoc = new XmlDocument();
using (var reader = XmlReader.Create(new StringReader(vendorData), settings))
{
    xmlDoc.Load(reader);
}
```

## Missing HSTS Header

HTTP Strict-Transport-Security 回應標頭 (HSTS) 告知瀏覽器該站點應僅使用 HTTPS 訪問，並且所有將來的 HTTP 訪問應自動轉換為 HTTPS。

**防範措施：**

```xml
<add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
```

## SSL Verification Bypass

>```csharp
>ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
>```
>
>這段程式碼會禁用所有 SSL 憑證的驗證，無論伺服器的憑證是否可信都會自動接受。這可能導致攻擊者進行中間人攻擊 (MITM)，攔截和篡改 HTTPS 傳輸中的敏感資料。

**修復方法：** 移除不安全的憑證驗證邏輯

```csharp
ServicePointManager.ServerCertificateValidationCallback = null;
```

## Often Misused: File Upload

**防範措施：**

- 前端對上傳的檔案進行副檔名限制

```html
<asp:FileUpload ID="oFileUpload" runat="server" accept=".xlsx,.xls"  />
```

- 後端對上傳的檔案進行副檔名、content-type 檢查

```csharp=
// 取得檔案副檔名
string fileExtension = Path.GetExtension(oFileUpload.FileName);
// 取得 MIME 類型
string contentType = oFileUpload.PostedFile.ContentType;
if (fileExtension.ToLower() != ".xls" &&
    fileExtension.ToLower() != ".xlsx" &&
    contentType != "application/vnd.ms-excel" &&
    contentType != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
    Utility.Alert("僅允許上傳 Excel (.xls, .xlsx) 檔案。", this, false);
    return;
}
```
