Table of Contents

Property masking

Masking hides a sensitive String value, such as a bank account, a card number or a social security number, in what the application sends to its screens. The server masks the value when it writes the response: the screens, the API clients, the exports of displayed data, the MCP clients and the AI models only receive the masked value, while the business code keeps working on the real value.

Masking is declared on the entity property, in Neos Studio, and applies to every entity view that exposes the property. The value is masked for every user.

Masking types

Type What stays visible Example value Masked value
None The whole value: the property is not masked
Full Nothing, the length is kept Allergic to penicillin **********************
Password Nothing, the length is hidden behind a fixed-size mask W1nch-Group ********
Partial A prefix and a suffix of the chosen lengths 18AV12345 (prefix 2, suffix 2) 18*****45
Regular expression The parts designated by a regular expression 1 85 05 78 006 084 36 1 85 ** ** *** *** **
Credit card The last 4 digits 4111 1111 1111 1111 **** **** **** 1111
IBAN The country code and the last 4 characters FR76 3000 6000 0112 3456 7890 189 FR** **** **** **** **** ***0 189
E-mail The first character and the domain [email protected] l**********@example.com
Phone The last 2 digits +33 6 12 34 56 78 +** * ** ** ** 78

Except for Full and Password, the separators (spaces, dashes, dots...) are kept and only letters and digits are replaced. The Email type masks the separators of the part before the @ too: everything after its first character is masked. A value that does not have the expected format, or that is too short to leave a part visible, is fully masked. An empty value stays empty.

Masking options

Option Used by Description
Visible prefix length Partial Number of characters left visible at the start of the value. Defaults to 0.
Visible suffix length Partial Number of characters left visible at the end of the value. Defaults to 4.
Visible parts (regular expression) Regular expression .NET regular expression that designates the parts of the value that stay visible.
Masking character Every type except None Character that replaces each masked character. Defaults to *. A punctuation mark or a symbol only.

An option that the masking type does not use cannot be set.

Regular expression

The expression designates the visible parts of the value, everything else is masked:

  • With capture groups, only the captured characters stay visible. Otherwise, the whole match stays visible.
  • Every match is taken into account. A value without any match is fully masked.
  • The expression runs with the non-backtracking engine of .NET: back-references, lookarounds and atomic groups are not supported. A repeated capture group only keeps its last iteration: write ^([A-Z]{2}), not ^([A-Z]){2}.

Expressions for common formats:

Value Expression Example value Masked value
French social security number (sex and year) ^([12]\s?[0-9]{2}) 1 85 05 78 006 084 36 1 85 ** ** *** *** **
SIRET number (SIREN part) ^([0-9]{3}\s?[0-9]{3}\s?[0-9]{3}) 732 829 320 00074 732 829 320 *****
French postal code (department) ^([0-9]{2}) 75008 75***

Configure masking in Neos Studio

  1. Open the entity property, which must be a String property that is not a key.
  2. In the Masking group, choose the masking type, then its options.
  3. Type a value in Sample value: Masked value shows it as the server will send it. A message warns when the sample value is fully masked although the type is meant to leave a part visible.

An entity view property inherits the masking options of its entity property. Each option can be overridden on the entity view property, which shows the inherited values and their origin like its other inherited options. When the masking type is overridden, only the inherited options that the new type uses still apply: an entity view can replace a Partial mask with Full, or remove the mask with None. An entity view property that is not bound to an entity property declares its own options.

An entity view property whose expression only reads a member of the entity, such as e.Customer.Iban, also inherits the mask of the entity property it reads. Neos Studio does not show this inherited mask: its Masking group shows the options declared on the entity view property only.

A UI view property has the same options. They are checked by the generation but do not change the value the screen shows: the screen displays the value as masked by the server, and a UI view never lifts that mask.

What the server masks

The server masks the values in the responses of the application's API:

  • The properties of the entity views, including those selected with $select or expanded with $expand.
  • The labels of lookups that display a masked property.

It masks them the same way in what the MCP tools and the AI skills return: the entity views they read, create or update, and the entity rows that an AI skill queries.

A cluster that reads the entity views of another cluster calls its API, so it receives the masked values too: its business code only sees the mask, never the real value. Sending this mask back in an update keeps the stored value (see Saving masked values).

The server refuses with an error a $apply request that groups, aggregates or computes a masked property, from the API or from an AI skill: such a result would come out without the property it was read from, so it could not be masked. Masking alone does not prevent filtering on a masked property, which the server does on the real value.

An encrypted property, masked or not, can be neither filtered, nor sorted, nor searched: the database only holds its encrypted value (see the SQL limits in Data encryption). Turn off Filterable on its entity view property, as the example below does for its eight encrypted properties.

Masked values on the screens

The loaded values are already masked, so the screens never work on the real value:

  • Sorting, filtering and quick search on a masked property are done on the server, even when the data is loaded on the client. When the property is also encrypted, none of them is available. A masked property cannot be used for grouping, and its summaries are left out.
  • In a datagrid, a masked column is read-only.
  • In a form, a masked property is edited in a field that replaces the whole value: the first key typed erases the masked value and starts a new one. The cancel button of the field, or the Escape key, brings back the masked value. The value is masked again once saved.

Only a form-field or a textbox without type can edit a masked property. Binding a masked property to another editor (a textbox of type mask or password, a code-editor, a rich-text-editor or an expression-editor) is a generation error, since these editors could save part of the masked value.

Saving masked values

The screens send a masked value only when the user entered it again. On the server, a received value equal to the mask of the stored value keeps the stored value, so that an API client sending back what it read does not overwrite the real value. A value that has the form of a mask is refused with an error: it holds the masking character, and masking it again leaves it unchanged, or masks it fully because the format does not accept it. Such a value is most likely an older mask, or a mask edited in its visible parts. A real value that holds the masking character, such as jean*[email protected] for an Email mask, is saved. With the Full type, only a value made of the masking character alone is refused, and with the Password type, only the fixed-length mask. With the Regex type, every value that holds the masking character is refused: masked again, an older mask can match another part of the expression, so it cannot be told apart from a real value.

Copying an item empties its masked values, unless they were entered again: the copy does not carry a real value the user never saw.

What masking does not cover

Important

Masking is a display discretion, not a protection of the stored data.

  • The value is stored in clear unless the property is encrypted (IsEncrypted). neos generate warns about a masked property stored in a database without encryption (M0102).
  • The business code and the event and validation rules work on the real value of the cluster's own data.
  • A key property, or a property of an alternate key, cannot be masked: keys appear in addresses, references and error messages.
  • An expression that computes a new value from a masked value, or a getter that copies it, exposes it in that property. Only an expression that reads the member as is, such as e.Customer.Iban or e.Customer?.Iban, keeps its mask.

neos generate also reports, as an information message (M0101), every String property marked as personal data or encrypted that has no masking type. See Message levels to raise or lower these messages, and the new checks of the version for the masking options checked by the generation.

Example

The People module of the Technical Demos cluster shows one example of each masking type on the person screen (PersonUI and PersonDetailUI).

See also