防禦性程式設計
此條目需要擴充。 (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.