Linux中國
如何在 C 語言中安全地讀取用戶輸入
getline()
提供了一種更靈活的方法,可以在不破壞系統的情況下將用戶數據讀入程序。
在 C 語言中讀取字元串是一件非常危險的事情。當讀取用戶輸入時,程序員可能會嘗試使用 C 標準庫中的 gets
函數。它的用法非常簡單:
char *gets(char *string);
gets()
從標準輸入讀取數據,然後將結果存儲在一個字元串變數中。它會返回一個指向字元串的指針,如果沒有讀取到內容,返回 NULL
值。
舉一個簡單的例子,我們可能會問用戶一個問題,然後將結果讀入字元串中:
#include <stdio.h>
#include <string.h>
int main()
{
char city[10]; // 例如 "Chicago"
// 這種方法很糟糕 .. 不要使用 gets
puts("Where do you live?");
gets(city);
printf("<%s> is length %ldn", city, strlen(city));
return 0;
}
輸入一個相對較短的值就可以:
Where do you live?
Chicago
<Chicago> is length 7
然而,gets()
函數非常簡單,它會天真地讀取數據,直到它認為用戶完成為止。但是它不會檢查字元串是否足夠容納用戶的輸入。輸入一個非常長的值會導致 gets()
存儲的數據超出字元串變數長度,從而導致覆蓋其他部分內存。
Where do you live?
Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch
<Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch> is length 58
Segmentation fault (core dumped)
最好的情況是,覆蓋部分只會破壞程序。最壞的情況是,這會引入一個嚴重的安全漏洞,惡意用戶可以通過你的程序將任意數據插入計算機的內存中。
這就是為什麼在程序中使用 gets()
函數是危險的。使用 gets()
,你無法控制程序嘗試從用戶讀取多少數據,這通常會導致緩衝區溢出。
安全的方法
fgets()
函數歷來是安全讀取字元串的推薦方法。此版本的 gets()
提供了一個安全檢查,通過僅讀取作為函數參數傳遞的特定數量的字元:
char *fgets(char *string, int size, FILE *stream);
fgets()
函數會從文件指針讀取數據,然後將數據存儲到字元串變數中,但最多只能達到 size
指定的長度。我們可以更新示常式序來測試這一點,使用 fgets()
而不是 gets()
:
#include <stdio.h>
#include <string.h>
int main()
{
char city[10]; // 例如 "Chicago"
puts("Where do you live?");
// fgets 雖好但是並不完美
fgets(city, 10, stdin);
printf("<%s> is length %ldn", city, strlen(city));
return 0;
}
如果編譯運行,你可以在提示符後輸入任意長的城市名稱。但是,程序只會讀取 size
= 10 數據存儲到字元串變數中。因為 C 語言在字元串末尾會添加一個空(