기본 콘텐츠로 건너뛰기

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 ...

CVE-2017-11352

CVE-2017-11352 ImageMagick 에서 발생했던 CVE-2017-9144 취약점의 미흡한 조치로 인하여 동일한 취약점이 다시 발생되었다. 재 발생된 취약점 CVE-2017-11352은 coders/rle.c 에서 RLE 이미지에 대한 부적절한 EOF 처리가 원인이었다. EOF 란? 파일의 끝(End of File, EOF)으로 데이터 소스로부터 더 이상 읽을 수 있는 데이터가 없음을 나타낸다. ImageMagick Github Page 에 들어가보면 해당 이슈를 상세히 확인할 수 있다. 부적절한 EOF 처리 원인은 소스 코드 수정 시 유사한 코드를 복사 붙여넣기 하는 과정에서 검증해야할 변수 명을 고치지 않고 그대로 적용해서 발생했다. operand=ReadBlobByte(image); if (opcode == EOF) ThrowRLEException(CorruptImageError, "UnexpectedEndOfFile" ); 이로 인해서 조치 완료된 줄 알았던 CVE-2017-9144 취약점은 CVE-2017-11352이라는 새로운 취약점 명으로 다시 조치 되었다. case SkipLinesOp: { operand=ReadBlobByte(image); - if (opcode == EOF) + if (operand == EOF) ThrowRLEException(CorruptImageError, "UnexpectedEndOfFile" ); if (opcode & 0x40 ) { operand=ReadBlobLSBSignedShort(image); - if (opcode == EOF) + if (operand == EO...