Создание Endpoint DLP для обнаружения PII на вашем компьютере в режиме реального времени

В этом руководстве мы создадим легкий DLP-сканер конечной точки, который сканирует файлы на вашем устройстве в режиме реального времени на наличие конфиденциальных данных, таких как PII и секреты, с помощью API-интерфейсов Nightfall для предотвращения потери данных.

Защита от потери данных конечных точек (DLP) обнаруживает, классифицирует и защищает конфиденциальные данные, такие как PII, номера кредитных карт и секреты, которые распространяются на конечные устройства, такие как ваш компьютер или машины EC2. Это способ помочь сохранить данные в безопасности, чтобы вы могли обнаруживать и останавливать случаи кражи данных. Наше приложение DLP для конечной точки будет состоять из двух основных служб, которые будут работать локально. Первый сервис будет отслеживать события файловой системы с помощью пакета Watchdog в Python. Когда запускается событие файловой системы, например, когда файл создается или изменяется, служба отправляет файл в Сумерки для сканирования на наличие конфиденциальных данных. Второй сервис — это сервер веб-перехватчиков, который будет получать результаты сканирования от Nightfall, анализировать конфиденциальные данные и записывать их в файл CSV в качестве выходных данных.

Вы познакомитесь со следующими инструментами и платформами:

  • питон
  • Колба
  • Сумерки
  • Нгрок
  • Сторожевая собака

Ключевые идеи

Прежде чем мы приступим к нашей реализации, начните с ознакомления с тем, как работает сканирование файлов с помощью Nightfall, чтобы вы познакомились с процессом, который мы реализуем.

В двух словах, Nightfall выполняет сканирование файлов асинхронно; после того, как вы загрузите файл в Nightfall и запустите сканирование, мы выполним сканирование в фоновом режиме. Когда сканирование завершится, Nightfall предоставит вам результаты, отправив запрос на ваш сервер веб-перехватчиков. Такое асинхронное поведение позволяет Nightfall сканировать файлы разного размера и сложности, не требуя, чтобы вы удерживали открытым длинный синхронный запрос или постоянно запрашивали обновления. Влияние этого шаблона заключается в том, что вам нужна конечная точка веб-перехватчика, которая может получать входящие уведомления от Nightfall после завершения сканирования — это одна из двух служб, которые мы создаем в этом руководстве.

Начиная

Вы можете разветвить образец репозитория и просмотреть полный код здесь или следовать инструкциям ниже. Если вы начинаете с нуля, создайте новый репозиторий GitHub. Это руководство было разработано для Mac и предполагает, что это операционная система конечной точки, которую вы используете, однако это руководство должно работать в операционных системах с небольшими изменениями. Например, вы можете расширить этот учебник, запустив DLP конечной точки на компьютере EC2 для мониторинга ваших производственных систем.

Настройка зависимостей

Во-первых, давайте начнем с установки наших зависимостей. Мы будем использовать Nightfall для классификации данных, веб-фреймворк Flask на Python, сторожевой таймер для мониторинга событий файловой системы и Gunicorn в качестве нашего веб-сервера. Создайте requirements.txt и добавьте в файл следующее:

nightfall 
Flask 
Gunicorn 
watchdog

Затем запустите pip install -r requirements.txt, чтобы выполнить установку.

Настройка обнаружения с помощью Nightfall

Далее нам понадобится наш ключ API Nightfall и секрет подписи веб-хука; первый аутентифицирует нас в Nightfall API, а второй аутентифицирует, что входящие веб-перехватчики исходят от Nightfall. Вы можете получить свой ключ API и секрет подписи веб-хука из Dashboard Nightfall. Завершите сумрачный налет Быстрый старт, чтобы получить более подробное описание. Зарегистрируйтесь для получения бесплатной учетной записи Nightfall, если у вас ее нет.

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

export NIGHTFALL_API_KEY=<your_key_here> 
export NIGHTFALL_SIGNING_SECRET=<your_secret_here>

Мониторинг событий файловой системы

