설명
코드 인젝션은 외부 입력이 단순한 데이터가 아니라 실행 가능한 프로그램 구조로 해석될 때 발생합니다. C#에서 신뢰할 수 없는 문자열을 Roslyn 스크립팅, CS-Script 또는 동적 컴파일 API에 전달하면, 스크립트가 평가되거나 생성된 코드·어셈블리가 실행될 때 애플리케이션 권한으로 파일, 네트워크, 환경 변수, 프로세스와 같은 자원에 접근할 수 있습니다.
EvaluateAsync, RunAsync, Eval, LoadCode, LoadMethod와 같은 API는 코드를 평가하거나 로드합니다. CSharpScript.Create, CompileCode, CompileAssemblyFromSource와 같은 API는 호출 시점에 코드를 바로 실행하지 않을 수 있지만, 이후 로드하거나 호출할 수 있는 공격자가 제어하는 스크립트 또는 어셈블리를 만듭니다. 따라서 두 경우 모두 신뢰 경계를 확인해야 합니다.
잠재적 영향
- 애플리케이션 권한으로 임의 코드 또는 명령을 실행할 수 있습니다.
- 파일과 데이터베이스의 기밀성·무결성·가용성이 침해될 수 있습니다.
- 내부 네트워크 접근, 자격 증명 탈취, 권한 상승 또는 서비스 거부로 이어질 수 있습니다.
해결 방법
가장 안전한 방법은 신뢰할 수 없는 입력을 C# 코드로 해석하지 않는 것입니다.
- 요청 값은 작업 식별자로만 사용하고,
add,subtract처럼 허용된 식별자를 고정된 애플리케이션 함수에 매핑합니다. - 동적 표현식이 꼭 필요하면 C#을 축소하려 하지 말고, 허용할 구문과 연산을 명시한 목적별 문법을 파싱합니다. 입력 길이, 구문 깊이, 실행 시간과 메모리도 제한합니다.
- 기존 통합에서 여러 스크립트 중 하나를 선택해야 한다면 개발자가 관리하는 변경 불가능한 카탈로그에서 완전한 스크립트를 선택합니다. 사용자 입력을 코드 조각에 이어 붙이지 않습니다.
- 신뢰할 수 없는 C# 실행이 제품 요구사항상 불가피하면 애플리케이션 프로세스 안에서 실행하지 않습니다. 별도의 제한된 계정으로 실행되는 프로세스, 컨테이너 또는 가상 머신을 사용하고 운영 체제 수준에서 파일 시스템, 네트워크, 프로세스 권한, CPU 시간과 메모리를 제한합니다.
ScriptOptions에서 참조나 import를 줄이거나 unsafe 구문을 금지하는 것만으로는 보안 경계가 만들어지지 않습니다. C# 및 .NET API 표면 전체를 완전하게 제한하기 어렵고, 무한 루프나 메모리 고갈 같은 공격은 참조 제한만으로 막을 수 없습니다.
CodeDomProvider.CompileAssemblyFromSource는 레거시 .NET Framework용 API입니다. Microsoft 문서에 따르면 .NET Core 및 .NET 5 이상에서는 항상 PlatformNotSupportedException을 발생시키므로 최신 .NET의 해결 방법이나 새 구현 예제로 사용하면 안 됩니다.
예시
변경 전
다음 코드는 HTTP 요청 본문을 C# 스크립트로 직접 평가합니다.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.CSharp.Scripting;
[ApiController]
[Route("api/scripts")]
public class ScriptController : ControllerBase
{
[HttpPost]
public Task<object> Run([FromBody] string code)
{
return CSharpScript.EvaluateAsync(code);
}
}
변경 후
다음 코드는 요청 값을 코드로 실행하지 않고 고정된 연산을 선택하는 데이터로만 사용합니다.
using Microsoft.AspNetCore.Mvc;
public record CalculationRequest(string Operation, decimal Left, decimal Right);
[ApiController]
[Route("api/calculations")]
public class CalculationController : ControllerBase
{
[HttpPost]
public IActionResult Calculate([FromBody] CalculationRequest request)
{
return request.Operation switch
{
"add" => Ok(request.Left + request.Right),
"subtract" => Ok(request.Left - request.Right),
_ => BadRequest("Unsupported operation")
};
}
}
참조
- CWE-94: Improper Control of Generation of Code
- OWASP Top 10:2025 A05 - Injection
- OWASP Top 10:2021 A03 - Injection
- Roslyn Scripting API Samples
- Roslyn: Is running untrusted compiled code safe?
- Microsoft Learn: CodeDomProvider.CompileAssemblyFromSource
- CS-Script IEvaluator API
- CS-Script Script Syntax
- KISA 소프트웨어 보안약점 진단가이드 2021