C语言中危险函数

在 C 语言中,有一些函数因为存在缓冲区溢出、格式字符串漏洞等问题,被认为是"危险函数"。这些函数通常不能有效地检查输入的长度或数据,容易导致程序崩溃、内存泄漏或安全漏洞。常见的危险函数有:

1. gets()

gets() 函数用于从标准输入读取字符串,但它没有限制输入的长度。这就导致了缓冲区溢出问题。如果用户输入的字符串超出了缓冲区的大小,程序会覆盖其他内存区域,可能导致未定义行为或攻击者执行恶意代码。

替代函数fgets(),它允许指定读取的最大字符数,从而防止溢出。

2. scanf()

scanf() 在读取用户输入时,如果没有正确指定格式控制符的长度限制,也有可能导致缓冲区溢出。例如,scanf("%s", buffer) 会继续读取直到遇到空白字符,而不检查 buffer 的大小。

替代方法 :使用限定输入大小的格式,如 scanf("%99s", buffer),或者使用 fgets() 等安全输入函数。

3. strcpy()strcat()

strcpy()strcat() 分别用于字符串拷贝和连接操作,但它们不会检查目标缓冲区的大小,容易造成缓冲区溢出,尤其是当输入的字符串长度超过目标数组的容量时。

替代函数

  • strncpy():限制拷贝的字符数(但仍需小心,确保目标数组有足够的空间)。
  • strncat():限制连接的字符数。
  • 推荐使用 snprintf()strlcpy()(某些库提供)。

4. sprintf()vsprintf()

printf() 系列的函数在输出到字符数组时,如果没有检查目标数组的大小,也可能导致缓冲区溢出,特别是当格式字符串非常复杂或者输入数据太大时。

替代函数

  • snprintf():可以指定最大输出长度,避免溢出。
  • vsnprintf():用于格式化可变参数的版本。

5. memcpy()memmove()

这两个函数用于内存拷贝操作,但它们不会检查目标缓冲区的大小,使用时很容易引发缓冲区溢出问题。尤其在拷贝的长度不确定时,使用不当会导致内存破坏。

替代方法 :如果长度已知,确保不会超过目标缓冲区大小;否则使用带有边界检查的函数,如 memcpy_s()(在某些平台上可用)。

6. strtok()

strtok() 用于字符串分割,但它会直接修改原始字符串,并返回指向分割后的子字符串的指针。这种操作可能破坏原始数据,也容易出现内存泄漏或访问非法内存。

替代方法 :使用 strtok_r(),它是线程安全的。

7. alloca()

alloca() 函数分配内存,并且分配的内存是在栈上,而不是堆上。虽然它的作用和 malloc() 类似,但因为栈空间有限,使用不当可能会导致栈溢出。此外,它没有 free() 函数释放内存,导致内存泄漏。

替代方法 :尽量避免使用 alloca(),可以使用 malloc() 配合 free() 来管理堆内存。

8. system()

system() 函数用于执行系统命令,它会把字符串传递给操作系统的命令行解释器。在接受用户输入时,恶意用户可能通过构造特定的命令,执行系统命令,从而导致安全漏洞。

替代方法 :避免直接使用 system(),可以使用更安全的替代方法,如 exec() 系列函数,或者通过更安全的方式调用外部程序。

9. vprintf()vfprintf()

这些函数用于格式化输出,但如果格式化字符串不可靠(比如由用户提供),可能会导致格式字符串攻击。例如,攻击者通过控制格式字符串,可以导致程序泄露内存内容或执行任意代码。

替代方法 :使用参数校验,确保格式字符串是固定的,或者使用 snprintf() 等安全函数。

10. longjmp()setjmp()

setjmp()longjmp() 用于在程序中实现非局部跳转(类似异常处理机制)。虽然它们本身并不直接导致缓冲区溢出或内存泄漏,但它们的使用可能会绕过正常的资源清理流程(如栈上的变量销毁),因此,如果使用不当,可能会导致资源泄露或程序状态不一致。

建议 :尽量避免在代码中滥用 setjmp()longjmp(),并确保它们仅用于明确的控制流,避免跳过栈帧中重要的资源释放操作。

11. rand()srand()

虽然 rand()srand() 本身不会引发安全漏洞,但它们的伪随机性较差,生成的随机数序列很容易被预测。在密码学相关应用中,使用 rand() 生成的随机数不适合用于加密或安全相关的操作,因为它们不够随机,容易被攻击者预测。

