Skip to content

You are viewing documentation for Immuta version 2022.5.

For the latest version, view our documentation for Immuta SaaS or the latest self-hosted version.

External Masking Interface

Audience: Integration Developers

Content Summary: Building a REST Masking and Unmasking service to integrate with Immuta requires implementing a simple REST interface. This page documents this interface and provides example requests and responses.

Use Deterministic IVs/Salt

Use deterministic IVs/salt to ensure the same value is masked consistently throughout the data, as Immuta always pushes down the masked version of the literal when the querying user is exempt from the policy.

Authentication

Immuta can make requests to your External Masking service with one of two authentication methods:

  1. Username and password authentication: Immuta can send requests with a username and a password in the Authorization HTTP header. In this case, your service will need to be able to parse a Basic Authorization Header and validate the credentials sent with it.
  2. PKI Certificate: Immuta can send requests using a CA certificate, a certificate, and a key.

Alternatively, Immuta can make unauthenticated requests to your REST masking service. This is recommended only if you have other security measures in place (e.g., if the service is in an isolated network that's reachable only by your Immuta environment.)

Endpoints

POST /

Description

Warning

The unmask action allows Immuta to build predicates that can be used to query data that is consistently masked at rest in the remote database; it does not dynamically mask data at query time.

To dynamically mask data, use Immuta’s standard masking policies.

This endpoint accepts a set of values and a directive to either mask or unmask them.

Request Body

Your service will need to parse and process the following body parameters:

Parameter Type Description
action Enum(MASK, UNMASK) Either MASK or UNMASK the values
values Map(string, String[]) A map of columns containing an array of masked/unmasked values

Below is an example request payload to mask values in the ssn and ccn columns:

{
  "action": "MASK",
  "values": {
    "ssn": ["123-4567-890", "098-4567-321"],
    "ccn": ["0000-1111-2222-3333", "5555-6666-7777-8888", "9999-0000-1111-2222"]
  }
}

Below is an example request payload to unmask values in the ssn and ccn columns:

{
  "action": "UNMASK",
  "values": {
    "ssn": ["AX6YYTURNHtyDkjm", "ue6AUyQYgWiTLAUT"],
    "ccn": ["oDpEKiFutdWG46A2", "Tdz3BEVjjfVLrW6x", "nVANLwkgoLUAe4NQ"]
  }
}

Response Body

Your service will need to return a map of values that corresponds to the columns and values that were specified in the request. It is important that your service returns the same column keys and that the position of each masked/unmasked value in your response corresponds to the masked/unmasked value from the request.

For example, the following request

curl --location --request POST 'https://your.external.mask.service/' \
--data-raw '{
  "action": "MASK",
  "values": {
    "ssn": ["123-4567-890", "098-4567-321"],
    "ccn": ["0000-1111-2222-3333", "5555-6666-7777-8888", "9999-0000-1111-2222"]
  }
}'

could return the following body:

{
  "ssn": ["AX6YYTURNHtyDkjm", "ue6AUyQYgWiTLAUT"],
  "ccn": ["oDpEKiFutdWG46A2", "Tdz3BEVjjfVLrW6x", "nVANLwkgoLUAe4NQ"]
}

Notice that both ssn and ccn columns are present and that each of them contains the exact number of values specified in the request. Immuta will fail to validate responses to its request under the following circumstances:

  1. The response contains column keys that were not present in the request.
  2. The response is missing column keys that were present in the request.
  3. The response doesn't contain the exact number of values for each of the corresponding column keys in the request.

Examples

Below are some very simplistic implementation examples of a service with mask() and unmask() functions:

const mask = (value) => {
  // TODO: implement your custom masking logic and return a masked value
};
const unmask = (value) => {
  // TODO: implement your custom unmasking logic and return an unmasked value
};
// Use as a POST handler:
const externalMaskingHandler = async (request, response) => {
  const { action, values } = request.payload;
  const result = {};
  Object.keys(values).forEach(column => {
      result[column] = values[column].map(action === 'MASK' ? mask : unmask);
  });
  return result;
};
from flask import Flask, request

app = Flask()

def mask(value):
    # TODO: implement your custom masking logic and return a masked value
    pass

def unmask(value):
    # TODO: implement your custom unmasking logic and return an unmasked value
    pass

@app.route("/path/to/my/services", methods=["POST"])
def external_masking_handler():
    payload = request.get_json()
    action = payload.get("action")
    values = payload.get("values")
    result = {}
    for k, v in values:
        result[k] = [mask(value) if action == "MASK" else unmask(value) for value in v]
    return result