编写安全C和安全C语言
“普通人不想自由,他只是想安全。” – HL Menken
我正在尝试编写非常安全的C语言。下面我列出一些我使用的技术,并询问它们是否像我想的那样安全。 请不要犹豫,把我的代码/先入之见撕成碎片。 任何答案,即使是最微不足道的漏洞,或教我一个新的想法将受到高度重视 。
从stream中读取:
根据GNU C编程教程 getline:
getline函数会根据需要通过realloc函数自动放大内存块,所以永远不会有空间不足 – getline是如此安全的一个原因。 请注意,getline可以安全地处理您的input行,不pipe它多长时间。
我认为getline应该在所有input下防止从stream中读取时发生缓冲区溢出 。
- 我的假设是否正确? 有没有可能导致利用的input和/或分配scheme? 例如,如果stream中的第一个字符是一些奇怪的控制字符 ,可能是0x08 BACKSPACE(ctl-H)。
- 有没有工作做mathcertificategetline是安全的?
Malloc在失败时返回空值:
如果malloc遇到错误malloc返回一个NULL指针。 由于仍然可以将指针运算应用于NULL(0x0)指针,因此存在安全风险,因此维基百科build议
/* Allocate space for an array with ten elements of type int. */ int *ptr = (int*)malloc(10 * sizeof (int)); if (ptr == NULL) { /* Memory could not be allocated, the program should handle the error here as appropriate. */ }
安全sscanf:
当使用sscanf时,我习惯于将大小被提取的string分配给inputstring的大小,以避免出现溢出的可能性。 例如:
const char *inputStr = "a01234b4567c"; const char *formatStr = "a%[0-9]b%[0-9]c": char *str1[strlen(inputStr)]; char *str2[strlen(inputStr)]; sscanf(inputStr, formatStr, str1, str2);
由于str1和str2是inputStr的大小,并且从inputStr中不能再读取超过strlen(inputStr)的字符,所以给inputStr的所有可能的值导致缓冲区溢出似乎是不可能的。
- 我对么? 有没有想到的奇怪的angular落案件?
- 有没有更好的方法来写这个? 已经解决了它的图书馆?
一般的问题:
虽然我发布了大量的问题,但我不希望任何人回答所有问题。 这些问题更多的是我正在寻找的答案的指导方针。 我真的想学习安全的C心态。
- 还有哪些其他安全的C语言?
- 我需要经常检查哪些angular落案件?
- 我如何编写unit testing来执行这些规则?
- 我怎样才能以可testing性或可证实的方式强制约束呢?
- 任何推荐的静态/dynamic分析工具或C工具?
- 你遵循什么安全的C实践,你如何为自己和他人辩护?
资源:
许多资源是从答案中借来的。
- Linux和Unix的安全编程HOWTO David Wheeler
- 安全C编程 – SUN Microsystems
- 不安全的编程例子
- 添加更多NOPS – 覆盖这些问题的博客
- CERT安全编码倡议
- flawfinder – 静态分析工具
- Yannick Moy 使用Thmcertificate安全
- libsafe的
- 从stream中读取
getline()
“会根据需要自动放大内存块”的事实意味着这可能被用作拒绝服务攻击,因为生成一个很长的input将会耗尽可用的内存的过程(或更糟的是,系统!)。 一旦发生内存不足的情况,其他漏洞也可能发挥作用。 代码在低/无内存中的行为很less,很难预测。 恕我直言,设置一切合理的上限是安全的,特别是在安全敏感的应用程序。
此外(正如你所预料的,通过提及特殊字符), getline()
只会给你一个缓冲区; 它没有对缓冲区内容做任何保证(因为安全性完全取决于应用程序)。 因此,对input进行消毒仍然是处理和validation用户数据的重要部分。
- sscanf的
我倾向于使用正则expression式库,并且为用户数据定义了非常狭义的正则expression式,而不是使用sscanf
。 这样,您可以在input时进行大量的validation。
-
普通的留言
- 模糊工具可用于生成随机input(有效和无效),可用于testing您的input处理
- 缓冲区pipe理至关重要:缓冲区溢出,下溢,内存不足
- 竞争条件可以在其他安全的代码中被利用
- 二进制文件可以被操纵,以注入无效的值或超大的值,所以文件格式的代码必须坚如磐石,而不是假设二进制数据是有效的
- 临时文件通常可能是安全问题的来源,必须仔细pipe理
- 代码注入可以用来代替恶意版本的系统或运行时库调用
- 插件为攻击提供了一个巨大的载体
- 作为一般原则,我会build议有明确定义的接口,用户数据(或来自应用程序之外的任何数据)被假定为无效和敌对的,直到处理,消毒和validation,以及用户数据进入应用程序的唯一方式
我认为你的sscanf例子是错误的。 使用这种方式时,它仍然可以溢出。
试试这个,它指定了要读取的最大字节数:
void main(int argc, char **argv) { char buf[256]; sscanf(argv[0], "%255s", &buf); }
看看这篇IBM开发的关于防止缓冲区溢出的文章。
在testing方面,我会编写一个随机生成随机string的程序,并将它们提供给您的程序,并确保它们得到适当的处理。
天儿真好,
David Wheeler是一个非常好的安全编码站点 。
他的免费在线书籍“ Secure Programming for Linux and Unix HOWTO ”是定期更新的优秀资源。
你也可以看看他的优秀静态分析器FlawFinder,以获得更多的提示。 但请记住,没有一个自动化的工具可以代替一双经验丰富的眼睛,或者像David这样色彩缤纷的眼睛。
任何静态分析工具,如Flawfinder,只是一个工具。 没有工具可以替代人的思想! 总之, “用工具傻瓜还是个傻瓜” 。 认为分析工具(如缺陷探测器)是安全培训和知识的替代品是错误的
我已经亲自使用了大卫的资源好几年了,并发现他们是优秀的。
HTH
干杯,
不安全的编程例子
博客中有一些答案
Yannick Moy在PhD期间为C开发了一个Hoare / Floyd最弱的预处理系统,并将其应用于CERTpipe理的string库 。 他发现了一些错误(参见他的回忆录197页)。 好消息是图书馆为他的工作更安全。
你也可以在这里看到莱斯哈顿的网站,也可以看看你可以从亚马逊那里得到的安全C这本书。
不要使用gets()
来input,使用fgets()
。 要使用fgets()
,如果你的缓冲区是自动分配的(即“在堆栈上”),那么使用这个习惯用法:
char buf[N]; ... if (fgets(buf, sizeof buf, fp) != NULL)
这将继续工作,如果你决定改变buf
的大小。 我更喜欢这种forms:
#define N whatever char buf[N]; if (fgets(buf, N, fp) != NULL)
因为第一种forms使用buf
来确定第二个参数,并且更清楚。
检查fclose()
的返回值。