9

I am interested in using protobuf-net to serialize/deserialize data sent across my TCP applications. I learnt of protobuf-net a few weeks ago and downloaded it from here and plan to use it in my network applications. Although I've used the XML serializer before, I've never considered the issue of security in serialization as the programs I was creating were private and didn't send data across a network.

After some reading on Security Considerations for Data, I understand that careless implementation of serialization could result in a custom client sending malicious data to the server which is a major security flaw.

However, as I don't fully understand the process of serializing/deserializing an object and am not completely aware of the capabilities of .net, I don't know what precautions I should take to ensure that an attacker won't find any way to make the server act in an unintended way. I don't want to implement serialization until I'm sure that all of the necessary precautions have been taken in my code.

I understand that the possible threats an attacker could pose are:

  1. Unintentional information disclosure. I'm not sure on this, but if I'm not mistaken, this is only a security concern for the sender which is not a problem for the receiving end. As long as sent confidential data is encrypted, this shouldn't be a concern?

  2. Denial of service. This can be stopped by putting a limit on the size of the object being deserialized.

  3. Malicious code execution. This is the possibility I'm most worried about especially due to my lack of complete knowledge on serialization and .net.

    Are the only things which are serialized variables (both private and public) of the object? So events and functions cannot be transferred across in any way? Since functions cannot be set as variables in VB (unlike other languages such as Swift which has closures), this shouldn't be a concern, but I'm not entirely sure if there is another way to circumvent this which is why I ask.

    If the above is the case, then can I ensure that no malicious code is run if I only deserialize the data into an Object or a class which has no 'potentially dangerous' functions?

When an object is deserialized, is New called? If so, then I would have to make sure New is 'safe' to call?

In addition to the above, are there any other ways in which an attacker could destabilize my server (eg. taking advantage of inheritance or events/handlers)?

Are the answers to the above questions the same for serializers other than protobuf-net?


Moved from SO as it was deemed to be more suitable here

2
  • It usually depends on implementation, the serialized data itself isn't necessarily a threat, but what you do with it may be
    – wireghoul
    Sep 11, 2016 at 10:50
  • @wireghoul So if I am aware throughout my code that the data received could be malicious and take the necessary precautions then I should be fine?
    – Shuri2060
    Sep 11, 2016 at 10:52

3 Answers 3

7
+50

I cannot answer questions #1 or #2, but I can speak a bit to question #3. First, a quick overview of protobuf-net.

On the wire, Google's protocol buffers are a messaging format not a serialization format. Messages consist of sequences of key/value pairs, where values can be primitive data types or nested sequences. It's somewhat similar to JSON, except that the keys are integers rather than name strings. The format is language-independent (and thus contains no language-specific type information) and sufficiently self-documenting that values of unexpected properties can be skipped and data types for primitive values can be inferred.

protobuf-net is a contract based serializer for .NET types that serializes from and to the protocol buffer format. "Contract based" means that the serializer builds a map (a.k.a. "contract") between message properties and .Net properties, then uses that contract to construct and deserialize .Net objects. The "root" object to deserialize is specified by the call to deserialize, e.g.

var rootObject = ProtoBuf.Serializer.Deserialize<RootObjectType>(stream)

Basic instructions for its use can be found here and here.

protubuf-net makes only limited use of unsafe code - currently only for efficient deserialization of floating point values I believe. This can be disabled by building with FEAT_SAFE defined. Thus the possibility of a malformed message overwriting the heap or stack is minimized. Protobuf-net does not share a code base with the Google implementation of protocol buffers.

So, how does protobuf-net construct its contract (the RuntimeTypeModel) for mapping .Net types to messages? Since message keys are integers rather than strings, the canonical choice of using the .Net public property or field name does not work. Instead, there are the following options:

  1. The type can be annotated with with [ProtoContract] and [ProtoMember()] attributes, like this example from the docs:

    [ProtoContract]
    class Person {
        [ProtoMember(1)]
        public string Name {get;set:}
        [ProtoMember(2)]
        public Address Address {get;set;}
    }
    [ProtoContract]
    class Address {
        [ProtoMember(1)]
        public string Line1 {get;set;}
        [ProtoMember(2)]
        public string Line2 {get;set;}
    } 
    

    The properties and fields marked with [ProtoMember] will be discovered using reflection and serialized. Others will not.

  2. The contract can be constructed programmatically, as shown here.

  3. protobuf-net contract generation is opt-in by default. I.e., only the specified properties and fields are serialized. However, this can be overridden by setting ProtoContractAttribute.ImplicitFields to one of the following values:

    public enum ImplicitFields
    {
        /// <summary>
        /// No members are serialized implicitly; all members require a suitable
        /// attribute such as [ProtoMember]. This is the recmomended mode for
        /// most scenarios.
        /// </summary>
        None = 0,
        /// <summary>
        /// Public properties and fields are eligible for implicit serialization;
        /// this treats the public API as a contract. Ordering beings from ImplicitFirstTag.
        /// </summary>
        AllPublic= 1,
        /// <summary>
        /// Public and non-public fields are eligible for implicit serialization;
        /// this acts as a state/implementation serializer. Ordering beings from ImplicitFirstTag.
        /// </summary>
        AllFields = 2
    }
    
  4. protobuf-net can also use data contract serializer and XmlSerializer attributes for automatic contract creation. This can be disabled via the property ProtoContractAttribute.UseProtoMembersOnly.

  5. While protobuf-net was originally designed as a code-first serializer (you define your types, then define how to serialize them) it does have support for c# code generation from message prototypes. See here for details.

