1. 코드 삽입 취약점의 무서움
코드 삽입(Code Injection)은 공격자가 입력한 악의적인 코드가 애플리케이션 내부에서 동적으로 실행되는 취약점입니다. SQL 삽입이 데이터베이스에 영향을 준다면, 코드 삽입은 서버의 운영체제나 애플리케이션 엔진 자체를 장악할 수 있다는 점에서 훨씬 치명적입니다. 공격자는 서버에서 임의의 명령을 실행하거나, 파일을 삭제하고, 원격 셸(Remote Shell)을 획득하여 시스템 전체를 자신의 조종 아래에 둘 수 있습니다.
2. 동적 평가(Evaluation) 함수의 위험성
주로 eval(), exec(), 또는 리플렉션(Reflection)이나 스크립트 엔진(JavaScript Engine 등)을 사용하는 로직에서 발생합니다. 예를 들어, 수식 계산을 위해 사용자가 전달한 문자열을 그대로 스크립트 엔진에 전달하면, 공격자는 계산식 대신 java.lang.Runtime.getRuntime().exec("rm -rf /")와 같은 파괴적인 코드를 주입할 수 있습니다. 이는 개발자가 의도한 데이터 처리 범위를 벗어나 코드가 실행되는 전형적인 보안 결함입니다.
3. 실무적 대응: 동적 실행 지양과 화이트리스트 검증
가장 확실한 방어법은 외부 입력값을 실행 가능한 코드의 일부로 사용하지 않는 것입니다. 동적인 연산이 필요하다면 미리 정의된 함수나 상수의 매핑 테이블(화이트리스트)을 만들어 사용자가 정해진 옵션만 선택하도록 제한해야 합니다. 만약 동적 스크립트 실행이 반드시 필요하다면, 샌드박스(Sandbox) 환경을 구축하여 시스템 자원에 대한 접근 권한을 완전히 박탈한 상태에서 실행해야 합니다.
4. CWE-94/95 대응 및 안전한 로직 구현 자바 코드 예시
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List;
public class SecureCodeExecutor {
private static final Logger logger = LoggerFactory.getLogger(SecureCodeExecutor.class);
// [CWE-94/95 조치] 화이트리스트 방식을 사용한 안전한 연산
public double calculate(String operator, double a, double b) {
// 허용된 연산자 리스트 정의 (White-list)
List<String> allowedOperators = Arrays.asList("add", "sub", "mul", "div");
if (!allowedOperators.contains(operator)) {
logger.debug("Invalid operator attempt: {}", operator);
throw new IllegalArgumentException("Unsupported operator.");
}
logger.debug("Executing operation: {}", operator);
switch (operator) {
case "add": return a + b;
case "sub": return a - b;
case "mul": return a * b;
case "div": return (b != 0) ? a / b : 0;
default: return 0;
}
}
// 위험 사례: 외부 입력값을 스크립트 엔진에 직접 전달 (금지)
public void unsafeExecution(String userInput) {
try {
// ScriptEngineManager manager = new ScriptEngineManager();
// ScriptEngine engine = manager.getEngineByName("JavaScript");
// engine.eval(userInput); // 공격자가 코드를 주입할 수 있는 지점
} catch (Exception e) {
logger.debug("Execution failed: {}", e.getMessage());
}
}
public void process(String op, String val1, String val2) {
try {
double result = calculate(op, Double.parseDouble(val1), Double.parseDouble(val2));
logger.debug("Calculation result: {}", result);
} catch (NumberFormatException e) {
// [CWE-754, CWE-390] 부적절한 입력값 예외 처리
logger.debug("Invalid number format: {}", e.getMessage());
} catch (Exception e) {
// [CWE-209] 시스템 에러 정보 노출 방지
logger.debug("Unexpected system error during calculation.");
}
}
}
코멘트: 코드 삽입 방어의 철칙은 "데이터를 데이터로만 대하는 것"입니다. 사용자가 입력한 문자열이 절대 실행 엔진의 명령어로 변질되지 않도록 화이트리스트 기반의 설계 전략을 고수하십시오. logger.debug()를 통해 입력된 옵션을 기록하여 추적성을 확보하되, 시스템의 실행 권한을 외부의 손에 넘겨주는 실수를 범하지 않는 것이 시큐어 코딩의 핵심입니다.
자바스크립트의 시한폭탄: eval() 함수와 코드 삽입
1. eval() 함수가 위험한 이유
자바스크립트의 eval() 함수는 인자로 받은 문자열을 자바스크립트 코드로 해석하여 즉시 실행합니다. 만약 사용자로부터 입력받은 값이 이 함수에 그대로 전달된다면, 공격자는 악의적인 스크립트를 주입하여 브라우저 내의 쿠키 정보를 탈취(XSS)하거나, 서버 사이드 자바스크립트(Node.js) 환경의 경우 서버의 제어권을 완전히 획득할 수 있습니다. "문자열을 코드로 변환한다"는 편리함 뒤에는 시스템의 모든 권한을 열어주는 거대한 보안 구멍이 숨어 있습니다.
2. 흔히 발생하는 잘못된 패턴: 동적 프로퍼티 접근
많은 개발자가 JSON 데이터를 처리하거나 객체의 특정 프로퍼티에 동적으로 접근하기 위해 eval()을 사용하곤 합니다. 예를 들어 eval("obj." + userInput)과 같은 코드는 사용자 입력값에 따라 원치 않는 함수가 실행될 위험이 큽니다. 이는 CWE-95(동적 코드 실행)의 전형적인 사례로, 현대적인 자바스크립트 개발에서는 절대적으로 지양해야 할 패턴입니다.
3. 실무적 대응: JSON.parse()와 대괄호 표기법 활용
동적 코드가 필요한 대부분의 상황은 안전한 대체 수단으로 해결이 가능합니다.
-
데이터 파싱: 문자열을 객체로 변환할 때는
eval()대신 웹 표준인JSON.parse()를 사용하십시오. -
동적 접근: 객체의 속성에 접근할 때는
eval()대신 대괄호 표기법(obj[userInput])을 사용하면 입력값이 코드가 아닌 단순 '키(Key)'로만 취급되어 안전합니다.
4. CWE-95 대응 및 안전한 로직 구현 예시
1. eval() 함수가 위험한 이유
자바스크립트의 eval() 함수는 인자로 받은 문자열을 자바스크립트 코드로 해석하여 즉시 실행합니다. 만약 사용자로부터 입력받은 값이 이 함수에 그대로 전달된다면, 공격자는 악의적인 스크립트를 주입하여 브라우저 내의 쿠키 정보를 탈취(XSS)하거나, 서버 사이드 자바스크립트(Node.js) 환경의 경우 서버의 제어권을 완전히 획득할 수 있습니다. "문자열을 코드로 변환한다"는 편리함 뒤에는 시스템의 모든 권한을 열어주는 거대한 보안 구멍이 숨어 있습니다.
2. 흔히 발생하는 잘못된 패턴: 동적 프로퍼티 접근
많은 개발자가 JSON 데이터를 처리하거나 객체의 특정 프로퍼티에 동적으로 접근하기 위해 eval()을 사용하곤 합니다. 예를 들어 eval("obj." + userInput)과 같은 코드는 사용자 입력값에 따라 원치 않는 함수가 실행될 위험이 큽니다. 이는 CWE-95(동적 코드 실행)의 전형적인 사례로, 현대적인 자바스크립트 개발에서는 절대적으로 지양해야 할 패턴입니다.
3. 실무적 대응: JSON.parse()와 대괄호 표기법 활용
동적 코드가 필요한 대부분의 상황은 안전한 대체 수단으로 해결이 가능합니다.
-
데이터 파싱: 문자열을 객체로 변환할 때는
eval()대신 웹 표준인JSON.parse()를 사용하십시오. -
동적 접근: 객체의 속성에 접근할 때는
eval()대신 대괄호 표기법(obj[userInput])을 사용하면 입력값이 코드가 아닌 단순 '키(Key)'로만 취급되어 안전합니다.
4. CWE-95 대응 및 안전한 로직 구현 예시
// [로그 출력 설정] 실무에서는 전용 로거 라이브러리를 사용 권장
const logger = {
debug: (msg) => console.log(`[DEBUG] ${msg}`)
};
// 1. 위험한 사례: eval() 사용 (절대 금지)
function unsafeAction(userInput) {
try {
// 공격자가 "alert('hack');" 또는 서버 명령어를 넣으면 그대로 실행됨
return eval(userInput);
} catch (e) {
logger.debug("Execution failed");
}
}
// 2. 안전한 대안: 객체 매핑 및 대괄호 표기법 사용 (권장)
const actions = {
"calculate": (a, b) => a + b,
"greet": (name) => `Hello, ${name}`
};
function safeAction(actionName, params) {
// [CWE-94/95 조치] 화이트리스트 검증
if (typeof actions[actionName] === "function") {
logger.debug(`Executing safe action: ${actionName}`);
return actions[actionName](...params);
} else {
// [CWE-754] 부적절한 입력에 대한 예외 처리
logger.debug(`Invalid action attempt: ${actionName}`);
throw new Error("Invalid action");
}
}
// 실행 예시
try {
const result = safeAction("calculate", [10, 20]);
logger.debug(`Result: ${result}`);
} catch (e) {
// [CWE-209] 사용자에게는 최소한의 정보만 노출
logger.debug("A system error occurred during execution.");
}
코멘트: 자바스크립트 보안의 황금률은 "eval is evil(eval은 악이다)"입니다. 동적인 로직이 필요할 때는 언제나 대괄호 표기법이나 고정된 매핑 테이블을 먼저 떠올리십시오. logger.debug()를 통해 실행 흐름을 추적하되, 외부의 입력이 실행 엔진의 명령어(Instruction)로 변질되는 통로를 원천 봉쇄하는 것이 진정한 시큐어 코딩의 시작입니다.
다음의 예제코드는 동적 실행함수를 사용하였으나 외부 입력값을 실행하지 않아 취약하지 않다고 판정한다.
오탐코드의 예
1: <script>
-
2: var obj = {};
-
3: for(var i = 0; i < 5; i++){
4: eval("obj.test" + i + "=" + i); 5: }
6: console.log(obj);
7: </script>
댓글 달기