Рубрики
Без рубрики

В поисках JS Data Masker. Часть 1: Проблемы

Проблема чувствительной маскировки данных решается различными способами. Поэтому интересно не так … Помечено JavaScript, SensitiveviveData, Make.

Проблема чувствительной маскировки данных решается различными способами. Поэтому интересно не столько, чтобы сделать сравнение этих решений, но думать о том, какие аспекты актуальны сегодня. Критерии, соображения, ограничения и так далее.

Преодоленность

Большинство маскаров используют анализаторы для отдельных объектов, которые должны быть скрыты. Они изучают входные имена (например, «Пароль» , «Токен» , «секретные» ») или форматы данных (например, кастрюли для карт). Но этот эвристический является неоднозначным и очень хрупким. Невозможно полностью полностью покрывать все случаи. Иногда правило маскировки может быть определено только в контексте бизнес-логики.

class UserProfileDto {
  personalData: {}  // sensitive data
  personalSettings: {} // not sensitive data
}

Иногда, Этап, в которой мы определяем потребность в маскировке данных, а этап вывода данных являются, расположенные непосредственно не связанными слоями.

Уязвимость

Можно ли выводить конфиденциальные данные к консоли? Определенно да. Мы используем тонны рамки, библиотеки утилит, и мы не можем полностью контролировать их.

class CredentialsDto {
  constructor(username: string, password: string) {
    this.username = username
    this.password = password  
  }
}

Например, кредиты идут на Dто DTO передан поставщику запросов (DB, HTTP), затем запрос не удается с неожиданным состоянием и печатает все данные контекста вызова в Console.Error Отказ

Очевидное решение просто определить пользовательские значение и TOSTRING методы. Но немедленно возникают различные побочные эффекты. Например значение Может использоваться для сравнения операций в какой-то UTIL. Более того, console.log () Делает волшебство и игнорируйте эти реализации. Возможно отметьте поле как Неремиемое ? Хорошо, мы обманули по умолчанию Console.logger , но сломал какой-нибудь сериализатор, который итерации с помощью для ... в Отказ Переопределить родной console.log ? Может быть. Но что, если модуль использует песочница внутри и работает с собственным приставка пример? Или хранит консольные методы в закрытии? Короче говоря, любые инъекции влечет за собой технические трудности.

Связь

Должно быть принято, что маскировка и регистрация (любой выход) являются различными областями ответственности. Маска, может быть частью лесозаготовительного трубопровода, но она не требуется. Мы не смогли попытаться изменить цель вблизи выходной точки, но создайте компаньон в маске в бизнес-слое и просто связывают их через некоторые общие Слабый пометки Отказ

// Logger util layer
const maskedStore = new WeakMap()
const logger = (...args) =>
  console.log(...args.map(value => 
    maskedStore.has(value)
      ? maskedStore(value)
      : value
))

// Business logic
const a = {smthToHide: 'sensitive data', foo: 'bar'}
maskedStore.set(a, {...a, smthToHide: '***'})

Отражать. Метаданные также можно использовать для той же цели. Или даже CLS-контекст Отказ

Перехват

Размышляя о том, что делает MASKER, очевидно, что все приходит до двух фундаментальных вещей: поиск и замена данных. Подход, основанный на схеме, применимый, если мы знаем сущность данных в маске, если мы контролируем точку, где его создано. На практике мы используем каркасы, которые управляют внутренними слоями данных независимо и неконтролируемыми снаружи. На очень повезло, есть способ ввести свой пользовательский Маскирующий регистратор Отказ Часто для большей надежности мы должны повесить крючок на Stdout/Stderr или переопределить родной Консоль Отказ

Представление

Различные чехлы для маскировки требуют различных подходов обнаружения: регулярные ссылки, функции, двоичные операции (контрольные суммы). Принимая масштаб этих операций, маскировка может серьезно повлиять на производительность. И эти особенности должны быть исследованы ориентирами.

Искажение

Маскировка не всегда означает полную замену для содержания. Важно поддерживать баланс между безопасностью и восприятием. Для ясности представьте себе историю платежей пользователей:

Recipient: *** (personal data)
Sum: $25.00
Paymethod: credit card *** (sensitive data)

С сопоставимым уровнем безопасности это может быть более читаемой формой.

Recipient: J.S***d
Sum: $25.00
Paymethod: credit card 4256 **** **** 3770

Таким образом, модификаторы должны предоставлять минимум необходимый, но не максимально возможный уровень искажений данных, необходимых для определенного контекста.

Цепочка ответственности

Скореемое рассуждение предлагает следующее Имаскер договор.

interface IMasker {
  detect: (target: any) => any,
  modify: (target: any, detected: any[]) => any
}

Простое, четкое и легко сочинить, но также включает некоторые ограничения. Вот случай:

{
  token: {
    type: 'bearer',
    value: 'some string'    
  }
}

Какой должен быть последний результат? 1) Токен: '***' 2) Токен: '*** (объект)' 3) Токен: {Тип: '***', Значение: '***'}} 4) Токен: {Тип: «Международный носитель», значение: '***'}}

Если мы стремимся к варианту 4, нам нужно где-то разместить дополнительную логику, что выходит за пределы ответственности за обнаруживать и модифицировать Отказ Пусть это будет в контроллер Отказ