替代方法 :对于安全敏感的应用,应该使用更为安全的随机数生成方法,例如 random()arc4random(),或者在现代系统中使用 openssl 库提供的高质量随机数生成函数。

12. tmpnam()tempnam()

tmpnam()tempnam() 用于生成临时文件名,但它们有可能导致竞争条件(TOCTOU,时间一环条件)漏洞。尤其是在多线程环境或多个进程并发执行时,可能会导致文件被其他进程提前创建,从而被攻击者利用。

替代方法 :使用更安全的 mkstemp()tmpfile(),这些函数会创建并返回一个临时文件,并且具备更好的安全性。

13. strchr()strrchr()

strchr()strrchr() 分别用于查找字符串中第一次和最后一次出现指定字符的位置。它们本身不是不安全的,但如果传入一个没有 \0 结尾的字符串,可能会导致越界访问。由于 C 语言字符串是基于空字符终止的,错误的字符串输入可能导致无法预料的行为。

建议 :确保传入给 strchr()strrchr() 的字符串是合法的以避免访问非法内存。

14. setvbuf()setbuf()

setvbuf()setbuf() 用于设置文件流的缓冲区大小和类型,但如果不小心使用,可能会导致缓冲区溢出或错误的缓冲区处理。例如,如果你没有足够的内存或缓冲区空间,可能会出现崩溃或资源泄漏。

建议:使用时小心指定合适的缓冲区大小,确保程序稳定。

15. sscanf()

sscanf() 是一种从字符串中读取格式化数据的函数,虽然它功能强大,但也很容易出错。没有正确限制输入的长度或格式时,sscanf() 可能会导致缓冲区溢出、内存泄漏或其他问题。

建议:使用时确保格式字符串正确,且传入的缓冲区有足够的空间。

16. vfork()

vfork() 用于创建新进程,与 fork() 类似,但它的行为不同,通常会导致父进程被暂停,直到子进程执行完毕。因为 vfork() 可能改变进程的状态,容易引发竞态条件或不一致的资源状态,从而导致难以调试的错误。

建议 :在不需要特殊优化的情况下,尽量避免使用 vfork(),改用 fork(),并且确保正确管理资源。

17. strtol()strtoul()

虽然这些函数用于将字符串转换为长整型或无符号长整型,但如果输入字符串不符合预期格式,可能会导致未定义的行为,尤其是在字符串超出整数范围或包含非数字字符时。

建议:使用时确保输入字符串的格式正确,并检查转换后的返回值和错误码。

18. gethostbyname()gethostbyaddr()

这些函数用于 DNS 查询,但它们已经被标记为过时(deprecated),并且存在潜在的安全风险,例如缓冲区溢出,尤其是在一些老旧的代码库中。

替代方法 :使用 getaddrinfo(),这是一个更现代、更安全的替代方案。

19. fseek()ftell()

虽然 fseek()ftell() 本身不是危险的函数,但在某些情况下,它们可能会导致对文件指针的错误操作,导致程序崩溃或不一致的文件状态。例如,在文件打开模式不正确时,可能会导致未定义的行为。

总结

以上这些"危险函数"通常是因为缺乏足够的输入验证或边界检查,在处理数据时容易引发缓冲区溢出、格式字符串漏洞等问题。现代编程推荐使用更安全的替代函数,如 fgets()snprintf()strncpy() 等,来确保程序的健壮性和安全性。此外,始终注意进行输入验证,避免直接信任用户输入,减少安全漏洞的风险。

相关推荐
SomeB1oody10 分钟前
【Rust自学】15.4. Drop trait:告别手动清理,释放即安全
开发语言·后端·rust
liruiqiang0513 分钟前
DDD-全面理解领域驱动设计中的各种“域”
开发语言·架构
qy发大财24 分钟前
验证二叉搜索树(力扣98)
数据结构·算法·leetcode·职场和发展
前端熊猫38 分钟前
JavaScript 的 Promise 对象和 Promise.all 方法的使用
开发语言·前端·javascript
人类群星闪耀时40 分钟前
用深度学习优化供应链管理:让算法成为商业决策的引擎
人工智能·深度学习·算法
hy____1231 小时前
动态内存管理
linux·运维·算法
weixin_421133411 小时前
编写python 后端 vscode 安装插件大全
开发语言·vscode·python
_GR1 小时前
Java程序基础⑪Java的异常体系和使用
java·开发语言
小度爱学习1 小时前
数据链路层协议
运维·服务器·网络·网络协议·网络安全
karatttt1 小时前
MapReduce,Yarn,Spark理解与执行流程
大数据·spark·mapreduce