Web應用程式安全參考指引筆記 (Web Application Security Guide)

2023-07-09

筆記國家資通安全研究院106年Web應用程式安全參考指引(修訂)v2.1_1101231」關於 ASP.NET 的資安開發指引,並加上開發實務上的心得與經驗。

logo

逾越機關所定預期閒置時間或可使用期限時,系統應自動將使用者登出。

<system.web>
    <sessionState timeout ="30" />
</system.web>

實作登出機制時,主動清除 Session

Session.Abandon();

管理者介面限制存取來源或不允許遠端存取

if (null == HttpContext.Current.Request.ServerVariables["HTTP_VIA"])
{
    return HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
}
else
{
    return HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
}

需要注意的是:REMOTE_ADDR 的可靠性最高,HTTP_X_FORWARDED_FOR 有被竄改的風險。

軟體程序(process)及伺服器服務,以一般使用者權限執行,不以系統管理員或最高權限執行

使用 IIS APPPOOL\DefaultAppPool 而非 IIS_IUSRS,以確保最小權限。

資通系統產生之稽核紀錄(Audit Logs)應包含事件類型、發生時間、發生位置及任何與事件相關之使用者身分識別等資訊,並採用單一日誌紀錄機制,確保輸出格式之一致性

日誌紀錄宜考慮包含以下項目:

  1. 使用者ID,不可為個資類型,避免共用帳號之情況。
  2. 時間,系統應設定定期校時。
  3. 執行之功能或存取之資源名稱。
  4. 事件類型或優先等級。
  5. 執行結果或事件描述。
  6. 事件發生當下相關物件資訊。
  7. 網路來源與目的位址。
  8. 錯誤代碼,便於事件追查。
NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
if (UserLogin(this.TextBoxAccount.Text, this.TextBoxPassword.Text)){
    logger.Info(TextBoxAccount.Text + "登入成功!!");
}
else{
    logger.Warn(TextBoxAccount.Text + "登入失敗!!");
}

如果要將 Log 儲存於檔案,推薦儲存於 App_Data 資料夾中,預設上此資料夾的內容無法被要求下載。

依據稽核紀錄儲存需求,配置稽核紀錄所需之儲存容量。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        DriveInfo[] drives = DriveInfo.GetDrives();

        foreach (DriveInfo drive in drives)
        {
            Console.WriteLine($"Drive: {drive.Name}");
            Console.WriteLine($"Available space: {drive.AvailableFreeSpace} bytes");
            Console.WriteLine($"Total space: {drive.TotalSize} bytes");
            Console.WriteLine();
        }
    }
}

資通系統於稽核處理失效時,應採取適當之行動

重要系統:機關規定需要即時通報之稽核失效事件發生時,資通系統應於機關規定之時效內,對特定人員提出警告。

try
{
    DoSomething();

    string logMessage = TextBoxAccount.Text + " This is a log message ";
    logger.Info(logMessage);
}
catch (Exception e)
{
    string errorMessage = "稽核處理失效";
    SendEmailNotification(errorMessage);
}
using System.Net.Mail;

private void SendEmailNotification(string errorMessage)
{
    try
    {
        string emailSubject = "稽核處理失效";
        string emailBody = "發生以下錯誤:" + errorMessage;

        SmtpClient smtpClient = new SmtpClient();
        MailMessage mailMessage = new MailMessage();
        mailMessage.Subject = emailSubject;
        mailMessage.Body = emailBody;
        mailMessage.From = new MailAddress("[email protected]");
        mailMessage.To.Add("[email protected]");

        smtpClient.Send(mailMessage);
    }
    catch (Exception e)
    {
        Console.WriteLine("郵件發送失敗:" + e.Message);
    }
}

系統內部時鐘應依機關規定之時間週期與基準時間源進行同步

REM 確認伺服器與校時伺服器的時間差,正常情況為 0
w32tm /query /status

REM 立即與校時伺服器同步時間
w32tm /resync

對日誌紀錄進行適當保護及備份,避免未經授權存取

定期備份稽核紀錄到與原稽核系統不同之實體系統

使用 SQL Server 匯入 IIS Logs 進行分析

