【问题标题】:C# best practice when serializing objects to file将对象序列化到文件时的 C# 最佳实践
【发布时间】:2010-12-17 23:03:18
【问题描述】:

我正在构建一个需要将对象保存到文件以保存用户数据的小应用程序。 关于我对这个文件的序列化,我有两个问题:

  1. 我正在创建的对象有一些公共属性和一个事件。我将[Serializable] 属性添加到我的对象,然后意识到我无法序列化包含事件的对象。 然后我发现我可以在我的事件[field:NonSerialized] 上方添加一个属性,它会起作用。这是最好的方法吗,还是我应该尝试在没有任何事件的情况下构建我的 Serializable 对象?

  2. 我正在序列化的对象保存了有关应用程序的一些用户设置。这些设置不够敏感,无法在文件中加密它们,但我仍然不希望在不打开我的应用程序的情况下手动篡改它们。当我通过Serialize() 方法使用普通的BinaryFormatter 对象将我的对象序列化到一个文件时,我在保存它的文件中看到了.net 对象类型的可读名称。有没有办法让某人在不使用我的程序的情况下对其进行逆向工程并查看保存的内容?有没有办法让某人构建一个小型应用程序并找出如何反序列化此文件中的信息?如果是这样,我将如何隐藏此文件中的信息?

在这种情况下,在将对象序列化为文件时,我还应该遵循其他提示/建议/最佳实践吗?

提前致谢!

【问题讨论】:

    标签: c# serialization


    【解决方案1】:

    如果你的对象实现了ISerializable接口,你可以自己控制所有存储/序列化的数据,也可以控制反序列化。

    如果您的项目及时发展,这一点很重要。因为您可能会删除某些属性、添加其他属性或更改行为。

    我总是在序列化包中添加一个版本。这样我就知道对象存储时的版本是什么,因此我知道如何反序列化它。

    [Serializable]
    class Example : ISerializable {
       private static const int VERSION = 3;
    
       public Example(SerializationInfo info, StreamingContext context) {
          var version = info.GetInt32("Example_Version", VERSION);
          if (version == 0) {
             // Restore properties for version 0
          }
          if (version == 1) {
             // ....
          }
       }
    
       void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
           info.AddValue("Example_Version", VERSION);
           // Your data here
       }
    
    }
    

    如果您不加密,“读取”您的数据将非常容易。非常简单,这意味着您可能需要投入几个小时。如果你存储的数据值得几天,这意味着它很容易,如果它只值几分钟,那就很难了。如果你明白了。

    加密数据的一种非常简单的方法是通过 ProtectedData 类使用 Windows DPAPI。

    【讨论】:

    • 你能告诉我(如果样本太长,可能有一个好文章的链接)如何以我决定序列化哪些属性的方式实现 ISerializable ?
    • 你在哪里跟踪每个序列化对象的版本?
    • 添加了一个例子。也遵循 ISerializable 链接,这也有一个很好的例子。如您所见,VERSION 只是一个简单的变量/常量。
    • +1 用于 ISerializable 实现。如果您要将数据保存到文件中,那么版本容差几乎是必需的。此外,此代码提供了最透明的方式(可读性)来实现版本容错序列化。
    【解决方案2】:

    1:使用 BinaryFormatter,是的 - 您需要 NonSerialized 事件(除非您实现 ISerializable,但这会增加很多工作);但是,我非常明确地说我根本不会在这里使用 BinaryFormatter。对您的类型进行一系列更改并不是很宽容。我会使用与您的代码内部联系较少的东西; XmlSerializer; DataContractSerializer、JavaScriptSerializer。我也可以建议二元替代方案; NetDataContractSerializer、protobuf-net(我自己的)等

    2:是的,几乎所有不涉及适当加密的实现,如果有人关心,他们可以逆向工程并获取字符串。所以这取决于它需要隐藏到什么程度。简单地通过 GZipStream 运行现有的序列化可能足以满足您的需求,但这只是防止随意检查的面具。它不会阻止任何有理由寻找数据的人。

    如果数据需要安全,您需要使用用户在应用启动时输入的密钥进行适当的加密,或者使用安全存储在用户个人资料中的证书之类的东西进行适当的加密。

    【讨论】:

    • 如果我使用 XmlSerializer 进行序列化,然后在将来我的对象发生变化,我猜这仍然会导致反序列化错误.. ?
    • 使用 XmlSerializable 我不需要为事件添加 NonSerialized ?
    • @gillyb XmlSerializer 忽略事件,只查看公共属性/字段(但:不要使用公共字段)。您可以将整个类型系统交换为日志,因为属性仍然有意义。
    • 我不明白你所说的“只要属性仍然有意义就交换整个类型系统”???
    • 在我的例子中,我序列化的对象实现了 INotifyPropertyChanged,所以我需要一个公共事件来正确实现它。这有什么不好吗?
    【解决方案3】:
    1. 我会从对象中删除事件。这样会干净一些。

    2. 任何东西都可以逆向工程。只需在保存文件时对其进行加密。这很容易做到。当然,加密密钥必须存储在应用程序的某个地方,因此除非您混淆您的代码,否则有决心的黑客将能够得到它。

    【讨论】:

    • 即使应用程序被混淆,一个坚定的黑客也会找到加密密钥。这要困难得多,但仍然可行。这适用于任何语言和框架,而不仅仅是 .NET。
    • @Paul Sasik:你完全正确。归根结底,只要密钥在客户端,就没有什么可以完全阻止对它的访问。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-27
    • 2022-01-03
    • 2014-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-23
    相关资源
    最近更新 更多