Watchdog — это модуль Python, который отслеживает события файловой системы. Создайте файл с именем scanner.py. Мы начнем с импорта наших зависимостей и настройки базового обработчика событий. Этот обработчик событий реагирует на события изменения файла для путей к файлам, которые соответствуют заданному набору регулярных выражений (регулярных выражений). В этом случае .* указывает, что мы сопоставляем любой путь к файлу — мы настроим это чуть позже. Когда запускается событие файловой системы, мы выводим строку на консоль.

import os
import time
from watchdog.observers import Observer
from watchdog.events import RegexMatchingEventHandler
from nightfall import Confidence, DetectionRule, Detector, RedactionConfig, MaskConfig, Nightfall

class MyHandler(RegexMatchingEventHandler):
    # event handler callback that is called when a file is modified (created or changed)
    def on_modified(self, event):
        print(f'Event type: {event.event_type} | Path: {event.src_path}')

if __name__ == "__main__":
    regexes = [ ".*" ]

    # register event handler to monitor file paths that match our regex
    event_handler = MyHandler(regexes)
    observer = Observer()
    observer.schedule(event_handler,  path='',  recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

Запустите python scanner.py, и вы заметите, что в консоль выводится множество строк. Это все файлы, которые создаются и изменяются на вашем компьютере в режиме реального времени. Вы заметите, что ваша операционная система и запущенные вами приложения постоянно записывают, изменяют и удаляют файлы на диске!

Event type: modified | Path: /Users/myuser/Library/Caches
Event type: modified | Path: /Users/myuser/Library/Caches/com.apple.nsservicescache.plist
Event type: modified | Path: /Users/myuser/Library/Caches
Event type: modified | Path: /Users/myuser/Library/Caches/Google/Chrome/Default/Cache
Event type: modified | Path: /private/tmp
Event type: modified | Path: /Users/myuser/Library/Preferences/ContextStoreAgent.plist
Event type: modified | Path: /private/tmp
Event type: modified | Path: /Users/myuser/Library/Assistant
Event type: modified | Path: /Users/myuser/Library/Assistant/SyncSnapshot.plist
...

Далее мы обновим наш обработчик событий, чтобы вместо простого вывода на консоль мы отправляли файл в Nightfall для сканирования. Мы инициируем запрос на сканирование в Nightfall, указав путь к измененному/созданному файлу, URL-адрес веб-перехватчика, куда должны быть отправлены результаты сканирования, и наше правило обнаружения, которое указывает, какие конфиденциальные данные мы ищем. Если сканирование файла инициировано успешно, мы напечатаем соответствующий идентификатор загрузки, который Nightfall предоставит нам на консоли. Этот идентификатор будет полезен позже при идентификации результатов сканирования.

Вот наш полный scanner.py, поясняемый ниже:

import os
import time
from watchdog.observers import Observer
from watchdog.events import RegexMatchingEventHandler
from nightfall import Confidence, DetectionRule, Detector, RedactionConfig, MaskConfig, Nightfall

class MyHandler(RegexMatchingEventHandler):
    def scan_file(self, filepath):
        nightfall = Nightfall() # reads API key from NIGHTFALL_API_KEY environment variable by default
        webhook_url = f"{os.getenv('NIGHTFALL_SERVER_URL')}/ingest" # webhook server we'll create

        try:
            scan_id, message = nightfall.scan_file(
                filepath, 
                webhook_url=webhook_url,
                # detection rule to detect credit card numbers, SSNs, and API keys
                detection_rules=[ DetectionRule([ 
                    Detector(
                        min_confidence=Confidence.LIKELY,
                        nightfall_detector="CREDIT_CARD_NUMBER",
                        display_name="Credit Card Number"),
                    Detector(
                        min_confidence=Confidence.LIKELY,
                        nightfall_detector="US_SOCIAL_SECURITY_NUMBER",
                        display_name="US Social Security Number"),
                    Detector(
                        min_confidence=Confidence.LIKELY,
                        nightfall_detector="API_KEY",
                        display_name="API Key")
                    ])
                ])
            return scan_id, message
        except Exception as err:
            print(f"Error processing {filepath} | {err}")
            return None, None

    def on_modified(self, event):
        # scan file with Nightfall
        scan_id, message = self.scan_file(event.src_path)
        if scan_id:
            print(f"Scan initiated | Path {event.src_path} | UploadID {scan_id}")
        print(f'Event type: {event.event_type} | Path: {event.src_path}')

if __name__ == "__main__":
    regexes = [ ".*/Downloads/.*", ".*/Desktop/.*", ".*/Documents/.*" ]

    # register event handler to monitor file paths that match our regexes
    event_handler = MyHandler(regexes)
    observer = Observer()
    observer.schedule(event_handler,  path='',  recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

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

В этом примере мы указали встроенное правило обнаружения, которое обнаруживает вероятные номера кредитных карт, номера социального страхования и ключи API. Это правило обнаружения — простая отправная точка, которая лишь поверхностно описывает типы обнаружения, которые вы можете построить с помощью Nightfall. Узнайте больше о построении встроенных правил обнаружения здесь или о том, как их настроить в Dashboard Nightfall.

Также обратите внимание, что мы обновили наше регулярное выражение с .* на набор путей к файлам на компьютерах Mac, которые обычно содержат созданные пользователем файлы — папки «Рабочий стол», «Документы» и «Загрузки»:

regexes = [ ".*/Downloads/.*", ".*/Desktop/.*", ".*/Documents/.*" ]

Вы можете настроить эти регулярные выражения на любые интересующие вас пути к файлам. Другой вариант — написать универсальное регулярное выражение, которое игнорирует/исключает пути к файлам конфигурации и временным файлам:

regexes = [ "(?!/opt/|.*/Library/|.*/private/|/System/|/Applications/|/usr/).*" ]

Настройка нашего сервера Webhook

Далее мы настроим наш сервер веб-перехватчиков Flask, чтобы мы могли получать результаты сканирования файлов из Nightfall. Создайте файл с именем app.py. Мы начнем с импорта наших зависимостей и инициализации клиентов Flask и Nightfall:

import os
from flask import Flask, request, render_template
from nightfall import Confidence, DetectionRule, Detector, RedactionConfig, MaskConfig, Nightfall
from datetime import datetime, timedelta
import urllib.request, urllib.parse, json
import csv

app = Flask(__name__)

nightfall = Nightfall(
	key=os.getenv('NIGHTFALL_API_KEY'),
	signing_secret=os.getenv('NIGHTFALL_SIGNING_SECRET')
)

Затем мы добавим наш первый маршрут, который будет отображать «Hello World», когда клиент переходит к /ping, просто для проверки того, что все работает:

@app.route("/ping")
def ping():
	return "Hello World", 200

Во втором окне командной строки запустите gunicorn app:app в командной строке, чтобы запустить сервер, и перейдите на локальный сервер в веб-браузере. Вы увидите, где находится веб-браузер в журналах Gunicorn, обычно это будет 127.0.0.1:8000 или localhost:8000.

[2021-11-26 14:22:53 -0800] [61196] [INFO] Starting gunicorn 20.1.0
[2021-11-26 14:22:53 -0800] [61196] [INFO] Listening at: http://127.0.0.1:8000 (61196)
[2021-11-26 14:22:53 -0800] [61196] [INFO] Using worker: sync
[2021-11-26 14:22:53 -0800] [61246] [INFO] Booting worker with pid: 61246

Чтобы открыть наш локальный сервер веб-перехватчиков через общедоступный туннель, на который Nightfall может отправлять запросы, мы будем использовать ngrok. Загрузите и установите ngrok через их документацию по быстрому запуску здесь. Мы создадим туннель ngrok следующим образом:

./ngrok http 8000

После выполнения этой команды ngrok создаст туннель в общедоступном Интернете, который перенаправит трафик со своего сайта на ваш локальный компьютер. Скопируйте конечную точку туннеля HTTPS, созданную ngrok: мы можем использовать ее в качестве URL-адреса веб-перехватчика при запуске сканирования файлов.

Account                       Nightfall Example
Version                       2.3.40
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://3ecedafba368.ngrok.io -> http://localhost:8000
Forwarding                    https://3ecedafba368.ngrok.io -> http://localhost:8000

Давайте установим эту конечную точку HTTPS как локальную переменную среды, чтобы мы могли ссылаться на нее позже:

export NIGHTFALL_SERVER_URL=https://3ecedafba368.ngrok.io

Совет. С учетной записью Pro ngrok вы можете создать поддомен, чтобы URL-адрес вашего туннеля был согласованным, а не генерировался случайным образом при каждом запуске туннеля.

Обработка входящего веб-перехватчика

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

Во-первых, что значит иметь выводы? Если в файле есть результаты, это означает, что Nightfall идентифицировал конфиденциальные данные в файле, которые соответствуют настроенным вами правилам обнаружения. Например, если вы приказали Nightfall искать номера кредитных карт, любая подстрока полезной нагрузки запроса, совпадающая с нашим детектором кредитных карт, будет представлять собой конфиденциальные результаты.

Мы разместим наш входящий веб-хук по адресу /ingest методом POST.

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

Мы проверим входящий веб-хук от Nightfall, получим результаты JSON по предоставленной ссылке и запишем результаты в файл CSV. Во-первых, давайте инициализируем наш файл CSV, куда мы будем записывать результаты, и добавим наш метод /ingest POST.

# create CSV where sensitive findings will be written
headers = ["upload_id", "#", "datetime", "before_context", "finding", "after_context", "detector", "confidence", "loc", "detection_rules"]
with open(f"results.csv", 'a') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(headers)

# respond to POST requests at /ingest
# Nightfall will send requests to this webhook endpoint with file scan results
@app.route("/ingest", methods=['POST'])
def ingest():
    data = request.get_json(silent=True)
    # validate webhook URL with challenge response
    challenge = data.get("challenge") 
    if challenge:
        return challenge
    # challenge was passed, now validate the webhook payload
    else: 
        # get details of the inbound webhook request for validation
        request_signature = request.headers.get('X-Nightfall-Signature')
        request_timestamp = request.headers.get('X-Nightfall-Timestamp')
        request_data = request.get_data(as_text=True)

        if nightfall.validate_webhook(request_signature, request_timestamp, request_data):
            # check if any sensitive findings were found in the file, return if not
            if not data["findingsPresent"]: 
                print("No sensitive data present!")
                return "", 200

            # there are sensitive findings in the file
            output_results(data)
            return "", 200
        else:
            return "Invalid webhook", 500

Вы заметите, что когда есть конфиденциальные данные, мы вызываем метод output_results(). Давайте напишем это дальше. В output_results() мы собираемся проанализировать результаты и записать их в виде строк в наш CSV-файл.

def output_results(data):
	findings_url = data['findingsURL']
	# open findings URL provided by Nightfall to access findings
	with urllib.request.urlopen(findings_url) as url:
		findings = json.loads(url.read().decode())
		findings = findings['findings']

	print(f"Sensitive data found, outputting {len(findings)} finding(s) to CSV | UploadID {data['uploadID']}")
	table = []
	# loop through findings JSON, get relevant finding metadata, write each finding as a row into output CSV
	for i, finding in enumerate(findings):
		row = [
			data['uploadID'],
			i+1,
			datetime.now(),
			repr(finding['beforeContext']), 
			repr(finding['finding']),
			repr(finding['afterContext']),
			finding['detector']['name'],
			finding['confidence'],
			finding['location']['byteRange'],
			finding['matchedDetectionRules']
		]
		table.append(row)
		with open(f"results.csv", 'a') as csvfile:
			writer = csv.writer(csvfile)
			writer.writerow(row)
	return

Перезапустите сервер, чтобы изменения распространились. Мы рассмотрим вывод консоли и CSV нашей конечной точки веб-перехватчика в следующем разделе.

Сканировать измененные файлы в режиме реального времени

В нашем предыдущем окне командной строки теперь мы можем снова обратить внимание на scanner.py. Теперь у нас есть URL-адрес веб-перехватчика, поэтому давайте также установим его здесь и запустим наш сканер.

export NIGHTFALL_SERVER_URL=https://3ecedafba368.ngrok.io 
python scanner.py

Чтобы запустить событие сканирования файла, загрузите следующий образец файла данных. Предполагая, что он автоматически загружается в вашу папку Загрузки, это должно немедленно вызвать событие изменения файла, и вы увидите вывод журнала консоли! Если нет, вы также можете загрузить файл с curl в расположение, соответствующее регулярному выражению вашего обработчика событий, которое мы установили ранее.

curl https://raw.githubusercontent.com/nightfallai/dlp-sample-data/main/sample-pci.csv > ~/Downloads/sample-pci.csv

Вы увидите следующий вывод консоли из scanner.py:

Event type: modified | Path: /Users/myuser/Downloads/sample-pci.csv Scan initiated | Path /Users/myuser/Downloads/sample-pci.csv | UploadID c23fdde2-5e98-4183-90b0-31e2cdd20ac0

И следующий консольный вывод с нашего сервера веб-перехватчиков:

Sensitive data found, outputting 10 finding(s) to CSV | UploadID ac6a4a9d-a7b9-4a78-810d-8a66f7644704

И следующие конфиденциальные данные, написанные results.csv:

upload_id,#,datetime,before_context,finding,after_context,detector,confidence,loc,detection_rules ac6a4a9d-a7b9-4a78-810d-8a66f7644704,1,2021-12-04 22:12:21.039602,'Name\tCredit Card\nRep. Viviana Hintz\t','5433-9502-3725-7862','\nEloisa Champlin\t3457-389808-83234\nOmega',Credit Card Number,VERY_LIKELY,"{'start': 36, 'end': 55}",[] ...

Каждая строка в выходном CSV-файле будет соответствовать конфиденциальной находке. Каждая строка будет иметь следующие поля, которые вы можете настроить в app.py: идентификатор загрузки, предоставленный Nightfall, увеличивающийся индекс, метка времени, символы перед конфиденциальным выводом (для контекста), сам секретный вывод, символы после конфиденциального обнаружения (для контекст), уровень достоверности обнаружения, диапазон байтов (символьные индексы) конфиденциальной находки в ее родительском файле и соответствующие правила обнаружения, которые пометили конфиденциальную находку.

Обратите внимание, что вы также можете увидеть события для системных файлов, таких как .DS_Store, или ошибки, соответствующие неудачным попыткам сканирования временных версий файлов. Это связано с тем, что выполнение таких действий, как загрузка файла, может вызвать несколько событий модификации файла. В качестве дополнения к этому руководству вы можете рассмотреть возможность их дальнейшей фильтрации, хотя они не должны влиять на нашу способность сканировать интересующие вас файлы.

Если мы оставим эти службы включенными, мы продолжим отслеживать файлы на наличие конфиденциальных данных и добавлять к нашим результатам CSV при обнаружении конфиденциальных данных!

Запуск Endpoint DLP в фоновом режиме

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

nohup python -u scanner.py > scanner.log & 
nohup gunicorn app:app > server.log &

Это вернет соответствующие идентификаторы процессов — мы всегда можем проверить их позже с помощью команды ps.

[1] 93373 
[2] 93374

Этот пост представляет собой просто пробную версию концепции защиты от потери данных для конечных точек. Создание DLP-приложения для конечной точки производственного уровня будет иметь дополнительную сложность и функциональность. Однако механизм обнаружения является одним из самых больших компонентов DLP-системы конечных точек, и этот пример должен дать вам представление о том, насколько легко интегрироваться с API-интерфейсами Nightfall и какова мощность механизма обнаружения Nightfall.

Вот несколько идей о том, как вы можете расширить эту услугу:

  • Запустите сканер на машинах EC2, чтобы сканировать рабочие машины в режиме реального времени.
  • Реагируйте на дополнительные системные события, такие как ввод-вывод USB-накопителей и внешних портов.
  • Реализуйте действия по исправлению, такие как уведомления конечных пользователей или удаление файлов.
  • Отредактируйте конфиденциальные результаты, прежде чем записывать их в файл результатов.
  • Сохраняйте результаты в облаке для центральной отчетности
  • Упакуйте в исполняемый файл, чтобы приложение можно было легко запустить
  • Сканировать все файлы на диске при первой загрузке приложения

Первоначально опубликовано на https://docs.nightfall.ai 16 декабря 2021 г.