Skip to content

Commit 5274e8d

Browse files
authoredMar 6, 2025··
Fix: Enhance JSON Deserialization Security - Mitigate TypeNameHandling Vulnerability (#7423)
[Bug 30973440](https://msazure.visualstudio.com/One/_workitems/edit/30973440) and [Bug 30973442](https://msazure.visualstudio.com/One/_workitems/edit/30973442) CodeQL issue: https://liquid.microsoft.com/codeql/issues/621b3860-9992-462a-8a9d-0a24593f51a5?copilot_promptid=E91B0CE9-0C1B-4AC2-8A46-33F49B67E058 This commit addresses a potential security vulnerability **within our test code and infrastructure** related to JSON deserialization by enhancing the type handling mechanism. **Issue:** The previous deserialization configuration was using `TypeNameHandling.Auto` in Newtonsoft.Json. `TypeNameHandling.Auto` allows for automatic deserialization of types based on `$type` metadata embedded in the JSON. If an attacker can control the JSON input, they can potentially inject malicious `$type` properties to instantiate arbitrary types, leading to Remote Code Execution (RCE) vulnerabilities. This is related to our test infrastructure, so the potential security impact does not include production code running on customers' devices **Fix Implemented:** To mitigate this risk, the following changes have been made: 1. **Disabled Automatic Type Name Handling (`TypeNameHandling.None`):** - The `TypeNameHandling` setting in `JsonSerializerSettings` has been explicitly set to `TypeNameHandling.None`. - This is because the serialized JSON in our use case does not include `$type` metadata. Setting `TypeNameHandling.None` ensures that automatic `$type` processing is completely disabled, further enhancing security. 2. **Implemented Secure Deserialization with KnownTypes Whitelist:** - Updated TypeNameSerializationBinder binder to utilize a `KnownTypes` whitelist, explicitly defining the set of allowed types that can be deserialized. - The deserializer is now configured to use this `SerializationBinder`, ensuring that only types present in the `KnownTypes` whitelist are permitted for deserialization. This significantly restricts the attack surface and prevents the instantiation of unauthorized or potentially malicious types. - This approach aligns with secure deserialization best practices and follows the guidance outlined in: [https://liquid.microsoft.com/Web/Object/Read/MS.Security/Requirements/Microsoft.Security.SystemsADM.10010#Zguide](https://liquid.microsoft.com/Web/Object/Read/MS.Security/Requirements/Microsoft.Security.SystemsADM.10010#Zguide) and recommendation: Solution using custom ISerializationBinder: [https://liquid.microsoft.com/Web/Object/Read/ScanningToolWarnings/Requirements/CodeQL.SM02211#Zguide](https://liquid.microsoft.com/Web/Object/Read/ScanningToolWarnings/Requirements/CodeQL.SM02211#Zguide) **Tested the changes in the local:** ![image](https://github.com/user-attachments/assets/884e1f4b-aedd-42ba-9727-9d1f4089c4d2) **References:** https://liquid.microsoft.com/Web/Object/Read/ScanningToolWarnings/Requirements/CodeQL.SM02211#Zguide https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2326 https://liquid.microsoft.com/Web/Object/Read/MS.Security/Requirements/Microsoft.Security.SystemsADM.10010#Zguide ## Azure IoT Edge PR checklist:
1 parent ceac627 commit 5274e8d

File tree

2 files changed

+23
-4
lines changed

2 files changed

+23
-4
lines changed
 

‎edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.Integration.Test/AgentTestsBase.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,14 @@ public static IEnumerable<object[]> GenerateStartTestData(string testConfig)
3838

3939
string testConfigPath = Path.Combine(TestConfigBasePath, testConfig);
4040
string json = File.ReadAllText(testConfigPath);
41+
var knownTypesList = new List<Type>
42+
{
43+
typeof(ModulePriorityValidator)
44+
};
4145
var settings = new JsonSerializerSettings()
4246
{
4347
TypeNameHandling = TypeNameHandling.Auto,
44-
SerializationBinder = new TypeNameSerializationBinder(format),
48+
SerializationBinder = new TypeNameSerializationBinder(format, knownTypesList),
4549
Converters = new List<JsonConverter>
4650
{
4751
new ModuleSetJsonConverter(),
@@ -253,7 +257,7 @@ public DeploymentConfigJsonConverter()
253257

254258
this.settings = new JsonSerializerSettings()
255259
{
256-
TypeNameHandling = TypeNameHandling.Auto,
260+
TypeNameHandling = TypeNameHandling.None, // Security: $type is not used in json, enforcing explicit type control.
257261
Converters = new List<JsonConverter>
258262
{
259263
new TypeSpecificJsonConverter(deserializerTypes)
@@ -313,7 +317,7 @@ public ModuleSetJsonConverter()
313317

314318
this.settings = new JsonSerializerSettings()
315319
{
316-
TypeNameHandling = TypeNameHandling.Auto,
320+
TypeNameHandling = TypeNameHandling.None, // Security: $type is not used in json, enforcing explicit type control.
317321
Converters = new List<JsonConverter>
318322
{
319323
new TypeSpecificJsonConverter(deserializerTypes)

‎edge-util/test/Microsoft.Azure.Devices.Edge.Util.Test.Common/TypeNameSerializationBinder.cs

+16-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
namespace Microsoft.Azure.Devices.Edge.Util.Test.Common
33
{
44
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
57
using Newtonsoft.Json.Serialization;
68

79
/// <summary>
@@ -13,11 +15,23 @@ public class TypeNameSerializationBinder : ISerializationBinder
1315
{
1416
public readonly string TypeFormat;
1517

18+
// KnownTypes provides a allow-list of allowed types for deserialization, mitigating potential security vulnerabilities
19+
// associated with uncontrolled type deserialization when using TypeNameHandling
20+
// Secure Deserialization Best Practices: https://liquid.microsoft.com/Web/Object/Read/MS.Security/Requirements/Microsoft.Security.SystemsADM.10010#Zguide
21+
// CodeQL Scanning Tool Warning: https://liquid.microsoft.com/Web/Object/Read/ScanningToolWarnings/Requirements/CodeQL.SM02211#Zguide
22+
public IList<Type> KnownTypes { get; set; }
23+
1624
public TypeNameSerializationBinder(string typeFormat)
1725
{
1826
this.TypeFormat = typeFormat;
1927
}
2028

29+
public TypeNameSerializationBinder(string typeFormat, IList<Type> knownTypes)
30+
{
31+
this.TypeFormat = typeFormat;
32+
this.KnownTypes = knownTypes;
33+
}
34+
2135
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
2236
{
2337
assemblyName = null;
@@ -26,7 +40,8 @@ public void BindToName(Type serializedType, out string assemblyName, out string
2640

2741
public Type BindToType(string assemblyName, string typeName)
2842
{
29-
return Type.GetType(string.Format(this.TypeFormat, typeName), true);
43+
Type resolvedType = Type.GetType(string.Format(this.TypeFormat, typeName), true);
44+
return this.KnownTypes.SingleOrDefault(t => t == resolvedType );
3045
}
3146
}
3247
}

0 commit comments

Comments
 (0)
Please sign in to comment.