编写安全的C和安全的C成语
-
18-09-2019 - |
题
“普通人不想自由。他只是想安全。” - HL Menken
我正在尝试写非常安全的C。在下面,我列出了我使用的一些技术,并问它们像我认为一样安全。请不要毫不犹豫地将我的代码/先入为主撕成碎片。任何发现最琐碎的脆弱性或教给我新想法的答案都将是 高度重视.
从流来阅读:
根据 GNU C编程教程 getline:
GETLINE功能将根据需要自动扩大内存块,因此永远不会缺少空间 - Getline如此安全的原因之一。 [..]请注意,Getline可以安全地处理您的输入线,无论它有多长时间。
我认为getline应该 在所有输入下, ,防止 缓冲区溢出 从从流中阅读时发生。
- 我的假设正确吗?是否存在可能导致利用的输入和/或分配方案?例如,如果流的第一个字符是某些 奇异的控制字符, ,也许是0x08 backspace(CTL-H)。
- 是否有任何工作以数学证明是安全的?
Malloc在失败时返回无效:
如果Malloc遇到错误Malloc将返回空指针。这会带来安全风险,因为人们仍然可以将指针算术应用于null(0x0)指针,因此Wikipedia 推荐
/* 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 我已经养成了将要提取的字符串分配给输入字符串大小的习惯,希望避免使用超支的可能性。例如:
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是输入str的大小,并且可以从Inputstr读取strlen(Inputstr)的字符更多,所以似乎不可能,给定 所有可能的值 让输入量引起缓冲区溢出?
- 我对么?有我没有想到的奇怪角案吗?
- 有更好的写作方法吗?已经解决了它的库?
一般的问题:
尽管我发布了许多问题,但我不希望有人回答所有问题。这些问题是我想要的各种答案的指南。我真的很想学习安全的心态。
- 那里还有哪些安全的C习惯?
- 我需要什么角案 总是检查?
- 如何编写单元测试来执行这些规则?
- 如何以可测试性或可证明正确的方式执行约束?
- 任何推荐的静态/动态分析技术还是C的工具?
- 您遵循哪些安全的C练习,以及如何为自己和他人证明它们是合理的?
资源:
许多资源都是从答案中借来的。
- 为Linux和Unix Howto提供安全编程 戴维·惠勒(David Wheeler)
- 安全C编程 - 太阳微型系统
- 以身作则不安全的编程
- 添加更多的网点 - 涵盖这些问题的博客
- 证书安全编码计划
- 缺点 - 静态分析工具
- 使用Thm侵略证明安全 Yannick Moy
- libsafe
解决方案
- 从流读
事实 getline()
“将根据需要自动扩大内存块”意味着可以用作拒绝服务的攻击,因为生成要长时间的输入是微不足道的,它将耗尽该过程的可用内存(或更糟糕的是,系统!)。一旦发生了不符状态,其他漏洞也可能发挥作用。低/无内存中代码的行为很少很好,而且很难预测。恕我直言,在所有事物上设置合理的上限是更安全的,尤其是在对安全敏感的应用程序中。
此外(正如您提及特殊角色所预期的那样), getline()
只给您一个缓冲区;它不能保证缓冲区的内容(因为安全完全取决于应用程序)。因此,对输入进行消毒仍然是处理和验证用户数据的重要组成部分。
- SSCANF
我倾向于使用正则表达式库,并且具有非常狭义的用户数据的差异性,而不是使用 sscanf
. 。这样,您可以在输入时执行大量验证。
普通的留言
- 有模糊工具可生成随机输入(有效和无效),可用于测试输入处理
- 缓冲区管理至关重要:缓冲区的溢出,下水下,内存外
- 可以用否则安全的代码来利用比赛条件
- 可以操纵二进制文件以将无效的值或超大值注入标题,因此文件格式代码必须是岩石固体的,并且不要假设二进制数据有效
- 临时文件通常可以成为安全问题的来源,必须仔细管理
- 代码注入可用于用恶意版本替换系统或运行时库呼叫
- 插件为攻击提供了巨大的向量
- 总的来说,我建议在用户数据(或应用程序外部的任何数据)中明确定义接口,直到对其进行处理,消毒和验证,并且用户数据输入应用程序的唯一方法是
其他提示
我认为您的SSCANF示例是错误的。使用这种方式,它仍然可以溢出。
尝试此方法,它指定要读取的最大字节数:
void main(int argc, char **argv)
{
char buf[256];
sscanf(argv[0], "%255s", &buf);
}
在测试方面,我将编写一个程序,该程序生成随机长度的随机字符串并将其馈送到您的程序中,并确保它们得到适当处理。
一个开始看这个的好地方是 戴维·惠勒(David Wheeler)出色的安全编码网站.
他的免费在线书”为Linux和Unix Howto提供安全编程“是定期更新的绝佳资源。
您可能还想看他的出色静态分析仪 缺点 要获得一些提示。但是请记住,没有自动化工具可以替代一双经验丰富的眼睛,或者像大卫如此彩色的那样。
任何静态分析工具(例如缺陷)都只是一种工具。没有工具可以代替人类的思想!简而言之, “用工具的傻瓜仍然是个傻瓜”. 。认为分析工具(如缺点)是安全培训和知识的替代品是错误的
我亲自使用了大卫的资源已有几年了,发现它们非常出色。
Yannick Moy在他的博士学位期间为C开发了C的Hoare/Floyd最弱的前提系统 将其应用于CERT托管库. 。他发现了许多虫子(请参阅他的回忆录的第197页)。好消息是,图书馆现在的工作更加安全。
您也可以查看Les Hatton的网站 这里 在他的书中 更安全c 您可以从亚马逊获得。
不要使用 gets()
要输入,请使用 fgets()
. 。使用 fgets()
, ,如果您的缓冲区自动分配(即“在堆栈上”),请使用此习惯:
char buf[N];
...
if (fgets(buf, sizeof buf, fp) != NULL)
如果您决定更改大小,这将继续工作 buf
. 。我更喜欢此表格,而不是:
#define N whatever
char buf[N];
if (fgets(buf, N, fp) != NULL)
因为第一批形式使用 buf
确定第二个论点,并且更清楚。