Skip to content

Commit 1d065a9

Browse files
committed
Merge branch 'dev'
2 parents be40344 + 10ac4e7 commit 1d065a9

40 files changed

+1173
-611
lines changed

RELEASE_NOTES.md

+24
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
#### 1.4.24 August 17 2021 ####
2+
**Maintenance Release for Akka.NET 1.4**
3+
4+
**Bug Fixes and Improvements**
5+
6+
* [Akka: Make `Router` open to extensions](https://github.com/akkadotnet/akka.net/pull/5201)
7+
* [Akka: Allow null response to `Ask<T>`](https://github.com/akkadotnet/akka.net/pull/5205)
8+
* [Akka.Cluster: Fix cluster startup race condition](https://github.com/akkadotnet/akka.net/pull/5185)
9+
* [Akka.Streams: Fix RestartFlow bug](https://github.com/akkadotnet/akka.net/pull/5181)
10+
* [Akka.Persistence.Sql: Implement TimestampProvider in BatchingSqlJournal](https://github.com/akkadotnet/akka.net/pull/5192)
11+
* [Akka.Serialization.Hyperion: Bump Hyperion version from 0.11.0 to 0.11.1](https://github.com/akkadotnet/akka.net/pull/5206)
12+
* [Akka.Serialization.Hyperion: Add Hyperion unsafe type filtering security feature](https://github.com/akkadotnet/akka.net/pull/5208)
13+
14+
You can [see the full set of changes introduced in Akka.NET v1.4.24 here](https://github.com/akkadotnet/akka.net/milestone/55?closed=1)
15+
16+
| COMMITS | LOC+ | LOC- | AUTHOR |
17+
| --- | --- | --- | --- |
18+
| 5 | 360 | 200 | Aaron Stannard |
19+
| 3 | 4 | 4 | dependabot[bot] |
20+
| 1 | 548 | 333 | Arjen Smits |
21+
| 1 | 42 | 19 | Martijn Schoemaker |
22+
| 1 | 26 | 27 | Andreas Dirnberger |
23+
| 1 | 171 | 27 | Gregorius Soedharmo |
24+
125
#### 1.4.23 August 09 2021 ####
226
**Maintenance Release for Akka.NET 1.4**
327

docs/articles/networking/serialization.md

+51
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,57 @@ akka {
304304
}
305305
```
306306

307+
## Danger of polymorphic serializer
308+
One of the danger of polymorphic serializers is the danger of unsafe object type injection into
309+
the serialization-deserialization chain. This issue applies to any type of polymorphic serializer,
310+
including JSON, BinaryFormatter, etc. In Akka, this issue primarily affects developers who allow third parties to pass messages directly
311+
to unsecured Akka.Remote endpoints, a [practice that we do not encourage](https://getakka.net/articles/remoting/security.html#akkaremote-with-virtual-private-networks).
312+
313+
Generally, there are two approaches you can take to alleviate this problem:
314+
1. Implement a schema-based serialization that are contract bound, which is more expensive to setup at first but fundamentally faster and more secure.
315+
2. Implement a filtering or blacklist to block dangerous types.
316+
317+
An example of using a schema-based serialization in Akka can be read under the title "Using Google
318+
Protocol Buffers to Version State and Messages" in [this documentation](https://petabridge.com/cluster/lesson3)
319+
320+
Hyperion chose to implement the second approach by blacklisting a set of potentially dangerous types
321+
from being deserialized:
322+
323+
- System.Security.Claims.ClaimsIdentity
324+
- System.Windows.Forms.AxHost.State
325+
- System.Windows.Data.ObjectDataProvider
326+
- System.Management.Automation.PSObject
327+
- System.Web.Security.RolePrincipal
328+
- System.IdentityModel.Tokens.SessionSecurityToken
329+
- SessionViewStateHistoryItem
330+
- TextFormattingRunProperties
331+
- ToolboxItemContainer
332+
- System.Security.Principal.WindowsClaimsIdentity
333+
- System.Security.Principal.WindowsIdentity
334+
- System.Security.Principal.WindowsPrincipal
335+
- System.CodeDom.Compiler.TempFileCollection
336+
- System.IO.FileSystemInfo
337+
- System.Activities.Presentation.WorkflowDesigner
338+
- System.Windows.ResourceDictionary
339+
- System.Windows.Forms.BindingSource
340+
- Microsoft.Exchange.Management.SystemManager.WinForms.ExchangeSettingsProvider
341+
- System.Diagnostics.Process
342+
- System.Management.IWbemClassObjectFreeThreaded
343+
344+
Be warned that these class can be used as a man in the middle attack vector, but if you need
345+
to serialize one of these class, you can turn off this feature using this inside your HOCON settings:
346+
```
347+
akka.actor.serialization-settings.hyperion.disallow-unsafe-type = false
348+
```
349+
350+
> [!IMPORTANT]
351+
> This feature is turned on as default since Akka.NET v1.4.24
352+
353+
> [!WARNING]
354+
> Hyperion is __NOT__ designed as a safe serializer to be used in an open network as a client-server
355+
> communication protocol, instead it is designed to be used as a server-server communication protocol,
356+
> preferably inside a closed network system.
357+
307358
## Cross platform serialization compatibility in Hyperion
308359
There are problems that can arise when migrating from old .NET Framework to the new .NET Core standard, mainly because of breaking namespace and assembly name changes between these platforms.
309360
Hyperion implements a generic way of addressing this issue by transforming the names of these incompatible names during deserialization.

src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
10+
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
1111
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
1212
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
1313
<!-- FluentAssertions is used in some benchmarks to validate internal behaviors -->

src/benchmark/SerializationBenchmarks/SerializationBenchmarks.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
9+
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
1010
</ItemGroup>
1111

1212
<ItemGroup>

src/common.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<PropertyGroup>
1212
<XunitVersion>2.4.1</XunitVersion>
1313
<TestSdkVersion>16.10.0</TestSdkVersion>
14-
<HyperionVersion>0.11.0</HyperionVersion>
14+
<HyperionVersion>0.11.1</HyperionVersion>
1515
<NewtonsoftJsonVersion>[12.0.3,)</NewtonsoftJsonVersion>
1616
<NBenchVersion>2.0.1</NBenchVersion>
1717
<ProtobufVersion>3.17.3</ProtobufVersion>

src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonManager.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,7 @@ private void InitializeFSM()
907907
if (e.FsmEvent is OldestChangedBuffer.OldestChanged oldestChanged && e.StateData is YoungerData youngerData)
908908
{
909909
_oldestChangedReceived = true;
910-
if (oldestChanged.Oldest.Equals(_selfUniqueAddress))
910+
if (oldestChanged.Oldest != null && oldestChanged.Oldest.Equals(_selfUniqueAddress))
911911
{
912912
Log.Info("Younger observed OldestChanged: [{0} -> myself]", youngerData.Oldest.Head()?.Address);
913913

src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/BatchingSqlJournal.cs

+16-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using Akka.Event;
2323
using Akka.Pattern;
2424
using Akka.Persistence.Journal;
25+
using Akka.Persistence.Sql.Common.Journal;
2526
using Akka.Serialization;
2627
using Akka.Util;
2728
using Akka.Util.Internal;
@@ -233,7 +234,12 @@ public abstract class BatchingSqlJournalSetup
233234
/// <summary>
234235
/// The default serializer used when not type override matching is found
235236
/// </summary>
236-
public string DefaultSerializer { get; }
237+
public string DefaultSerializer { get; }
238+
239+
/// <summary>
240+
/// The fully qualified name of the type that should be used as timestamp provider.
241+
/// </summary>
242+
public string TimestampProviderTypeName { get; }
237243

238244
/// <summary>
239245
/// Initializes a new instance of the <see cref="BatchingSqlJournalSetup" /> class.
@@ -293,6 +299,7 @@ protected BatchingSqlJournalSetup(Config config, QueryConfiguration namingConven
293299
ReplayFilterSettings = new ReplayFilterSettings(config.GetConfig("replay-filter"));
294300
NamingConventions = namingConventions;
295301
DefaultSerializer = config.GetString("serializer", null);
302+
TimestampProviderTypeName = config.GetString("timestamp-provider", null);
296303
}
297304

298305
/// <summary>
@@ -528,6 +535,11 @@ public RequestChunk(int chunkId, IJournalRequest[] requests)
528535
/// </summary>
529536
protected readonly bool CanPublish;
530537

538+
/// <summary>
539+
/// The timestamp provider that will be used for the timestamp column when writing messages to the database.
540+
/// </summary>
541+
protected ITimestampProvider TimestampProvider { get; }
542+
531543
/// <summary>
532544
/// Logging adapter for current journal actor .
533545
/// </summary>
@@ -562,7 +574,8 @@ public RequestChunk(int chunkId, IJournalRequest[] requests)
562574
protected BatchingSqlJournal(BatchingSqlJournalSetup setup)
563575
{
564576
Setup = setup;
565-
CanPublish = Persistence.Instance.Apply(Context.System).Settings.Internal.PublishPluginCommands;
577+
CanPublish = Persistence.Instance.Apply(Context.System).Settings.Internal.PublishPluginCommands;
578+
TimestampProvider = TimestampProviderProvider.GetTimestampProvider(setup.TimestampProviderTypeName, Context);
566579

567580
_persistenceIdSubscribers = new Dictionary<string, HashSet<IActorRef>>();
568581
_tagSubscribers = new Dictionary<string, HashSet<IActorRef>>();
@@ -1286,7 +1299,7 @@ private async Task<WriteMessagesResult> HandleWriteMessages(WriteMessages req, T
12861299
persistent = persistent.WithPayload(tagged.Payload);
12871300
}
12881301

1289-
WriteEvent(command, persistent.WithTimestamp(DateTime.UtcNow.Ticks), tagBuilder.ToString());
1302+
WriteEvent(command, persistent.WithTimestamp(TimestampProvider.GenerateTimestamp(persistent)), tagBuilder.ToString());
12901303

12911304
await command.ExecuteNonQueryAsync();
12921305

src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/ITimestampProvider.cs

+18
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//-----------------------------------------------------------------------
77

88
using System;
9+
using Akka.Actor;
910

1011
namespace Akka.Persistence.Sql.Common.Journal
1112
{
@@ -34,4 +35,21 @@ public sealed class DefaultTimestampProvider : ITimestampProvider
3435
/// <returns>TBD</returns>
3536
public long GenerateTimestamp(IPersistentRepresentation message) => DateTime.UtcNow.Ticks;
3637
}
38+
39+
public static class TimestampProviderProvider
40+
{
41+
public static ITimestampProvider GetTimestampProvider(string typeName, IActorContext context)
42+
{
43+
if (string.IsNullOrEmpty(typeName))
44+
{
45+
return new DefaultTimestampProvider();
46+
}
47+
48+
var type = Type.GetType(typeName, true);
49+
var withSystem = type.GetConstructor(new[] { context.System.GetType() }) != null;
50+
return withSystem ?
51+
(ITimestampProvider)Activator.CreateInstance(type, context.System) :
52+
(ITimestampProvider)Activator.CreateInstance(type);
53+
}
54+
}
3755
}

src/contrib/persistence/Akka.Persistence.Sql.Common/Journal/SqlJournal.cs

+2-16
Original file line numberDiff line numberDiff line change
@@ -514,21 +514,7 @@ protected virtual string GetConnectionString()
514514
return connectionString;
515515
}
516516

517-
#region obsoleted
518-
519-
/// <summary>
520-
/// TBD
521-
/// </summary>
522-
/// <param name="typeName">TBD</param>
523-
/// <returns>TBD</returns>
524-
protected ITimestampProvider GetTimestampProvider(string typeName)
525-
{
526-
var type = Type.GetType(typeName, true);
527-
var withSystem = type.GetConstructor(new[] { Context.System.GetType() }) != null;
528-
return withSystem ?
529-
(ITimestampProvider)Activator.CreateInstance(type, Context.System) :
530-
(ITimestampProvider)Activator.CreateInstance(type);
531-
}
532-
#endregion
517+
protected ITimestampProvider GetTimestampProvider(string typeName) =>
518+
TimestampProviderProvider.GetTimestampProvider(typeName, Context);
533519
}
534520
}

src/contrib/persistence/Akka.Persistence.Sqlite/Akka.Persistence.Sqlite.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
</ItemGroup>
1717

1818
<ItemGroup>
19-
<PackageReference Include="Microsoft.Data.SQLite" Version="5.0.8" />
19+
<PackageReference Include="Microsoft.Data.SQLite" Version="5.0.9" />
2020
</ItemGroup>
2121

2222
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">

src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionConfigTests.cs

+5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public void Hyperion_serializer_should_have_correct_defaults()
3535
Assert.True(serializer.Settings.VersionTolerance);
3636
Assert.True(serializer.Settings.PreserveObjectReferences);
3737
Assert.Equal("NoKnownTypes", serializer.Settings.KnownTypesProvider.Name);
38+
Assert.True(serializer.Settings.DisallowUnsafeType);
3839
}
3940
}
4041

@@ -50,6 +51,7 @@ public void Hyperion_serializer_should_allow_to_setup_custom_flags()
5051
serialization-settings.hyperion {
5152
preserve-object-references = false
5253
version-tolerance = false
54+
disallow-unsafe-type = false
5355
}
5456
}
5557
");
@@ -59,6 +61,7 @@ public void Hyperion_serializer_should_allow_to_setup_custom_flags()
5961
Assert.False(serializer.Settings.VersionTolerance);
6062
Assert.False(serializer.Settings.PreserveObjectReferences);
6163
Assert.Equal("NoKnownTypes", serializer.Settings.KnownTypesProvider.Name);
64+
Assert.False(serializer.Settings.DisallowUnsafeType);
6265
}
6366
}
6467

@@ -82,6 +85,7 @@ public void Hyperion_serializer_should_allow_to_setup_custom_types_provider_with
8285
Assert.True(serializer.Settings.VersionTolerance);
8386
Assert.True(serializer.Settings.PreserveObjectReferences);
8487
Assert.Equal(typeof(DummyTypesProviderWithDefaultCtor), serializer.Settings.KnownTypesProvider);
88+
Assert.True(serializer.Settings.DisallowUnsafeType);
8589
}
8690
}
8791

@@ -105,6 +109,7 @@ public void Hyperion_serializer_should_allow_to_setup_custom_types_provider_with
105109
Assert.True(serializer.Settings.VersionTolerance);
106110
Assert.True(serializer.Settings.PreserveObjectReferences);
107111
Assert.Equal(typeof(DummyTypesProvider), serializer.Settings.KnownTypesProvider);
112+
Assert.True(serializer.Settings.DisallowUnsafeType);
108113
}
109114
}
110115

src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionSerializerSetupSpec.cs

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
35
using System.Linq;
6+
using System.Runtime.InteropServices;
7+
using System.Runtime.Serialization;
8+
using System.Security.Claims;
9+
using System.Security.Principal;
410
using System.Text;
511
using System.Threading.Tasks;
612
using Akka.Actor;
@@ -36,14 +42,16 @@ public void Setup_should_be_converted_to_settings_correctly()
3642
{
3743
var setup = HyperionSerializerSetup.Empty
3844
.WithPreserveObjectReference(true)
39-
.WithKnownTypeProvider<NoKnownTypes>();
45+
.WithKnownTypeProvider<NoKnownTypes>()
46+
.WithDisallowUnsafeType(false);
4047
var settings =
4148
new HyperionSerializerSettings(
4249
false,
4350
false,
4451
typeof(DummyTypesProvider),
4552
new Func<string, string>[] { s => $"{s}.." },
46-
new Surrogate[0]);
53+
new Surrogate[0],
54+
true);
4755
var appliedSettings = setup.ApplySettings(settings);
4856

4957
appliedSettings.PreserveObjectReferences.Should().BeTrue(); // overriden
@@ -52,6 +60,7 @@ public void Setup_should_be_converted_to_settings_correctly()
5260
appliedSettings.PackageNameOverrides.Count().Should().Be(1); // from settings
5361
appliedSettings.PackageNameOverrides.First()("a").Should().Be("a..");
5462
appliedSettings.Surrogates.ToList().Count.Should().Be(0); // from settings
63+
appliedSettings.DisallowUnsafeType.ShouldBe(false); // overriden
5564
}
5665

5766
[Fact]
@@ -115,5 +124,31 @@ public void Setup_surrogate_should_work()
115124
surrogated.Count.Should().Be(1);
116125
surrogated[0].Should().BeEquivalentTo(expected);
117126
}
127+
128+
[Theory]
129+
[MemberData(nameof(DangerousObjectFactory))]
130+
public void Setup_disallow_unsafe_type_should_work(object dangerousObject, Type type)
131+
{
132+
var serializer = new HyperionSerializer((ExtendedActorSystem)Sys, HyperionSerializerSettings.Default);
133+
var serialized = serializer.ToBinary(dangerousObject);
134+
serializer.Invoking(s => s.FromBinary(serialized, type)).Should().Throw<SerializationException>();
135+
}
136+
137+
public static IEnumerable<object[]> DangerousObjectFactory()
138+
{
139+
var isWindow = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
140+
141+
yield return new object[]{ new FileInfo("C:\\Windows\\System32"), typeof(FileInfo) };
142+
yield return new object[]{ new ClaimsIdentity(), typeof(ClaimsIdentity)};
143+
if (isWindow)
144+
{
145+
yield return new object[]{ WindowsIdentity.GetAnonymous(), typeof(WindowsIdentity) };
146+
yield return new object[]{ new WindowsPrincipal(WindowsIdentity.GetAnonymous()), typeof(WindowsPrincipal)};
147+
}
148+
#if NET471
149+
yield return new object[]{ new Process(), typeof(Process)};
150+
#endif
151+
yield return new object[]{ new ClaimsIdentity(), typeof(ClaimsIdentity)};
152+
}
118153
}
119154
}

0 commit comments

Comments
 (0)