代码脆弱性防范指引

缓冲区溢出防范

缓存溢出出现在当一些数据拷贝到内存区域中产生,如果内存的缓存区不能够容纳这些数据,到拷贝成功的时候,目标内存的边界部分就会被覆盖。程序中的变量可以被存在程序栈(stack)中也可以存在程序堆(heap)中,因此我们也可以常听到这些字汇,栈溢出和堆溢出,相比较这两个溢出类型,栈溢出的利用多数情况下会比较容易点。

现在许多有缓存溢出风险的函数都存在相对安全的替代函数,它们都通过传递参数来限制多少数据被调用。如:

 Strcpy

strcpy():此函数没有执行任意长度的确认,而使用strncpy()函数可以限制长度,当使用strncpy()的时候,需要以NULL终结字符串,因为有NULL终止符隐含定义源缓存区的大小。

不正确情况

void func(char *str)
{
    char buffer[256];
    strcpy(buffer, str);
    return;
}

正确情况

void func(char *str)
{
    char buffer[256];
    strncpy(buffer, str, sizeof(buffer) -1);
    buffer[sizeof(buffer) – 1] = 0;
    return;
}


 

strcat

strcat():此函数类似与strcpy(),也不检查字符串的长度,使用strncat()可以很好的限制被拷贝的长度,strncat()函数只会追加定义在大小参数中的长度,NULL终止符将终止字符串。

不正确情况

void func(char *str)
{
    char buffer[256];
    strcat(buffer, str);
    return;
}

正确情况

void func(char *str)
{
char buffer[256];
strncat(buffer,str,sizeof(buffer)-1-strlen(buffer));
    return;
}

 sprintf

 

sprintf():此函数用来格式化字符串变量,是很容易产生缓存溢出的地方。使用snprintf()可以很好的限制拷贝到缓存中的打印数据长度,snprintf()函数返回要传递过去的数据字符总数,如果它大于能传递字节大小的值,那么没有任何数据写到缓存中,用户必须检查snprintf()的值以保证要被打印的缓存。

不正确情况

void func(char *str)
{
    char buffer[256];
    sprintf(buffer, “%s”, str);
    return;
}

正确情况

void func(char *str)
{
    char buffer[256];
    if (snprintf(target, sizeof(target) - 1, "%s", string) > sizeof(target) - 1)
    return;
}

gets

gets:此函数天生就存在漏洞,在应用程序中应该永远不要使用,一般在编译的时候也会被警告,gets()没有规定任意长度,永远将导致缓存溢出,它从标准输入中读取数据并一直到换行符或者EOF标识接收到,所读的数据全部填充到定义的缓存,并不检查任何长度。fgets()函数用来代替不安全的gets()函数,此函数会至多读取’sizeof(buffer) – 1’的文件流 。当然中途读取到EOF或者换行符也会停止。

不正确情况

 
void func(char *str)
{
    char buffer[256];
    gets(buffer);
    return;
}

正确情况

void func(char *str)
{
    char buffer[256];
    fgets(buffer, sizeof(buffer) – 1, stdin);
    return;
}

Scanf\fscan\sscan

scanf()、fscan()sscan():在使用这些函数的时候需要小心读取要放到固定长度缓存区的数据,必须确保规定要读到缓存区的数据长度。如下图,在正确的情况下只能读取255个字符到规定的缓存中,而在不正确的情况下,就会无限制的读取,并导致函数的堆栈被破坏。

不正确情况

char buffer[256];
int num;
num = fscanf(stdin, "%s", buffer);


正确情况

char buffer[256];
int num;
num = fscanf(stdin, "%255s", buffer);

memcpy

 

Memcpy():由于不安全的使用memcpy()会导致不少溢出,当规定了长度的memcpy()函数可以受外界操作的时候,就可能发生溢出,所以保证拷贝到内存结构中的数据不大于规定大小将非常重要,下面一个示例将说明溢出是怎样产生的:

unsigned long copyaddress(struct hosten *hp) {
    unsigned long address;
    memcpy(&address, hp->h_addr_list[0], hp->h_length);
}


上面的示例来自与一个出现在BIND上的实际漏洞,在这里我们做了简化,上面的函数拷贝hp->h_length字节到’address’变量中(4字节),在正常情况下, 因为网络地址hp->h_length一般都是4字节,但是,攻击者确可以利用h_length变量,如果他伪造一个假的DNS回应,他就可以提供更长的地址通过hp-h_addr_list变量传递,这将导致超过4个字节大小拷贝到’address’变量,溢出变量,拷贝更多的数据到堆栈。

安全编码规范:

缓冲区溢出主要是一个C/C + + 问题,要想避免缓冲区溢出,在编写函数时应该遵循以下三个原则。

1. 要求代码传递缓冲区的长度

要求调用者传递缓冲区的长度,根据长度判断是否能够安全地复制数据。同时也不应该盲目地信任调用者提供的长度。

2. 探测内存

通过向目标缓冲区写入一个固定的已知值,可以在源缓冲区太大时,强制代码失败。同样这样也可以在开发过程中及早发现错误。与其运行攻击者的恶意有效代码,还不如让程序失败,这就是不直接复制缓冲区的原因。

3. 采取防范措施

 探测虽然很有用,但并不能完全阻止攻击。真正安全的办法是编写防范性的代码。前面的代码已经具有防范性了,它将检查进入函数的数据是否不超过内部缓冲区。然而,有些函数在处理或复制不可靠的数据时,如果使用不当,则会存在潜在的严重安全问题。需要注意的函数包括诸如 StrcpyStrcatgets 等常见函数

代码检查关键词:strcpy,strcat,sprintf,gets,scanf,fscan,sscan,memcpy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值