interface IMasker {
  (target: any, next: IMasker): any
}

Стратегии

Важно ясно выполнять маскировку. Основная причина в том, что маскировка может быть предметом аудита. Например, если вы просто замените Pan С случайными числами он все еще будет повышать вопросы из PSI DSS Отказ Символ канонического маскирования – * (звездочка), менее часто применяется – х чар, даже реже – • (пуля для интерактивных элементов, таких как поля ввода). Последовательность трех символов или более указывает на маскирующие.

Самый простой способ спрятаться – заменить контент. foobar становится ** * , какая-то длинная струна , справа, равен ** * после маскировки. Это равнина маскировка.

Если есть необходимость сохранить длину текста происхождения, мы могли бы заменить каждый символ, как если бы вычеркнул. Когда Еще одна строка превращается в ******* ***** * Это означает ударить Маскировка была применена. Обычно пробелы не замаскируются. Примечание Этот тип сопоставления символов не должен быть применен к паролям. **** Похоже, приглашение для грубой силы.

Для некоторых типов данных важно сохранить специфичность формата. В этом случае частичный Замена будет влиять только на определенный фрагмент. Примеры: номер телефона +7 *** *** 23 50 , Pan 5310 **** **** 9668 Отказ

Разборка

Маскировка необходима для различных типов ввода. В зависимости от структуры они представляют простую или сложную задачу.

  • JSON довольно легко повторять Рекурсивная карта / Deepmap Отказ
  • XML требует ресурсоемкого анализа. Потенциально содержит конфиденциальные данные в текстовых узлах или атрибутах.
  • URL Может содержать учетные данные в частях пути или запроса. Токен доступа легко спутать с идентификатором, потому что оба могут быть UUIDs Отказ
  • Пользовательский Комиссия Модели прикрепляются Шанциальные флаги данных Отказ
  • Pan Требуется проверка контрольной суммы.

Список продолжается. Эти особенности должны быть реализованы таким образом, чтобы Маска не стала парсером. Они связаны, но не идентичны.

Директивы

Следующим этапом абстракции является переход от создания направления прямого маскированного объекта и связывания с делегацией этой функции к отдельной подсистеме. Эта функция требует декларативных инструкций по контракту или директивы маскировки, которые могут быть интерпретированы. По аналогии с тем, как JSON-SCHEMA Мы сможем использовать различные реализации в будущем. Зависит от абстракций, а не конкреций. Желательно унаследовать известного контракта в качестве основы.

interface IMaskerDirective {
  type: string    // masking type
  value?: any     // replacement entity reference
  options?: any   // options for current `type` of masker
  description?: string // optional comment 
  properties?: Record // Directives for nested props
  definitions?: Record,
  $ref?: string
}

Асинхроничность

Там несколько двигателей JS, которые поддерживают синхронный (носорог, нашорн) и асинхронный (V8, чакра) поток. Если честно, сегодня V8 полностью доминирует среди них. Поэтому целесообразно следить за асинхронной парадигмой из коробки, особенно если маскировка является ресурсом интенсивным.

Обычно синхронизируемые/асинхронные версии API представлены различными функциями: Fs.readfile и Fs.readfileync. , ведение / execa.sync , так далее.

interface IMasker {
  (target: any, next: IMasker): Promise
  sync?: (target: any, next: IMasker) => any
}
export {
   masker,
   maskerSync
}

Расширяемость

Долгосрочное решение должно постоянно адаптироваться к новым требованиям. Если концепция непрерывной модификации указывается в оригинальном дизайне, процесс улучшения будет более эффективным. Как это сделать просто? Плагины.

Композитивность

Хотя маскировщики высокого уровня повторно используют часть функциональности основных маскаров, лучше избегать прямых зависимостей. Раствор может основываться на системе DI/IOC-Container/некоторые общий реестр. Каждый пользовательский маскер должен быть объявлен в качестве провайдера и быть доступен с псевдонимами (интерфейс/имя). В современных JS поставщики контекста становятся популярными ( isferfify , Усилий , stestjs di ), но еще не достаточно широко распространено. Пусть там будет реестр плагинов по меньшей мере.

interface MaskerRegistry {
  add(type: string, masker: IMasker): void
  remove(type: string, masker: IMasker): boolean
}

Готовые решения

Я не смею говорить, что нет библиотеки, подходящей для предприятия. К сожалению, я не мог найти что-то зрелое, что можно взять в качестве основы для уточнения.

Известные проекты реализуют свои собственные маски, где это необходимо. Например, семантический релиз/lib/hide-sensive.js

module.exports = (env) => {
  const toReplace = Object.keys(env).filter((envVar) => {
    return /token|password|credential|secret|private/i.test(envVar) && size(env[envVar].trim()) >= SECRET_MIN_SIZE;
  });

  const regexp = new RegExp(toReplace.map((envVar) => escapeRegExp(env[envVar])).join('|'), 'g');
  return (output) =>
    output && isString(output) && toReplace.length > 0 ? output.toString().replace(regexp, SECRET_REPLACEMENT) : output;
};

Оригинал: “https://dev.to/antongolub/in-search-of-js-data-masker-part-1-issues-1j10”