首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

克服 C 語言 gets 函數緩衝區溢位漏洞的安全替代方案

     編輯:關於C

gets() 函數危險性分析

gets() 是 C 標準庫中最危險的輸入函數之一,已在 C11 標準中移除:


char *gets(char *buf);  // 無長度限制!

// 危險示範
char str[30];
gets(str);  // 輸入超過 29 字元即緩衝區溢位 → 未定義行為

漏洞根源

  • 無緩衝區大小參數,無法檢查輸入長度
  • 遇換行符才停止,惡意輸入可覆蓋堆疊返回位址
  • 歷史著名漏洞 (如 Morris Worm) 多利用此弱點

安全替代方案對比

推薦方案一:標準 fgets() 包裝


#include 
#include 

// 安全讀取一行,自動移除換行符
char *safe_gets(char *buf, size_t size) {
    if (fgets(buf, size, stdin) == NULL)
        return NULL;
    
    // 移除結尾換行符
    size_t len = strlen(buf);
    if (len > 0 && buf[len-1] == '\n')
        buf[len-1] = '\0';
    
    return buf;
}

// 使用範例
int main() {
    char str[30];
    while (safe_gets(str, sizeof(str))) {
        printf("輸入: %s\n", str);
    }
}

推薦方案二:動態記憶體版本 (無長度限制)


#include 
#include 
#include 

// 動態擴充緩衝區讀取任意長度行
char *dynamic_gets(void) {
    size_t capacity = 128;
    size_t len = 0;
    char *buf = malloc(capacity);
    if (!buf) return NULL;
    
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {
        if (len + 1 >= capacity) {
            capacity *= 2;
            char *new_buf = realloc(buf, capacity);
            if (!new_buf) { free(buf); return NULL; }
            buf = new_buf;
        }
        buf[len++] = (char)ch;
    }
    
    if (len == 0 && ch == EOF) { free(buf); return NULL; }
    buf[len] = '\0';
    return buf;  // 呼叫者需 free()
}

推薦方案三:固定緩衝區強化版


// 修正原文缺陷:正確處理 EOF、緩衝區滿、輸入殘留
char *const Gets_fixed(char *const array, int maxlen) {
    if (!array || maxlen <= 0) return NULL;
    
    int i = 0, ch;
    while (i < maxlen - 1) {  // 保留空間給 \0
        ch = getchar();
        if (ch == EOF) {
            if (i == 0) return NULL;
            break;
        }
        if (ch == '\n') break;
        array[i++] = (char)ch;
    }
    array[i] = '\0';
    
    // 關鍵:清除輸入緩衝區殘留字元
    if (ch != '\n' && ch != EOF)
        while ((ch = getchar()) != '\n' && ch != EOF);
    
    return array;
}

原文程式碼缺陷分析

  1. 未檢查 malloc 失敗後的返回值使用:if(!str) return 0; 但後續仍操作 str
  2. 靜態區域變數:static char* str 導致非重入、執行緒不安全
  3. 未清理輸入緩衝區:輸入超過 maxlen 時,殘留字元干擾下次讀取
  4. fflush(stdin) 未定義行為:C 標準僅定義輸出流刷新,輸入流刷新為實作定義

現代最佳實務


// C11 可選邊界檢查介面 (需編譯器支援)
#define __STDC_WANT_LIB_EXT1__ 1
#include 

char buf[100];
if (gets_s(buf, sizeof(buf)) == NULL) {
    // 處理錯誤或 EOF
}

// 跨平台通用安全模式
#define SAFE_GETS(buf) fgets(buf, sizeof(buf), stdin)

編譯器防護機制

  • GCC/Clang:-Wdeprecated-declarations 警告 gets 棄用
  • MSVC:_CRT_SECURE_NO_WARNINGS 否則編譯錯誤
  • Fortify Source:-D_FORTIFY_SOURCE=2 運行期緩衝區檢查
  • Stack Smashing Protector:-fstack-protector-strong 堆疊金絲雀防護
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
函數標準安全性備註
gets()C89~C99❌ 極度危險C11 移除
fgets()C89✅ 安全保留換行符,需手動移除
gets_s()C11 Annex K✅ 安全需定義 __STDC_WANT_LIB_EXT1__
getline()POSIX✅ 安全自動分配記憶體,Linux/Unix 專用