重要系統資料或紀錄留存雜湊值以確保完整性

CREATE TABLE LoginRecords (
    RecordID INT IDENTITY(1,1) PRIMARY KEY,
    Username NVARCHAR(50) NOT NULL,
    LoginTime DATETIME NOT NULL,
    IPAddress NVARCHAR(50) NOT NULL,
    HashValue NVARCHAR(100) NOT NULL
);
using System;
using System.Data;
using System.Data.SqlClient;
using System.Security.Cryptography;
using System.Text;

public class LoginRecordWriter
{
    private string connectionString = "YourConnectionString";

    public void WriteLoginRecord(string username, string ipAddress)
    {
        string loginTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        string hashValue = CalculateHash(username + loginTime + ipAddress);

        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            connection.Open();

            string insertQuery = "INSERT INTO LoginRecords (Username, LoginTime, IPAddress, HashValue) " +
                                 "VALUES (@Username, @LoginTime, @IPAddress, @HashValue)";

            using (SqlCommand command = new SqlCommand(insertQuery, connection))
            {
                command.Parameters.AddWithValue("@Username", username);
                command.Parameters.AddWithValue("@LoginTime", loginTime);
                command.Parameters.AddWithValue("@IPAddress", ipAddress);
                command.Parameters.AddWithValue("@HashValue", hashValue);

                command.ExecuteNonQuery();
            }
        }
    }

    private string CalculateHash(string input)
    {
        using (SHA256 sha256 = SHA256.Create())
        {
            byte[] bytes = Encoding.UTF8.GetBytes(input);
            byte[] hashBytes = sha256.ComputeHash(bytes);

            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                builder.Append(hashBytes[i].ToString("x2"));
            }

            return builder.ToString();
        }
    }
}

具備帳號鎖定機制,帳號登入進行身分鑑別失敗達5次後,至少15分鐘內不允許該帳號及來源IP繼續嘗試登入

...

採用圖形驗證碼(CAPTCHA)機制於身分鑑別及重要交易行為,以防範自動化程式之嘗試

對外服務網站可以使用 Google reCAPTCHA,內部網站需使用其他解決方案 ASP.NET MVC 實作登入驗證碼 (CAPTCHA)

資訊系統應遮蔽在鑑別過程中之資訊(如密碼),以防止未授權之使用者可能之窺探/使用

<label for="passwordInput" class="form-label">Password</label>
<input type="password" class="form-control" id="passwordInput" placeholder="Enter your password">

密碼添加亂數(Salt)進行雜湊函式(HASH Function)處理後,分別儲存亂數及雜湊後密碼

密碼在資料庫的保存必須為雜湊值,更理想的是儲存當作鹽的亂數以及加鹽雜湊後的值。

具有防範跨站腳本攻擊(XSS, Cross-Site Scripting)之措施

<configuration>
  <system.web>
    <httpRuntime encoderType="System.Web.Security.AntiXss.AntiXssEncoder,
    System.Web,
    Version=4.0.0.0,
    Culture=neutral,
    PublicKeyToken=b03f5f7f11d50a3a" />
  </system.web>
</configuration>
string result = AntiXssEncoder.HtmlEncoder(orgionMessage, true);

發生錯誤時,使用者頁面僅顯示簡短錯誤訊息及代碼,不包含詳細的錯誤訊息

<configuration>
  <system.web>
    <customErrors mode="RemoteOnly" defaultRedirect="ErrorPage.aspx">
      <error statusCode="404" redirect="404.aspx" />
      <error statusCode="500" redirect="500.aspx" />
    </customErrors>
  </system.web>
</configuration>

作業平台定期更新並關閉不必要服務及埠口(Port)

$connections = Get-NetTCPConnection
$ports = New-Object System.Collections.ArrayList

foreach ($connection in $connections) {
    $localPort = $connection.LocalPort
    $ports.Add($localPort) | Out-Null
}

$distinctPorts = $ports | Select-Object -Unique | Sort-Object -Descending

foreach ($port in $distinctPorts) {
    $port
}

相關連結

實踐 SSDLC 深入附表十《資通系統防護基準修正規定》DSCS 的饗宴 🍱