Data Masking: Proper way to update masked data?

234 views Asked by At

There are some data need be mask first before transfer to frontend for display, for example phone number 14012345678 will displayed as 1401*****78 on the page. The point here is never transfer original sensitive data to frontend. But how can I update the masked phone number if the operator wants to modify it?

UPDATES:

Let's say we have a system maintain customer information. For data safety, the system operator can only view the masked phone numbers of customer, but the system allow the operator to modify it with a real one. Then on the operator's perspective, The data is shown as following in a modification dialog:

+----------------------------------------------------------+
|Modify Customer                                           |
|                                                          |
|             +---------------------------------+          |
|        Name | David                           | Required |
|             +---------------------------------+          |
|                                                          |
|             +---------------------------------+          |
|Primary phone| 1401*****78                     | Required |
|             +---------------------------------+          |
|                                                          |
|             +---------------------------------+          |
| Backup phone| 9173*****66                     |          |
|             +---------------------------------+          |
|                                                          |
|                                                          |
|                           +----------+  +----------+     |
|                           |  Cancel  |  |   Save   |     |
|                           +----------+  +----------+     |
|                                                          |
+----------------------------------------------------------+

Assume the operator change the Backup phone to real one and click Save, then how the backend deal with the Primary phone? Posting it with empty value is obviously inappropriate since it is required and the backend will check it(plus the phone formatting check).

2

There are 2 answers

2
Stephen C On BEST ANSWER

I'm not seeing what your actual problem here is, but the solution to the problem that you stated is to simply provide a text input field in the web page for the user to enter the new value. You can use <input type="password"> if the information is so sensitive that the user may be concerned about (say) someone reading the new value over their shoulder as they type it.

The point of the masking is to avoid revealing information to someone who is not the real user. But if they are providing the information themselves, masking is not necessary. They already know the information.


Concerning the update, it seems that you want to allow the operator to be able to submit the form with (say) 1401*****78 in the primary field form. Well you can do that! On the server side you would need check what has been sent as primary_phone, to see if it contains masking characters. If so, you would (presumably) treat that as meaning "don't change that number"). Likewise for the backup_phone field.

But this strikes me as a bizarre (i.e. not properly thought out) requirement. It doesn't make sense to me for the operator to be able to change the phone numbers without knowing what they were before. I would check with the customer to make sure that 1) you really understand what these requirements are saying, and that 2) it is what the customer actually needs.


The other things to note are:

  • you should not be asking the user to provide information about themselves unless and until they have authenticated1, and
  • you should be using a secure session for this; i.e. HTTPS with a proper server-side SSL cert and up-to-date server-side TLS version.

Sending sensitive information over an insecure HTTP session is potentially worse than not masking the phone number.


1 - ... unless you are asking for the information in order to authenticate them. But authenticating someone by asking them to enter a phone number is terribly weak. Knowledge of a phone number doesn't prove it is yours!

1
fr4n03 On

Are you using Spring boot & Spring Security? I'm thinking that if you're using these tools, you can create a custom annotation to mask the data. And depending on the role of the logged user, you can show the data unmasked allowing modifications. For example, if you creates a @MaskData annotation, such as:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@JacksonAnnotationsInside
@JsonSerialize(using = ProtectDataSerializer.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MaskData {
}

I will try to explain what happens with this custom annotation:

  1. @JacksonAnnotationsInside: Indicates that MaskData will be part of Jackson annotations, that is a popular library to work with JSON in Java.
  2. @JsonSerialize(using = ProtectDataSerializer.class): Indicates that when convert the fields annotated with @MaskData to JSON, we will use a class named ProtectDataSerializer to handle why the field is converted to JSON. That means that we will use an specific implementation to convert the values when serialize them to JSON.
  3. @Target(ElementType.FIELD): Indicates the scope to use the annotation that we create, in this case is for fields.
  4. @Retention(RetentionPolicy.RUNTIME): The annotation @MaskData is used only in runtime execution.

The next step is to create an implementation of ProtectDataSerializer class and also this class will extends JsonSerializer class, from the Jackson library in Java. It's used to specify how an object should be serialized into JSON format with customized logic.

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import java.io.IOException;

public class ProtectDataSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String userRole = authentication.getAuthorities().stream()
                                    .findFirst()
                                    .map(Object::toString)
                                    .orElse(null);
        if (!"OPERATOR_ROLE".equals(userRole)) {
            String pii = value.toString().replaceAll("\\w(?=\\w{4})", "*");
            //phone_number could be 1401234****
            jsonGenerator.writeString(pii);
        } else {
            //phone_number could be 14012345678
            jsonGenerator.writeObject(value);
        }
    }
}

This method, serialize, is the core part responsible for converting an object into its JSON representation. It takes three parameters:

  • value the object to be converted into JSON.
  • jsonGenerator a tool provided by Jackson to generate JSON output.
  • serializerProvider offers additional information and methods for the serialization.

Finally, the modified string pii is written to the JSON output using the jsonGenerator. This string contains certain masked data, and this transformed version is what gets included in the final JSON representation of the object, only if the user role is different than OPERATOR.

You only need to test it. For example in a User class:

public class User {
    // other fields...

    @MaskData
    private String phoneNumber;
}

You can see:

I hope it will be useful for you!