针对PCI合规性的敏感数据的自定义JSON序列化

时间:2018-01-26 16:54:27

标签: c# json serialization pci-compliance

我们必须记录我们的Web服务的传入请求和传出响应。这包括每个对象的JSON序列化,以便它们可以存储在数据库中。

某些信息被视为敏感信息(例如社会保险号,信用卡号等),我们无法将这些信息包含在符合PCI合规性的日志中。现在我们手动用占位符值替换值(例如" [PRIVATE]")但这仅适用于字符串属性。某些数据(如出生日期)不会存储为字符串,因此在序列化之前更改属性值时,这不起作用。最大的问题是,在记录之前忘记删除敏感数据太容易了,这是非常不受欢迎的。

为了解决这个问题,我考虑创建一个自定义属性并将其放在属性上,然后让JSON序列化例程在每个属性上查找此属性,如果存在,则将该序列化值替换为占位符,例如& #34; [PRIVATE]"

目前我们正在使用System.Web.Script.Serialization.JavaScriptSerializer进行序列化。显然它对我的自定义属性一无所知。我将如何更改序列化过程,以便使用我的自定义" SensitiveData"属性是否替换为占位符值?我不反对使用不同的序列化程序,但希望我可以利用现有的功能而不是编写自己的功能。

2 个答案:

答案 0 :(得分:2)

这是我的解决方案,虽然可能需要稍微调整一下:

我的自定义JsonConverter:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Reflection;

public class SensitiveDataJsonConverter : JsonConverter
{
    private readonly Type[] _types;

    public SensitiveDataJsonConverter(params Type[] types)
    {
      _types = types;
    }

    public override bool CanConvert(Type objectType)
    {
        return _types.Any(e => e == objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var jObject = new JObject();
        var type = value.GetType();

        foreach (var propertyInfo in type.GetProperties())
        {
            // We can only serialize properties with getters
            if (propertyInfo.CanRead)
            {
                var sensitiveDataAttribute = propertyInfo.GetCustomAttribute<SensitiveDataAttribute>();
                object propertyValue;

                if (sensitiveDataAttribute != null)
                    propertyValue = "[REDACTED]";
                else
                    propertyValue = propertyInfo.GetValue(value);

                if (propertyValue == null)
                  propertyValue = string.Empty;

                var jToken = JToken.FromObject(propertyValue, serializer);

                jObject.Add(propertyInfo.Name, jToken);
            }
        }

        jObject.WriteTo(writer);
    }

这是我的自定义属性:

using System;

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
public class SensitiveDataAttribute : Attribute
{

}

我们这样使用它:

string serialized;

try
{
    serialized = JsonConvert.SerializeObject(value, new SensitiveDataJsonConverter(value.GetType()));
}
catch // Some objects cannot be serialized
{
    serialized = $"[Unable to serialize object '{key}']";
}

这是我尝试序列化的测试类:

class Person
{
    public Person()
    {
        Children = new List<Person>();
    }

    public List<Person> Children { get; set; }

    [SensitiveData]
    public DateTime DateOfBirth { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    [SensitiveData]
    public string SocialSecurityNumber { get; set; }

    public Person Spouse { get; set; }
}

在添加Spouse和Children属性之前,这似乎很有用,但我得到了NullReferenceException。我将此添加到WriteJson方法中,该方法纠正了问题:

if (propertyValue == null)
    propertyValue = string.Empty;

答案 1 :(得分:0)

@DesertFoxAZ在这里有一个很棒的答案。为了扩大答案,我将添加以下代码。没有此代码,PII编辑将仅适用于传递给构造函数的顶级类。如果您的对象属性也应删除一些数据,则还需要以下代码。

下面的代码是根据this SO post.

中的答案进行修改的
        private readonly List<Type> _alreadyVisitedTypes = new List<Type>(); //avoid infinite recursion

        private void RecursivelySetTypesToOperateOn(Type currentType)
        {
            if (_alreadyVisitedTypes.Contains(currentType))
            {
                return;
            }
            _alreadyVisitedTypes.Add(currentType);

            if (currentType.IsClass && currentType.Namespace != "System") //custom defined classes only
            {
                _types.Add(currentType);
            }
            foreach (PropertyInfo pi in currentType.GetProperties())
            {
                if (pi.PropertyType.IsClass)
                {
                    RecursivelySetTypesToOperateOn(pi.PropertyType);
                }
            }
        }