주의
BinaryFormatter 지원은 권장되지 않습니다. 새 형식 안전 API로 즉시 마이그레이션할 수 없는 레거시 애플리케이션에 대한 임시 마이그레이션 브리지로만 사용합니다. 이 방법은 상당한 보안 위험을 수반합니다.
이 문서에서는 .NET 10에서 Windows Forms 클립보드 작업에 대한 제한된 BinaryFormatter 지원을 구성하는 방법을 보여 줍니다.
BinaryFormatter 보안 취약성으로 인해 .NET 9의 런타임에서 제거되었지만 마이그레이션 시간이 필요한 레거시 애플리케이션에 대한 명시적 구성을 통해 제한된 기능을 복원합니다.
형식이 안전한 새 API에 대한 전체 마이그레이션 지침은 .NET 10의 Windows Forms 클립보드 및 DataObject 변경 내용을 참조하세요.
중요합니다
이 콘텐츠는 별도로 명시되지 않는 한 .NET Framework가 아닌 최신 .NET에만 적용됩니다.
필수 조건
계속하기 전에 다음 개념을 검토합니다.
- 애플리케이션이 현재 클립보드 작업에서 사용하는
BinaryFormatter방식입니다. - 제거를
BinaryFormatter초래한 보안 취약성. - 귀하의 새 형식 안전 클립보드 API로의 마이그레이션 타임라인.
자세한 내용은 다음 문서를 참조하세요.
보안 경고 및 위험
BinaryFormatter 는 기본적으로 안전하지 않으며 다음과 같은 이유로 더 이상 사용되지 않습니다.
- 임의 코드 실행 취약성: 공격자는 역직렬화하는 동안 악성 코드를 실행하여 애플리케이션을 원격 공격에 노출할 수 있습니다.
- 서비스 거부 공격: 악의적인 클립보드 데이터는 과도한 메모리 또는 CPU 리소스를 소비하여 충돌 또는 불안정을 일으킬 수 있습니다.
- 정보 공개 위험: 공격자가 메모리에서 중요한 데이터를 추출할 수 있습니다.
- 보안 경계 없음: 형식은 근본적으로 안전하지 않으며 구성 설정은 보안을 설정할 수 없습니다.
새 형식 안전 API를 사용하도록 애플리케이션을 업데이트하는 동안 이 지원을 임시 브리지로만 사용하도록 설정합니다.
호환성 패키지 설치
지원 BinaryFormatter 되지 않는 호환성 패키지를 프로젝트에 추가합니다. 이 패키지는 BinaryFormatter 작업을 위한 필수 런타임 지원을 제공합니다.
<ItemGroup>
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="10.0.0*-*"/>
</ItemGroup>
비고
이 패키지는 지원되지 않고 사용되지 않는 것으로 표시됩니다. 마이그레이션 중에 임시 호환성을 위해서만 사용합니다.
프로젝트에서 안전하지 않은 직렬화 기능을 활성화하세요.
프로젝트 파일에서 EnableUnsafeBinaryFormatterSerialization 속성을 true으로 설정합니다. 이 속성은 BinaryFormatter 사용량을 허용하도록 컴파일러에 지시합니다.
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
이 설정이 없으면 애플리케이션이 API를 사용 BinaryFormatter 하려고 할 때 컴파일 오류가 생성됩니다.
Windows Forms 런타임 스위치 구성
Windows Forms 관련 클립보드 스위치를 사용하도록 애플리케이션의 runtimeconfig.json 파일을 만들거나 업데이트합니다. 필요한 경우 이 구성은 클립보드 작업이 BinaryFormatter로 되돌아가도록 허용합니다.
{
"runtimeOptions": {
"configProperties": {
"Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization": true
}
}
}
중요합니다
이 특정 런타임 스위치가 없으면, 일반적인 serialization 지원이 활성화되어 있어도 클립보드 작업을 BinaryFormatter로 대체할 수 없습니다. 이 스위치는 특히 Windows Forms 및 WPF 클립보드 기능에 필요합니다.
보안에 중점을 둔 타입 해석기 구현
활성화된 경우에도 BinaryFormatter 형식 확인자를 구현하여 역직렬화를 명시적으로 승인된 형식으로 제한합니다. 형식 확인자는 악의적인 페이로드 공격에 대한 유일한 방어 기능을 제공합니다.
보안 형식 확인자 만들기
// Create a security-focused type resolver
private static Type SecureTypeResolver(TypeName typeName)
{
// Explicit allow-list of permitted types—add only what you need
var allowedTypes = new Dictionary<string, Type>
{
["MyApp.Person"] = typeof(Person),
["MyApp.AppSettings"] = typeof(AppSettings),
["System.String"] = typeof(string),
["System.Int32"] = typeof(int),
// Add only the specific types your application requires
};
// Only allow explicitly listed types - exact string match required
if (allowedTypes.TryGetValue(typeName.FullName, out Type allowedType))
{
return allowedType;
}
// Reject any type not in the allow-list with clear error message
throw new InvalidOperationException(
$"Type '{typeName.FullName}' is not permitted for clipboard deserialization");
}
' Create a security-focused type resolver
Private Shared Function SecureTypeResolver(typeName As TypeName) As Type
' Explicit allow-list of permitted types—add only what you need
Dim allowedTypes As New Dictionary(Of String, Type) From {
{"MyApp.Person", GetType(Person)},
{"MyApp.AppSettings", GetType(AppSettings)},
{"System.String", GetType(String)},
{"System.Int32", GetType(Integer)}
} ' Add only the specific types your application requires
' Only allow explicitly listed types - exact string match required
Dim allowedType As Type = Nothing
If allowedTypes.TryGetValue(typeName.FullName, allowedType) Then
Return allowedType
End If
' Reject any type not in the allow-list with clear error message
Throw New InvalidOperationException(
$"Type '{typeName.FullName}' is not permitted for clipboard deserialization")
End Function
클립보드 작업에서 형식 확인자 사용
// Use the resolver with clipboard operations
private static Type SecureTypeResolver(TypeName typeName)
{
// Implementation from SecureTypeResolver example
// ... (allow-list implementation here)
throw new InvalidOperationException($"Type '{typeName.FullName}' is not permitted");
}
public static void UseSecureTypeResolver()
{
// Retrieve legacy data using the secure type resolver
if (Clipboard.TryGetData("LegacyData", SecureTypeResolver, out MyCustomType data))
{
ProcessLegacyData(data);
}
else
{
Console.WriteLine("No compatible data found on clipboard");
}
}
' Use the resolver with clipboard operations
Private Shared Function SecureTypeResolver(typeName As TypeName) As Type
' Implementation from SecureTypeResolver example
' ... (allow-list implementation here)
Throw New InvalidOperationException($"Type '{typeName.FullName}' is not permitted")
End Function
Public Shared Sub UseSecureTypeResolver()
' Retrieve legacy data using the secure type resolver
Dim data As MyCustomType = Nothing
If Clipboard.TryGetData("LegacyData", AddressOf SecureTypeResolver, data) Then
ProcessLegacyData(data)
Else
Console.WriteLine("No compatible data found on clipboard")
End If
End Sub
형식 확인자에 대한 보안 지침
형식 확인자를 구현할 때 다음 필수 보안 지침을 따릅니다.
명시적 허용 목록 사용
- 기본적으로 거부: 명시적으로 나열된 형식만 허용합니다.
- 와일드카드 없음: 패턴 일치 또는 네임스페이스 기반 사용 권한을 사용하지 않습니다.
- 정확한 일치: 형식 이름에 대해 정확한 문자열 일치가 필요합니다.
모든 입력 유효성 검사
- 형식 이름 유효성 검사: 형식 이름이 예상 형식과 일치하는지 확인합니다.
- 어셈블리 제한: 형식을 알려진 신뢰할 수 있는 어셈블리로 제한합니다.
- 버전 검사: 버전별 형식 제한을 고려합니다.
알 수 없는 형식을 안전하게 처리
- 예외 발생: 항상 권한이 없는 형식에 대해 예외를 발생시킵니다.
- 로그 시도: 권한 없는 액세스 시도를 로깅하는 것이 좋습니다.
- 오류 메시지 지우기: 디버깅에 대한 특정 거부 이유를 제공합니다.
정기적인 유지 관리
- 정기적으로 감사: 허용된 형식 목록을 검토하고 업데이트합니다.
- 사용되지 않는 형식 제거: 더 이상 필요하지 않은 형식에 대한 사용 권한을 제거합니다.
- 문서 결정: 각 형식이 허용되는 이유에 대한 명확한 설명서를 유지 관리합니다.
구성 테스트
지원을 구성 BinaryFormatter 한 후 애플리케이션을 테스트하여 올바르게 작동하는지 확인합니다.
- 클립보드 작업 확인: 사용자 지정 형식으로 데이터 저장 및 검색을 모두 테스트합니다.
- 테스트 형식 확인자: 권한이 없는 형식이 제대로 거부되었는지 확인합니다.
- 보안 모니터링: 예기치 않은 형식 확인 시도를 감시합니다.
- 성능 테스트: 타입 판별기가 성능에 큰 영향을 미치지 않는지 확인합니다.
public static void TestBinaryFormatterConfiguration()
{
// Test data to verify configuration
var testPerson = new Person { Name = "Test User", Age = 30 };
try
{
// Test storing data (this should work with proper configuration)
Clipboard.SetData("TestPerson", testPerson);
Console.WriteLine("Successfully stored test data on clipboard");
// Test retrieving with type resolver
if (Clipboard.TryGetData("TestPerson", SecureTypeResolver, out Person retrievedPerson))
{
Console.WriteLine($"Successfully retrieved: {retrievedPerson.Name}, Age: {retrievedPerson.Age}");
}
else
{
Console.WriteLine("Failed to retrieve test data");
}
// Test that unauthorized types are rejected
try
{
Clipboard.TryGetData("TestPerson", UnauthorizedTypeResolver, out Person _);
Console.WriteLine("ERROR: Unauthorized type was not rejected!");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"SUCCESS: Unauthorized type properly rejected - {ex.Message}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Configuration test failed: {ex.Message}");
}
}
private static Type SecureTypeResolver(TypeName typeName)
{
var allowedTypes = new Dictionary<string, Type>
{
["ClipboardExamples.Person"] = typeof(Person),
};
if (allowedTypes.TryGetValue(typeName.FullName, out Type allowedType))
{
return allowedType;
}
throw new InvalidOperationException($"Type '{typeName.FullName}' is not permitted");
}
private static Type UnauthorizedTypeResolver(TypeName typeName)
{
// Intentionally restrictive resolver to test rejection
throw new InvalidOperationException($"No types are permitted by this test resolver");
}
Public Shared Sub TestBinaryFormatterConfiguration()
' Test data to verify configuration
Dim testPerson As New Person With {.Name = "Test User", .Age = 30}
Try
' Test storing data (this should work with proper configuration)
Clipboard.SetData("TestPerson", testPerson)
Console.WriteLine("Successfully stored test data on clipboard")
' Test retrieving with type resolver
Dim retrievedPerson As Person = Nothing
If Clipboard.TryGetData("TestPerson", AddressOf SecureTypeResolver, retrievedPerson) Then
Console.WriteLine($"Successfully retrieved: {retrievedPerson.Name}, Age: {retrievedPerson.Age}")
Else
Console.WriteLine("Failed to retrieve test data")
End If
' Test that unauthorized types are rejected
Try
Dim testResult As Person = Nothing
Clipboard.TryGetData("TestPerson", AddressOf UnauthorizedTypeResolver, testResult)
Console.WriteLine("ERROR: Unauthorized type was not rejected!")
Catch ex As InvalidOperationException
Console.WriteLine($"SUCCESS: Unauthorized type properly rejected - {ex.Message}")
End Try
Catch ex As Exception
Console.WriteLine($"Configuration test failed: {ex.Message}")
End Try
End Sub
Private Shared Function SecureTypeResolver(typeName As TypeName) As Type
Dim allowedTypes As New Dictionary(Of String, Type) From {
{"ClipboardExamples.Person", GetType(Person)}
}
Dim allowedType As Type = Nothing
If allowedTypes.TryGetValue(typeName.FullName, allowedType) Then
Return allowedType
End If
Throw New InvalidOperationException($"Type '{typeName.FullName}' is not permitted")
End Function
Private Shared Function UnauthorizedTypeResolver(typeName As TypeName) As Type
' Intentionally restrictive resolver to test rejection
Throw New InvalidOperationException($"No types are permitted by this test resolver")
End Function
마이그레이션 전략 계획
새로운 타입 안전 API로 전환할 마이그레이션 계획을 개발하되, BinaryFormatter 지원은 임시 호환성을 제공합니다.
- 사용량 식별: 사용자 지정 형식을 사용하여 모든 클립보드 작업을 카탈로그로 지정합니다.
- 마이그레이션 우선 순위 지정: 가장 보안에 민감한 작업에 먼저 집중합니다.
- 증분 업데이트: 위험을 줄이기 위해 한 번에 하나의 작업을 마이그레이션합니다.
- 철저히 테스트: 새 구현이 동등한 기능을 제공하는지 확인합니다.
- BinaryFormatter 제거: 마이그레이션이 완료되면 지원을 사용하지 않도록 설정합니다.
자원을 정리하세요
형식이 안전한 새 클립보드 API로 마이그레이션한 후에는 보안을 강화하기 위해 구성을 BinaryFormatter 제거합니다.
- 패키지 참조
System.Runtime.Serialization.Formatters를 제거합니다. -
EnableUnsafeBinaryFormatterSerialization프로젝트 파일에서 속성을 제거합니다. -
runtimeconfig.json에서Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization설정을 제거합니다. - 더 이상 필요하지 않은 형식 확인자 구현을 삭제합니다.
관련 콘텐츠
.NET Desktop feedback