Web攻击消减措施
缓冲区溢出消减措施
-
产品开发设计时,选用开发语言时,需要考虑缓冲区溢出攻击的风险,JAVA风险小。
-
如果选用C/C++开发语言,可以通过使用安全函数、启用编译选项降低缓冲区溢出攻击的安全风险。
-
操作系统加固,如启用Linux系统的ASLR保护。
-
进程低权限运行,防止溢出后直接获取root权限。
命令注入消减措施
-
C/C++开发语言中,建议Linux/Unix下使用exec系列函数来避免命令注入。exec系列函数中的path,file参数禁止使用命令解析器(如/bin/sh) 。在语言执行层,调用操作系统 Shell/CMD的接口时,需要对用户的输入进行白名单判断。
-
进程低权限运行,防止命令注入成功后直接获取root权限。
SQL注入消减措施
-
要使用预编译,并正确使用;无法使用预编译的场景,优先考虑白名单判断。
-
权限最小化:数据库进程权限必须为低权限运行,禁止使用root启动。数据库账号需要使用低权限,禁止使用SA、root、DBA权限这类管理权限。
-
Web解决方案中使用WAF可以拦截绝大部分的SQL攻击。
跨站脚本消减措施
- 不要信任任何来自外部的参数,外部的参数包括请求头,请求体,cookie,流对象,文件等,任何来自外部的参数都有可能被篡改,所以都需要做防XSS处理。
- 对于XSS的防御可以采用输入校验和输出编码两种方式,但是为了达到所见即所得的目的,建议根据输出点的类型不同,采用不同的转义函数对不可信参数进行输出编码。
C/C++常见错误
整数溢出
void NoCompliant(TLV_S *pstInput)
{
unsigned char *pucMsg = NULL;
unsigned short usLen = 0;
/*【错误】 pstInput >usIeLen可能为ushort范围内任意值,
进行加法运算后赋值给uslen可能截断为极小值.
*/
usLen = pstInput > usIeLen + sizeof(TLV_S);
pucMsg = (unsigned char *)malloc(usLen);
if (NULL == pucMsg) {
/* handle error */
}
memcpy_s(pucMsg + sizeof(TLV_S ), pstInput > usIeLen, pucIeVal, pstInput > usIeLen);
/*...申请的内存使用后free...*/
}
数组读写越界
typedef struct BigIntType {
unsigned int length;
char val[MAX_INT_DIGITS];
} BigInt;
BigInt *AsnOctsToBigInt(const AsnOcts *asnOcts)
{
BigInt *bigNumber = NULL;
...
/*【错误】外部输入作为内存操作相关函数的复制长度时.未校验合法性,导致数组写越界
*/
for (i = 0; i < asnOcts->octetLen; i++) {
bigNumber->val[i] = asnOcts->octs[i];
}
...
}
格式化字符串错误
void Noncompliant_ArgMismatch ()
{
char *error_msg = "Resource not available to user.";
int error_type = 3;
/* ...do something... */
printf(“Error (type %d): %s\n”, error_msg, error_type);
/*【错误】格式化参数类型不匹配 */
}
void Noncompliant ()
{
char *error_msg = "Resource not available to user.";
/* ...do something... */
printf("Error (type %s)\n");
/*【错误】格式化参数个数不匹配 */
}
void Noncompliant(char *user, char *password)
{
char input[1000];
if (fgets(input, sizeof(input) - 1, stdin) == NULL)
{
/* handle error */
}
input[sizeof(input)-1] = ’\0’;
printf(input);
/*【错误】不允许将用户输入直接作为格式字符串,
若其中包含“%s””%p”等特殊符号时,会引起程序异常 */
}
访问已经释放的内存
typedef struct _tagNode
{
int value;
struct _tagNode * next;
}Node;
Node * Noncompliant()
{
Node * head = (Node *)malloc(Node);
if (head==NULL)
{
/* ...do something... */
return NULL;
}
/* ...do something... */
free(head);
/* ...do something... */
head->next = NULL;
/*【错误】解引用了已经释放的内存*/
return head;
}
未对指定申请内存大小的整数值进行合法性校验
int * NoCompliant( size_t size )
{
size_t i;
int * array = (int *)malloc(size * sizeof(size_t));
/* 【错误】malloc的参数没有进行校验,可能会使的分配的内存过大或者过小,产生意想不到的问题 */
if(NULL == array)
return NULL;
for(i=0; i < size; i++)
{
array[i] = CreatRand();
}
...
}
使用realloc函数调整内存大小
/*…*/
void *ptr = realloc(ptr, NEW_SIZE); /* 【错误】当realloc()执行失败时会发生错误 */
if (!ptr)
{
/* 错误处理 */
}
/*…*/
realloc()是一个非常特殊的函数,其原型如下:
void *realloc(void \*ptr, size_t size)
随着参数的不同,其行为也是不同。
1、当ptr与size均不为NULL时,该函数会重新调整内存大小,并将新的内存指针返回,并保证最小的size的内容不变;
2、参数ptr为NULL,但size不为0,那么行为等同于malloc(size);
3、参数size为0,则realloc的行为等同于free(ptr)。
使用realloc函数会引出内存泄露的问题:
当realloc()分配失败的时候,会返回NULL。但参数中的ptr的内存未被释放,如果直接将realloc()的返回值赋给ptr,那么ptr原来指向的内存就会丢失,造成内存泄露。
安全函数使用不当
安全函数返回值未校验,遗漏异常处理流程,可能导致业务失效场景( 必须检查安全函数返回值,并针对返回值进行正确的处理)
函数传递时未将数组可用长度传递,导致下一层使用出错。(数组作为函数参数时,必须同时将其长度作为函数的参数)
安全函数使用不当之封装安全函数
不允许任何形式的安全函数封装,如果第一次封装未丢失参数及返回值,而第二次封装丢失了目的缓冲区长度,但由于是二次封装,工具/检视很难识别,埋下隐患。
对指针变量进行sizeof操作
将指针当做数组进行sizeof操作,导致实际的执行结果与预期不符。如果需要判断当前的指针类型大小,请使用sizeof(指针类型)而非sizeof(指针变量)的方式。