| 類型 | 格式 | 遮罩範例 | 保留位數 |
|---|---|---|---|
| 手機 (台) | 09XX-XXX-XXX | 09XX-XXX-XXX → 09** ***** 123 | 前 3 碼 + 後 3 碼 |
| 手機 (陸) | 1XX-XXXX-XXXX | 138****1234 | 前 3 碼 + 後 4 碼 |
| 身分證 (台) | A123456789 | A1********* | 前 2 碼 |
| 身分證 (陸) | 18 碼 | 110105********123X | 前 6 碼 + 後 4 碼 |
| 信用卡 | 16 碼 | 4242 **** **** 1234 | 前 4 碼 + 後 4 碼 |
| user@domain.com | u***@domain.com | 首字元 + 網域 |
using System;
using System.Text.RegularExpressions;
public static class MaskHelper
{
///
/// 依規則遮罩字串
///
/// 原始字串
/// 保留前 N 碼
/// 保留後 N 碼
/// 遮罩字元 (預設 '*')
/// 最小遮罩長度 (不足則不遮罩)
public static string Mask(string input, int keepStart, int keepEnd, char maskChar = '*', int minMask = 1)
{
if (string.IsNullOrEmpty(input)) return input;
int len = input.Length;
int maskLen = len - keepStart - keepEnd;
if (maskLen < minMask) return input; // 太短不遮罩
var sb = new StringBuilder(len);
sb.Append(input.Substring(0, keepStart));
sb.Append(maskChar, maskLen);
sb.Append(input.Substring(len - keepEnd));
return sb.ToString();
}
///
/// 手機號遮罩 (自動偵測台/陸)
///
public static string MaskPhone(string phone)
{
if (string.IsNullOrEmpty(phone)) return phone;
phone = Regex.Replace(phone, @"\D", ""); // 移除非數字
if (phone.StartsWith("09") && phone.Length == 10) // 台灣
return Mask(phone, 3, 3);
if (phone.StartsWith("1") && phone.Length == 11) // 中國大陸
return Mask(phone, 3, 4);
return phone; // 無法識別格式
}
///
/// Email 遮罩
///
public static string MaskEmail(string email)
{
if (string.IsNullOrEmpty(email)) return email;
int at = email.IndexOf('@');
if (at <= 1) return email;
return email[0] + new string('*', at - 1) + email.Substring(at);
}
///
/// 信用卡遮罩 (保留前4後4,每4碼空格)
///
public static string MaskCreditCard(string card)
{
if (string.IsNullOrEmpty(card)) return card;
card = Regex.Replace(card, @"\D", "");
if (card.Length != 16) return card;
return $"{card.Substring(0,4)} **** **** {card.Substring(12,4)}";
}
}
-- 檢視表/函數遮罩
CREATE FUNCTION dbo.MaskPhone(@phone VARCHAR(20))
RETURNS VARCHAR(20)
AS BEGIN
DECLARE @clean VARCHAR(20) = REPLACE(REPLACE(REPLACE(@phone, '-', ''), ' ', ''), '+886', '0');
IF LEN(@clean) = 10 AND @clean LIKE '09%'
RETURN LEFT(@clean, 3) + REPLICATE('*', 4) + RIGHT(@clean, 3);
IF LEN(@clean) = 11 AND @clean LIKE '1%'
RETURN LEFT(@clean, 3) + REPLICATE('*', 4) + RIGHT(@clean, 4);
RETURN @clean;
END
[AttributeUsage(AttributeTargets.Property)]
public class MaskAttribute : Attribute
{
public int KeepStart { get; set; }
public int KeepEnd { get; set; }
public char MaskChar { get; set; } = '*';
}
public class UserDto
{
public string Name { get; set; }
[Mask(KeepStart = 3, KeepEnd = 3)]
public string Phone { get; set; }
[Mask(KeepStart = 1, KeepEnd = 0)] // Email 特殊處理
public string Email { get; set; }
}
// 序列化時自動遮罩 (System.Text.Json)
public class MaskJsonConverter : JsonConverter
{
public override string Read(...) => throw new NotSupportedException();
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
var prop = options.GetPropertyMetadata(); // 需自行實作取得屬性
if (prop?.GetCustomAttribute() is { } attr)
value = MaskHelper.Mask(value, attr.KeepStart, attr.KeepEnd, attr.MaskChar);
writer.WriteStringValue(value);
}
}
敏感資訊遮罩核心在於 **`KeepStart` + `KeepEnd` + `MaskChar` 參數化**,配合 **Regex 正規化輸入**、**最小遮罩長度保護**、**屬性標記自動化**。前端/後端/資料庫三層同步實施,並建立**遮罩規則表**統一管理,即可滿足個資法 (GDPR/PDPA) 合規與資安稽核需求。