Thus, as you can see, the possibility for unwanted types and code to be injected during serialization is limited by design since the contract, rather than the message, controls the types of constructed objects. This can be further limited via the following recommendations:

  1. Do not use [ProtoMember(DynamicType = true)].

    protobuf-net implements an extension to the standard allowing the .Net type to be deserialized to be specified in the message itself - breaking the relative safety of of the out-of-the-box standard. This is disabled by default but can be enabled by setting DynamicType = true. Do not use this functionality, as this is the most obvious way for an unexpected object to be injected during deserialization.

    If you are concerned that some other developer on the team might use this functionality (as it does make serializing polymorphic types extremely easy), you can set an event on TypeModel.DynamicTypeFormatting that throws an exception whenever the serializer tryies to resolve a dynamic type, or a dynamic type name, e.g.:

        ProtoBuf.Meta.TypeFormatEventHandler handler = (o, e) => { throw new NotSupportedException("Dynamic typing is not allowed."); };
        ProtoBuf.Meta.RuntimeTypeModel.Default.DynamicTypeFormatting += handler;
    

    Be sure to set the event on the typemodel you are actually using, as is pointed out here.

  2. Be careful with inheritance. protobuf-net does support inheritance. Take care not to allow unwanted derived types to creep into the contract. E.g., given the following:

    [ProtoContract]
    [ProtoInclude(7, typeof(ValidatedSqlQuery))]
    [ProtoInclude(8, typeof(DebugSqlCommand))]
    public abstract class SqlCommand
    {
    }
    
    /// <summary>
    /// Represents a fully validated, parameterized SQL query
    /// </summary>
    [ProtoContract]
    public class ValidatedSqlQuery
    {
    }
    
    /// <summary>
    /// For testing purposes, execute any command against the database.
    /// </summary>
    [ProtoContract]
    class DebugSqlCommand
    {
    }
    

    It's possible for a DebugSqlCommand to get constructed in a List<SqlCommand> via an appropriate message, possibly causing damage down the line.

  3. Serialize properties, not fields. Do not use ImplicitFields.AllFields. Avoid using ImplicitFields.AllPublic whenever possible.

  4. Validate data in all property setters. You can use serialization-specific setters if you don't want to do the validation on the "normal" setters, e.g.:

    [ProtoContract]
    class Contact {
        public string Email { get; set; }
    
        [ProtoMember(1)]
        string SerializedEmail {  
            get { return Email; }
            set
            {
                // Make sure the email is well-formed, throw an exception if not
                ValidateEmail(value);
                Email = value;
            }
        }
    }
    

    In complex validation scenarios, you can validate multiple related properties in an [OnDeserialized] callback.

  5. Finally, your deserialization code is only as safe as the objects you deserialize! Do not serialize objects containing or using unsafe code. Do not allow debug objects to be serialized.

2
  • 1
    Thank you for your excellent and detailed answer! I'll take my time to understand everything before accepting it. I see that protobuf is probably much safer than other options as long as it is implemented correctly.
    – Shuri2060
    Sep 11, 2016 at 23:41
  • @QuestionAsker - Json.NET is also pretty safe as long as you set TypeNameHandling = TypeNameHandling.None. Any other value for TypeNameHandling has the same dangers as DynamicType = true. Above all, do not use BinaryFormatter.
    – dbc
    Sep 11, 2016 at 23:45
2

It isn't the serialisation that is the issue. It is the deserialisation on the server.

An incautious deserialisation would create a new object in code. An object may contain all sorts of information, some of which may be executable. So a rogue client could serialise and send through information containing program code which the server would then unthinkingly execute. Bad news!

If you are receiving any data from an external client, you must always vet it before doing anything with it. In this case, you need to ensure that either no functions are contained within the object or that any such functions can never run. You also need to ensure that any remaining data is in the form you expect and follows any constraints you expect (e.g. string length, number size, etc). You have to do all of this before you can trust the data and do something with it in your application code.

The use of open standards can also help here. Using JSON for example will provide some level of assurance that the contents cannot be executable. XML would be similar but more wordy. You can use existing tools that will validate the data which saves you the effort and is almost certainly more robust.

Clarification update: What I've said here is more generic an answer than your original question I'm afraid due to my lack of understanding of the protocol buffers format. To answer your last question, yes things may be different for other serialisers, please see my comments as well as this answer.

As requested: links for proto-buffers:

https://github.com/google/protobuf/issues/760

http://blog.codeclimate.com/blog/2014/06/05/choose-protocol-buffers/

The Google search I used: protobuf-net security issues

7
  • So from what you say, functions can be serialized? I'm using protobuf-net
    – Shuri2060
    Sep 11, 2016 at 16:52
  • Apologies, I'm not a .net person and I don't know that tool specifically. Sep 11, 2016 at 17:04
  • Its based on google's protocol buffers for serialization... I don't completely understand it either.
    – Shuri2060
    Sep 11, 2016 at 17:08
  • OK, after some reading. It looks like proto-buffers is already strongly typed and so should require less server-side checks before consumption. However, I did spot on mention of a buffer overflow issue in the serialisation side which would be because (possibly) the serialisation library is C++ or C# based. That adds a small risk on the client-side. Sep 11, 2016 at 21:24
  • Would you mind passing me the link? I haven't found much on this subject.
    – Shuri2060
    Sep 11, 2016 at 21:26
1

1# If the data is properly encrypted then information is not disclosed to a third-party, but of course information will be disclosed to the receiver.

2# Yes, DoS might be an issue if you're talking about huge objects.

3# Generally deserialization does not execute code. Constructors don't run (except default) when deserializing however there are deserialization callbacks which might do stupid things. Please check all the parent classes of your objects (that you're deserializing) whether they do stupid things.

A stupid thing for example would be

public void OnDeserialized(...) { deleteFile(someMember); }

You can't deserialize into an "Object" the way I think you mean. The runtime type won't be object even though the compile-time type is.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .