기본 콘텐츠로 건너뛰기

Secure-Coding-C-DCL-4

Secure-Coding-C-DCL-4

C 언어 시큐어코딩 - Declare and Initialization 4

예약된 식별자를 선언하거나 정의하지 않는다.

밑줄과 대문자 또는 밑줄로 시작하는 모든 식별자는 항상 그 용도가 예약되어 있다. 예약된 식별자를 매크로 이름으로 정의하거나, 예약된 컨텍스트에서 식별자를 선언하거나 정의하지 않는다.

잘못된 코드 예제 (Include Guard)

C 표준 라이브러리에 헤더에 구현되어 정의된 이름이 없는 경우에도 컴파일러에 의해 암시적으로 미리 정의된 이름과 충돌할 수 있다.

#ifndef _MY_HEADER_H_
#define _MY_HEADER_H_

/* Contents of <my_header.h> */

#endif /* _MY_HEADER_H_ */

올바른 코드 예제 (Include Guard)

매크로 이름을 밑줄로 시작하지 않는다.

#ifndef MY_HEADER_H
#define MY_HEADER_H

/* Contents of <my_header.h> */

#endif /* MY_HEADER_H */

잘못된 코드 예제 (File Scope Objects)

_max_limit은 정적이므로 충돌의 영향을 받지 않을 것으로 생각할 수 있으나, size_t를 정의하기 위해 포함된 이름들과 충돌할 가능성이 있으며 컴파일러는 예약된 이름을 암시적으로 선언할 수 있다. _limit은 런타임 라이브러리에 정의된 동일한 이름의 심볼과 충돌할 수 있다.

#include <stddef.h>

static const size_t _max_limit = 1024;
size_t _limit = 100;

unsigned int getValue(unsigned int count) {
	return count < _limit ? count : _limit;
}

올바른 코드 예제 (File Scope Objects)

객체 이름을 밑줄로 시작하지 않는다.

#include <stddef.h>

static const size_t max_limit = 1024;
size_t limit = 100;

unsigned int getValue(unsigned int count) {
	return count < limit ? count : limit;
}

잘못된 코드 예제 (Reserved Macros)

SIZE_MAX라는 이름은 size_t의 상한선을 나타내는 데 사용되는 표준 매크로 이름과 충돌한다. 또한 INTFAST16_LIMIT_MAX라는 이름이 C 표준 라이브러리에 정의되어 있지는 않지만 INT 접두어로 시작하고 _MAX 접미사로 끝나기 때문에 예약된 식별자이다.

#include <inttypes.h>
#include <stdio.h>

static const int_fast16_t INTFAST16_LIMIT_MAX = 12000;

void print_fast16(int_fast16_t val) {
	enum { SIZE_MAX = 80 };
	char buf[SIZE_MAX];
	if (INTFAST16_LIMIT_MAX < val) {
		sprintf(buf, "The value is too large");
	} else {
		snprintf(buf, SIZE_MAX, "The value is %" PRIdFAST16, val);
	}
}

올바른 코드 예제 (Reserved Macros)

예약된 접두사 및 접미사를 피해서 선언한다.

#include <inttypes.h>
#include <stdio.h>

static const int_fast16_t MY_INTFAST16_UPPER_LIMIT = 12000;

void print_fast16(int_fast16_t val) {
	enum { BUFSIZE = 80 };
	char buf[BUFSIZE];
	if (MY_INTFAST16_UPPER_LIMIT < val) {
		sprintf(buf, "The value is too large");
	} else {
		snprintf(buf, BUFSIZE, "The value is %" PRIdFAST16, val);
	}
}

잘못된 코드 예제

malloc()free() 표준 라이브러리 함수를 재정의하고 있다. 이 방법은 UNIX의 많은 전통적인 구현 방식으로 허용되지만, C 표준에 따라 문제가 발생할 수 있는 정의되지 않은 동작이다.

#include <stddef.h>

void *malloc(size_t nbytes) {
	void *ptr;
	/* Allocate storage from own pool and set ptr */
	return ptr;
}

void free(void *ptr) {
	/* Return storage to own pool */
}

올바른 코드 예제

C 표준 라이브러리 식별자를 재정의하지 않는다.

#include <stddef.h>

void *my_malloc(size_t nbytes) {
	void *ptr;
	/* Allocate storage from own pool and set ptr */
	return ptr;
}

void *my_aligned_alloc(size_t alignment, size_t size) {
	void *ptr;
	/* Allocate storage from own pool, align properly, set ptr */
	return ptr;
}

void *my_calloc(size_t nelems, size_t elsize) {
	void *ptr;
	/* Allocate storage from own pool, zero memory, and set ptr */
	return ptr;
}

void *my_realloc(void *ptr, size_t nbytes) {
	/* Reallocate storage from own pool and set ptr */
	return ptr;
}

void my_free(void *ptr) {
	/* Return storage to own pool */
}

잘못된 코드 예제 (errno)

errno가 매크로인지 외부 Linkage로 선언된 식별자인지 불확실하다. 매크로 정의가 실제 객체에 액세스하지 못하거나 프로그램이 errno라는 이름으로 식별자를 정의하면 문제가 발생할 수 있다.

extern int errno;

올바른 코드 예제 (errno)

errno를 선언하는 올바른 방법은 헤더를 포함하는 것이다.

#include <errno.h>

예외들

라이브러리 함수가 헤더에 정의된 유형을 참조하지 않고 선언될 수 있는 경우, 선언이 표준 선언과 호환되면 헤더를 포함하지 않고 해당 함수를 선언할 수 있다.

/* Not including stdlib.h */
void free(void *);

void func(void *ptr) {
	free(ptr);
}

선언이 stdlib.h가 제공하는 것과 일치하고 예약된 식별자를 다시 정의하지 않기 때문에 이러한 코드는 호환된다. 그러나 이 예제에서 free()함수에 대한 정의를 제공하는 것은 허용되지 않는다.

반복 실행해도 동작이 동일한 경우 다른 컴파일러 또는 언어 표준 모드와의 호환성을 위해 예약된 식별자와 동일한 매크로 식별자를 만들 수 있다.

/* Sometimes generated by configuration tools such as autoconf */
#define const const
  
/* Allowed compilers with semantically equivalent extension behavior */
#define inline __inline

컴파일러 벤더 또는 표준 라이브러리 개발자는 구현을 위해 예약 된 식별자를 사용할 수 있다.

/*
  The following declarations of reserved identifiers exist in the glibc implementation of
  <stdio.h>. The original source code may be found at:
  https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=include/stdio.h;hb=HEAD
*/
  
#  define __need_size_t
#  include <stddef.h>
/* Generate a unique file name (and possibly open it).  */
extern int __path_search (char *__tmpl, size_t __tmpl_len,
              const char *__dir, const char *__pfx,
              int __try_tempdir);

이 블로그의 인기 게시물

데일 카네기 인간관계론 정리

Remove-Server-Header

응답 메시지 내 서버 버전 정보 제거 1. Apache 1) 조치 방법 “/etc/htpd/conf/httpd.conf” 파일 안에서 1. ServerTokens OS → ServerTokens Prod 2. ServerSignature On → ServerSignature Off 로 변경한 후 아파치를 재시작하면 헤더 값의 아파치 버전 정보 및 OS 정보를 제거할 수 있다. 2) 참고 URL http://zetawiki.com/wiki/CentOS_ 아파치_보안권장설정_ServerTokens_Prod,_ServerSignature_Off 2. IIS 1) 조치 방법 IIS 6.0 urlscan_setup 실행. 설치. \windows\system32\inetsrv\urlscan\urlscan.ini 파일을 열어 다음 수정(RemoveServerHeader=0 을 RemoveServerHeader=1 로 변경) 서비스에서 IIS Admin Service 재시작. IIS 7.0 IIS 관리자를 열고 관리하려는 수준으로 이동합니다. 기능 보기에서 HTTP 응답 헤더를 두 번 클릭합니다. HTTP 응답 헤더 페이지에서 제거할 헤더를 선택합니다. 작업 창에서 제거를 클릭하고 예를 클릭합니다. 2) 참고 URL IIS 6.0 : http://gonnie.tistory.com/entry/iis6- 응답헤더-감추기 IIS 7.0 : https://technet.microsoft.com/ko-kr/library/cc733102(v=ws.10).aspx 3. jetty 1) 조치 방법 “jetty.xml” 파일에서 jetty.send.server.version=false 설정 2) 참고 URL http://attenuated-perspicacity.blogspot.kr/2009/09/jetty-61x-hardening.html 4. Nginx ...

American Fuzzy Lop And Address Sanitizer

American Fuzzy Lop And Address Sanitizer Address Sanitizer(ASAN) 단지 gcc/clang에 -fsanitize=address 옵션을 추가하는 것으로 간단히 사용할 수 있지만 그 효과는 충분하다. Example Out Of Bounds Read #include <stdio.h> int main() { int a[ 2 ] = { 3 , 1 }; int i = 2 ; printf ( "%i\n" , a[i]); } 예제 파일을 OutOfBoundsRead.c 로 생성하고 ASAN 옵션을 지정하여 clang 으로 컴파일하자. clang -g -fsanitize = address -fno -omit -frame -pointer OutOfBoundsRead . c -o OutOfBoundsRead 생성된 OutBoundsRead 파일을 실행하면 다음과 같은 결과를 볼 수 있다. ================================================================= ==3678==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc0ba87428 at pc 0x47b7db bp 0x7ffc0ba87390 sp 0x7ffc0ba87388 READ of size 4 at 0x7ffc0ba87428 thread T0 ==3678==WARNING: Trying to symbolize code, but external symbolizer is not initialized! #0 0x47b7da (/root/ASAN/OutOfBoundsRead+0x47b7da) #1 0x7faba0260f44 (/lib/x86_64-linux-gnu/libc.so.6+0x21f44) #2 0x...