Web應用程式安全參考指引筆記 (Web Application Security Guide)
2023-07-09
筆記國家資通安全研究院「106年Web應用程式安全參考指引(修訂)v2.1_1101231」關於 ASP.NET 的資安開發指引,並加上開發實務上的心得與經驗。
逾越機關所定預期閒置時間或可使用期限時,系統應自動將使用者登出。
<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)應包含事件類型、發生時間、發生位置及任何與事件相關之使用者身分識別等資訊,並採用單一日誌紀錄機制,確保輸出格式之一致性
日誌紀錄宜考慮包含以下項目:
- 使用者ID,不可為個資類型,避免共用帳號之情況。
- 時間,系統應設定定期校時。
- 執行之功能或存取之資源名稱。
- 事件類型或優先等級。
- 執行結果或事件描述。
- 事件發生當下相關物件資訊。
- 網路來源與目的位址。
- 錯誤代碼,便於事件追查。
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
}