資安專家透過程式碼審查快速發現漏洞。本文提供 10 個含安全缺陷的程式碼片段(C/C++/C#/Silverlight),測試你的漏洞識別能力,並詳細解析每個錯誤的成因與修正方向。
void func(char *s1, char *s2) {
char d[32];
strncpy(d, s1, sizeof d - 1);
strncat(d, s2, sizeof d - 1); // 錯誤:未扣除 strncpy 已用空間
}
解析:strncat 最後參數為剩餘緩衝區大小,但 strncpy 已佔用部分空間,導致溢出。strncpy/strncat 比 strcpy/strcat 更難正確使用。VC++ 2005+ 會發出 C4996/C6053 警告。
int main(int argc, char* argv[]) {
char d[32];
if (argc==2) strcpy(d, argv[1]); // 風險視執行語境而定
}
解析:標準 EXE 非安全性錯誤(攻擊自己);但若為 SYSTEM 權限服務或 Linux setuid root 程式,即成本地提權漏洞。需掌握部署語境才能判斷。
byte[] GetKey(UInt32 keySize) {
byte[] key = null;
try {
key = new byte[keySize];
RNGCryptoServiceProvider.Create().GetBytes(key);
}
catch (Exception e) {
Random r = new Random(); r.NextBytes(key); // 弱隨機數
}
return key;
}
解析:(1) 密碼學強隨機數失敗時回退 Random 可預測;(2) catch (Exception) 掩蓋真實錯誤(含緩衝區溢出等嚴重錯誤)。SDL 要求密碼學隨機數且禁止捕獲所有例外。
void func(const char *s) {
if (!s) return;
char t[3];
memcpy(t, s, 3);
t[3] = 0; // 陣列只有 0-2 索引
}
解析:純編碼錯誤,攻擊者無控制權非安全性漏洞。但若索引可控(t[i]=0),即成任意寫入漏洞。/analyze 發出 C6201 警告。
private static Monkey[] defaultMonkeys = new[] { new RhesusMonkey() };
private IEnumerable<Monkey> monkeys = null;
public IEnumerable<Monkey> Monkeys {
get {
if (monkeys == null) monkeys = MonkeysReady() ? PopulateMonkeys() : defaultMonkeys;
return monkeys;
}
}
解析:調用者可將 IEnumerable<Monkey> 轉型為 Monkey[] 修改靜態預設陣列,影響其他實例。解決:快取 ReadOnlyCollection<T> 或真正唯讀儲存。
string lastLogin = Request["LastLogin"]; // 查詢字串優先於 Cookie
if (String.IsNullOrEmpty(lastLogin)) { /* 設定 Cookie */ }
else { Response.Write("Welcome back! " + lastLogin); } // 直接輸出未編碼輸入
解析:Request["key"] 優先讀取 QueryString,攻擊者可注入 ``。需對輸出進行 HTML 編碼。
XmlNode node = doc.SelectSingleNode("//products/product[id/text()='" + Request["itemId"] + "']/price");
解析:未驗證輸入直接拼接 XPath 查詢,同 SQL 注入原理。需參數化或嚴格驗證輸入。
public override string CreateSessionID(HttpContext context) {
lock (lockObject) {
Int32? last = (int?)context.Application["LastSessionId"];
last = (last ?? 0) + 1;
context.Application["LastSessionId"] = last;
return last.ToString(); // 連續整數,極易猜測
}
}
解析:(1) HttpContext.Application 不跨伺服器共享,Web Farm 會產生衝突;(2) 連續整數 Session ID 極易被劫持。應用 GUID 或大範圍隨機字串。
if (validPassword == null) { errorMessage = "Invalid user name"; return false; }
else if (validPassword != password) { errorMessage = "Incorrect password"; return false; }
解析:(1) 不同錯誤訊息讓攻擊者枚舉合法帳號;應統一為「帳號或密碼錯誤」。(2) 資料庫存明文密碼,應儲存雜湊值(如 bcrypt/PBKDF2)。
bool verifyCode(string discountCode) {
SHA1Managed hash = new SHA1Managed(); // SHA-1 已被 SDL 禁用
byte[] codeHash = hash.ComputeHash(Encoding.Unicode.GetBytes(discountCode));
byte[] secretCode = { ... }; // 硬編碼雜湊值
// 元素比較...
WebServiceSoapClient client = new WebServiceSoapClient();
client.ApplyDiscountCode(); // 客戶端直接呼叫特權操作
return true;
}
解析:(1) 使用已淘汰 SHA-1,應改用 SHA256Managed;(2) 未加鹽雜湊易遭彩虹表破解;(3) 核心錯誤:Silverlight 運行於客戶端瀏覽器,攻擊者可附加偵錯器竄改邏輯、繞過驗證、直接呼叫 Web Service。絕不在客戶端執行特權決策或嵌入機密。
| 分數 | 評語 |
|---|---|
| 15+ | 資安專家級 |
| 11-14 | 優秀,應用於夥伴程式碼審查 |
| 7-10 | 不錯,優於 50% Web 開發者 |
| 4-6 | 需加強,建議研讀專業資安書籍 |
| 0-3 | 建議暫離編輯器 |
持續練習程式碼審查,建立安全開發生命週期 (SDL) 思維,是資安防禦最有效的投資。