防禦性程式設計
此條目需要擴充。 (2017年12月6日) |
此條目需要補充更多來源。 (2017年12月6日) |
防禦性程式設計(Defensive programming)是防禦式設計的一種具體體現,它是為了保證,對程式的不可預見的使用,不會造成程式功能上的損壞。它可以被看作是為了減少或消除墨菲定律效力的想法。防禦式程式設計主要用於可能被濫用,惡作劇或無意地造成災難性影響的程式上。[1]
主要概念:若常式收到錯誤的資料,即便錯誤資料是由其他常式出錯所造成,此常式也不會受到傷害。[2]
防禦性程式設計通常通過以下途徑,從而提高軟件和原始碼的質素:
- 提高工程質素——減少bug和問題
- 提高原始碼可讀性—— 原始碼應該變得可讀且可理解,並且能經受代碼審計。
- 讓軟件能通過預期的行為來處理不可預期的用戶操作。
值得注意的是,過度的防禦性程式設計可能會預防不可能會發生的錯誤,這樣將導致執行時間與維護的損耗。當原始碼中擁有過多異常捕捉和例外處理,這有可能導致結果不正確或者被隱藏。
安全編程
防禦性程式設計有時也被電腦科學家稱為安全編程(Secure programming)。潛在的軟件缺陷可能會被黑客利用,而進行代碼注入,阻斷服務攻擊或其他攻擊。
防禦性程式設計與非防禦性程式設計之間的區別在於,程式設計師不會對特定的函數呼叫或庫的使用情況做假設。下面是一個例子:
int risky_programming(char *input){
char str[1000+1]; // 1000为内容,1为空终止字符串
// ...
strcpy(str, input); // 复制输入
// ...
}
當輸入超過1000個字元時,該函數將會崩潰。一些新手程式設計師可能並不會覺得這是個問題,因為沒有用戶會輸入這麼長的字串。實行防禦性程式設計的程式設計師不會允許這樣的錯誤,因為這段程式包含一個已知的bug,一個可能會導致緩衝區溢位攻擊的漏洞。下面這個例子是一個解決方案:
int secure_programming(char *input){
char str[1000+1]; // 1000为内容,1为空终止字符串
// ...
// 限制越界拷贝。
strncpy(str, input, sizeof(str));
// 当最后一个字符不为空终止字符串,我们可以选择只处理未越界的内容,或者直接停止程序
str[sizeof(str) - 1] = '\0';
// ...
}
進攻式編程
進攻式編程(Offensive programming )是防禦式程式設計( defensive programming)的子集,其思想為:在重點的代碼處不應該一些錯誤進行處理。在實踐中,只有來自於軟件外部的錯誤應該被處理(如:用戶的輸入);理論上,軟件自身和相關可控的數據是可信的。
可信內部數據的校驗
過度防禦式程式設計
const char* trafficlight_colorname(enum trafficlight_color c) {
switch (c) {
case TRAFFICLIGHT_RED: return "red";
case TRAFFICLIGHT_YELLOW: return "yellow";
case TRAFFICLIGHT_GREEN: return "green";
}
return "black"; //万一发生其他输入值时,使用信号灯将不亮
}
進攻式編程
const char* trafficlight_colorname(enum trafficlight_color c) {
switch (c) {
case TRAFFICLIGHT_RED: return "red";
case TRAFFICLIGHT_YELLOW: return "yellow";
case TRAFFICLIGHT_GREEN: return "green";
}
assert(0); // 认为这种情况不可能发生
}
可信的軟件組件
過度防禦式程式設計
if (is_legacy_compatible(user_config)) {
// 策略:新代码可能不完全兼容“用户配置”
new_code(user_config);
} else {
// 策略:新代码操作“用户配置”,可能出会错
// 一旦出错时,,使用旧代码处理
if (new_code(user_config) != OK) {
old_code(user_config);
}
}
進攻式編程
// 完全信任新代码
new_code(user_config);
技術層面
下面是常見的防禦式程式設計的技術。
理性地重用代碼
如果原始碼是廣為利用地且經得起測試的,那麼重用代碼將有助於減少bug。
然而,重用代碼往往不是最好的方案,越是底層的代碼(往往指框架),重用代碼帶來的潛在危害就越是大。重用過於複雜的代碼將大大地增加代碼的複雜度。
遺留代碼
在重用舊代碼、包、API、組態等事物前,我們必須思考這些事物是否值得被重用,思考這些是否會造成遺留問題。
遺留問題的產生,往往是因為舊的設計無法適應當今的需求,尤其舊的設計往往不是為了當今需求,因此開發和測試可能存在缺陷。
參見
參考文獻
- ^ 1.3 什么是防御性编程?. 51CTO.com. 2008-08-29 [2013-03-16]. (原始內容存檔於2013-04-06).
- ^ Steve McConnell. 軟體建構之道. ISBN 9789866800115.