使用 NLP 和模式匹配检测、评估和编辑日志中的个人身份信息 - 第 1 部分

作者:来自 Elastic Stephen Brown

如何使用 Elasticsearch 和 NLP 检测和评估日志中的 PII。

简介:

分布式系统中高熵日志的普遍存在大大增加了 PII(Personally Identifiable Information - 个人身份信息)渗入我们日志的风险,这可能导致安全和合规性问题。这篇由两部分组成的博客深入探讨了使用 Elastic Stack 识别和管理此问题的关键任务。我们将探索使用 NLP(Natural Language Processing - 自然语言处理)和模式匹配来检测、评估并在可行的情况下从正在被提取到 Elasticsearch 的日志中删除 PII。

在本博客的第 1 部分中,我们将介绍以下内容:

  • 回顾我们可用于管理日志中的 PII 的技术和工具
  • 了解 NLP/NER 在 PII 检测中的作用
  • 构建可组合的处理管道以检测和评估 PII
  • 对日志进行采样并通过 NER 模型运行它们
  • 评估 NER 模型的结果

在本博客的第 2 部分中,我们将介绍以下内容:

  • 使用 NER 和 redact 处理器编辑 PII
  • 应用字段级安全性来控制对未编辑数据的访问
  • 增强仪表板和警报
  • 生产注意事项和扩展
  • 如何在传入或历史数据上运行这些流程

以下是我们将在 2 个博客中构建的总体流程:

本练习的所有代码均可在以下位置找到:https://github.com/bvader/elastic-pii

工具和技术

我们将在本练习中使用四种通用功能。

  • 命名实体识别检测 (NER)
  • 模式匹配检测
  • 日志采样
  • 将采集管道作为可组合处理

命名实体识别 (Named Entity Recognition - NER) 检测

NER 是自然语言处理 (NLP) 的一个子任务,涉及识别非结构化文本中的命名实体并将其归类为预定义类别,例如:

  • 人(Person):个人姓名,包括名人、政治家和历史人物。
  • 组织(Organization):公司、机构和组织的名称。
  • 位置(Location):地理位置,包括城市、国家和地标。
  • 事件(Event):事件名称,包括会议、会议和节日。

对于我们的 PII 用例,我们将选择基本 BERT NER 模型 bert-base-NER,该模型可以从 Hugging Face 下载并作为训练模型加载到 Elasticsearch 中。

重要提示:NER/NLP 模型占用大量 CPU 资源,大规模运行成本高昂;因此,我们希望采用采样技术来了解日志中的风险,而无需通过 NER 模型发送完整的日志量。我们将在博客的第 2 部分讨论 NER 模型的性能和扩展性。

模式匹配检测

除了使用 NER,正则表达式模式匹配也是一种基于常见模式检测和编辑 PII 的强大工具。Elasticsearch redact 处理器就是为这种用例构建的。

日志采样

考虑到 NER 的性能影响以及我们可能会将大量日志导入 Elasticsearch 的事实,对传入的日志进行采样是有意义的。我们将构建一个简单的日志采样器来实现这一点。

摄取管道作为可组合处理

我们将创建多个管道,每个管道专注于特定功能,并创建一个主摄取管道来协调整个过程。

构建处理流程

日志采样 + 可组合采集管道

我们要做的第一件事是设置一个采样器来采样我们的日志。此采集管道仅采用 0(无日志)和 10000(所有日志)之间的采样率,允许低至 ~0.01% 的采样率,并使用 sample.sampled: true 标记采样日志。对日志的进一步处理将由 sample.sampled 的值驱动。sample.sample_rate 可以在此处设置或从编排管道 "passed in"。

该命令应从 Kibana -> Dev Tools 运行

以下三段代码可在此处找到

日志采样器管道代码:

# logs-sampler pipeline - part 1
DELETE _ingest/pipeline/logs-sampler
PUT _ingest/pipeline/logs-sampler
{
  "processors": [
    {
      "set": {
        "description": "Set Sampling Rate 0 None 10000 all allows for 0.01% precision",
        "if": "ctx.sample.sample_rate == null",
        "field": "sample.sample_rate",
        "value": 10000
      }
    },
    {
      "set": {
        "description": "Determine if keeping unsampled docs",
        "if": "ctx.sample.keep_unsampled == null",
        "field": "sample.keep_unsampled",
        "value": true
      }
    },
    {
      "set": {
        "field": "sample.sampled",
        "value": false
      }
    },
    {
      "script": {
        "source": """ Random r = new Random();
        ctx.sample.random = r.nextInt(params.max); """,
        "params": {
          "max": 10000
        }
      }
    },
    {
      "set": {
        "if": "ctx.sample.random <= ctx.sample.sample_rate",
        "field": "sample.sampled",
        "value": true
      }
    },
    {
      "drop": {
         "description": "Drop unsampled document if applicable",
        "if": "ctx.sample.keep_unsampled == false && ctx.sample.sampled == false"
      }
    }
  ]
}

现在,让我们测试日志采样器。我们将构建可组合管道的第一部分。我们将向 logs-generic-default 数据流发送日志。考虑到这一点,我们将创建 logs@custom 摄取管道,该管道将使用日志数据流框架自动调用以进行自定义。我们将添加一个额外的抽象级别,以便你可以将此 PII 处理应用于其他数据流。

接下来,我们将创建 process-pii 管道。这是核心处理管道,我们将在其中编排 PII 处理组件管道。在第一步中,我们将简单地应用采样逻辑。请注意,我们将采样率设置为 100,相当于日志的 10%。

process-pii 管道代码:

# Process PII pipeline - part 1
DELETE _ingest/pipeline/process-pii
PUT _ingest/pipeline/process-pii
{
  "processors": [
    {
      "set": {
        "description": "Set true if enabling sampling, otherwise false",
        "field": "sample.enabled",
        "value": true
      }
    },
    {
      "set": {
        "description": "Set Sampling Rate 0 None 10000 all allows for 0.01% precision",
        "field": "sample.sample_rate",
        "value": 1000
      }
    },
    {
      "set": {
        "description": "Set to false if you want to drop unsampled data, handy for reindexing hostorical data",
        "field": "sample.keep_unsampled",
        "value": true
      }
    },
    {
      "pipeline": {
        "if": "ctx.sample.enabled == true",
        "name": "logs-sampler",
        "ignore_failure": true
      }
    }
  ]
}

最后,我们创建日志 logs@custom,它将根据正确的 data_stream.dataset 简单地调用我们的 process-pii 管道

logs@custom 管道代码

# logs@custom pipeline - part 1
DELETE _ingest/pipeline/logs@custom
PUT _ingest/pipeline/logs@custom
{
  "processors": [
    {
      "set": {
        "field": "pipelinetoplevel",
        "value": "logs@custom"
      }
    },
        {
      "set": {
        "field": "pipelinetoplevelinfo",
        "value": "{{{data_stream.dataset}}}"
      }
    },
    {
      "pipeline": {
        "description" : "Call the process_pii pipeline on the correct dataset",
        "if": "ctx?.data_stream?.dataset == 'pii'", 
        "name": "process-pii"
      }
    }
  ]
}

现在,让我们测试一下,看看采样是如何进行的。

按照 数据加载附录 中的说明加载数据。让我们先使用示例数据,稍后我们将在本博客的末尾讨论如何使用传入或历史日志进行测试。

如果你使用 KQL 过滤器 data_stream.dataset : pii 和 Breakdown by sample.sampled 查看 Observability -> Logs -> Logs Explorer,你应该会看到细分约为 10%

此时,我们有一个可组合的采集管道,用于 "sampling" 日志。作为奖励,你还可以将此日志采样器用于任何其他用例。

NER 管道的加载、配置和执行

加载 NER 模型

你需要一个机器学习节点来运行 NER 模型。在本练习中,我们使用 AWS 上的 Elastic Cloud Hosted DeploymentCPU Optimized (ARM) 架构。NER 推理将在机器学习 AWS c5d 节点上运行。未来会有 GPU 选项,但今天我们将坚持使用 CPU 架构。

本练习将使用单个 c5d,配备 8 GB RAM 和 4.2 vCPU,最高可达 8.4 vCPU。

请参阅有关如何将 NLP 训练的模型导入 Elasticsearch 的官方文档,获取有关上传、配置和部署模型的完整说明。

获取模型的最快方法是使用 Eland Docker 方法。

以下命令将模型加载到 Elasticsearch 中,但不会启动它。我们将在下一步中执行此操作。

docker run -it --rm --network host docker.elastic.co/eland/eland \
  eland_import_hub_model \
  --url https://mydeployment.es.us-west-1.aws.found.io:443/ \
  -u elastic -p password \
  --hub-model-id dslim/bert-base-NER --task-type ner

部署并启动 NER 模型

一般来说,为了提高摄取性能,可以通过向部署添加更多分配来提高吞吐量。为了提高搜索速度,请增加每个分配的线程数。

为了扩展摄取,我们将专注于扩展已部署模型的分配。有关此主题的更多信息,请在此处获取。分配的数量必须小于每个节点可用的分配处理器(核,而不是 vCPU)。

部署并启动 NER 模型。我们将使用启动训练模型部署 API 执行此操作

我们将配置以下内容:

  • 4 个分配以允许更多并行摄取

  • 每个分配 1 个线程

  • 0 个 Byes Cache,因为我们预计缓存命中率较低

  • 8192 队列

    Start the model with 4 Allocators x 1 Thread, no cache, and 8192 queue

    POST _ml/trained_models/dslim__bert-base-ner/deployment/_start?cache_size=0b&number_of_allocations=4&threads_per_allocation=1&queue_capacity=8192

你应该会收到类似这样的回应:

{
  "assignment": {
    "task_parameters": {
      "model_id": "dslim__bert-base-ner",
      "deployment_id": "dslim__bert-base-ner",
      "model_bytes": 430974836,
      "threads_per_allocation": 1,
      "number_of_allocations": 4,
      "queue_capacity": 8192,
      "cache_size": "0",
      "priority": "normal",
      "per_deployment_memory_bytes": 430914596,
      "per_allocation_memory_bytes": 629366952
    },
...
    "assignment_state": "started",
    "start_time": "2024-09-23T21:39:18.476066615Z",
    "max_assigned_allocations": 4
  }
}

NER 模型已部署并启动,可供使用。

以下摄取管道通过 inference处理器实现 NER 模型。

这里有大量代码,但现在只有两个感兴趣的项目。其余代码是条件逻辑,用于驱动一些额外的特定行为,我们将在将来仔细研究这些行为。

  • 推理处理器通过我们之前加载的 ID 调用 NER 模型,并传递要分析的文本,在本例中是 message 字段,这是我们想要传递给 NER 模型以分析 PII 的 text_field
  • script 处理器循环遍历消息字段并使用 NER 模型生成的数据将识别的 PII 替换为编辑的占位符。这看起来比实际更复杂,因为它只是循环遍历 ML 预测数组并用常量替换消息字符串中的预测,并将结果存储在新字段 redact.message 中。我们将在以下步骤中更仔细地研究这一点。

以下三段代码可在此处找到

NER PII 管道:

# NER Pipeline
DELETE _ingest/pipeline/logs-ner-pii-processor
PUT _ingest/pipeline/logs-ner-pii-processor
{
  "processors": [
    {
      "set": {
        "description": "Set to true to actually redact, false will run processors but leave original",
        "field": "redact.enable",
        "value": true
      }
    },
    {
      "set": {
        "description": "Set to true to keep ml results for debugging",
        "field": "redact.ner.keep_result",
        "value": true
      }
    },
    {
      "set": {
        "description": "Set to PER, LOC, ORG to skip, or NONE to not drop any replacement",
        "field": "redact.ner.skip_entity",
        "value": "NONE"
      }
    },
    {
      "set": {
        "description": "Set to PER, LOC, ORG to skip, or NONE to not drop any replacement",
        "field": "redact.ner.minimum_score",
        "value": 0.0
      }
    },
    {
      "set": {
        "if" : "ctx.redact.message == null",
        "field": "redact.message",
        "copy_from": "message"
      }
    },
    {
      "set": {
        "field": "redact.successful",
        "value": true
      }
    },
    {
      "inference": {
        "model_id": "dslim__bert-base-ner",
        "field_map": {
          "message": "text_field"
        },
        "on_failure": [
          {
            "set": {
              "description": "Set 'error.message'",
              "field": "failure",
              "value": "REDACT_NER_FAILED"
            }
          },
          {
            "set": {
              "field": "redact.successful",
              "value": false
            }
          }
        ]
      }
    },
    {
      "script": {
        "if": "ctx.failure_ner != 'REDACT_NER_FAILED'",
        "lang": "painless",
        "source": """String msg = ctx['message'];
          for (item in ctx['ml']['inference']['entities']) {
          	if ((item['class_name'] != ctx.redact.ner.skip_entity) && 
          	  (item['class_probability'] >= ctx.redact.ner.minimum_score)) {  
          		  msg = msg.replace(item['entity'], '<' + 
          		  'REDACTNER-'+ item['class_name'] + '>')
          	}
          }
          ctx.redact.message = msg""",
        "on_failure": [
          {
            "set": {
              "description": "Set 'error.message'",
              "field": "failure",
              "value": "REDACT_REPLACEMENT_SCRIPT_FAILED",
              "override": false
            }
          },
          {
            "set": {
              "field": "redact.successful",
              "value": false
            }
          }
        ]
      }
    },
    {
      "remove": {
        "if": "ctx.redact.ner.keep_result != true",
        "field": [
          "ml"
        ],
        "ignore_missing": true,
        "ignore_failure": true
      }
    }
  ],
  "on_failure": [
    {
      "set": {
        "field": "failure",
        "value": "GENERAL_FAILURE",
        "override": false
      }
    }
  ]
}

更新后的 PII 处理器管道,现在调用 NER 管道。

process-pii 管道代码:

# Updated Process PII pipeline that now call the NER pipeline
DELETE _ingest/pipeline/process-pii
PUT _ingest/pipeline/process-pii
{
  "processors": [
    {
      "set": {
        "description": "Set true if enabling sampling, otherwise false",
        "field": "sample.enabled",
        "value": true
      }
    },
    {
      "set": {
        "description": "Set Sampling Rate 0 None 10000 all allows for 0.01% precision",
        "field": "sample.sample_rate",
        "value": 1000
      }
    },
    {
      "set": {
        "description": "Set to false if you want to drop unsampled data, handy for reindexing hostorical data",
        "field": "sample.keep_unsampled",
        "value": true
      }
    },
    {
      "pipeline": {
        "if": "ctx.sample.enabled == true",
        "name": "logs-sampler",
        "ignore_failure": true
      }
    },
    {
      "pipeline": {
        "if": "ctx.sample.enabled == false || (ctx.sample.enabled == true && ctx.sample.sampled == true)",
        "name": "logs-ner-pii-processor"
      }
    }
  ]
}

现在按照重新加载日志中的说明重新加载数据

结果

让我们看看 NER 处理后的结果。在带有 KQL 查询栏的日志资源管理器中,执行以下查询 data_stream.dataset : pii and ml.inference.entities.class_name : ("PER" and "LOC" and "ORG" )

日志资源管理器(Logs Explorer)应该看起来像这样,打开顶部消息查看详细信息。

NER 模型结果

让我们仔细看看这些字段的含义。

字段:ml.inference.entities.class_name

示例值:[PER、PER、LOC、ORG、ORG]

说明:NER 模型已识别的命名实体类的数组。

字段:ml.inference.entities.class_probability

示例值:[0.999、0.972、0.896、0.506、0.595]

说明:class_probability 是介于 0 和 1 之间的值,表示给定数据点属于某个类的可能性。数字越高,数据点属于命名类的概率就越高。这很重要,因为在下一篇博客中,我们可以决定要用来发出警报和编辑的阈值。你可以在此示例中看到它将 LOC 标识为 ORG,我们可以通过设置阈值来过滤掉/找到它们。

字段:ml.inference.entities.entity

示例值:[Paul Buck, Steven Glens, South Amyborough, ME, Costco]

描述:已识别的实体数组,与 class_name 和 class_probability 位置对齐。

字段:ml.inference.predicted_value

示例值:[2024-09-23T14:32:14.608207-07:00Z] log.level=INFO:订单 #4594 付款成功(用户:[Paul Buck](PER&Paul+Buck),david59@burgess.net)。电话:726-632-0527x520,地址:3713 [Steven Glens](PER&Steven+Glens), [South Amyborough](LOC&South+Amyborough), [ME](ORG&ME) 93580,订购自:[Costco](ORG&Costco)

描述:模型的预测值。

PII 评估仪表板

让我们快速浏览一下为评估 PII 数据而构建的仪表板。

要加载仪表板,请转到 Kibana -> Stack Management -> Saved Objects 并导入 pii-dashboard-part-1.ndjson 文件,该文件可在此处找到:

https://github.com/bvader/elastic-pii/elastic/pii-dashboard-part-1.ndjson

有关 Kibana 已保存对象的更多完整说明可在此处找到。

加载仪表板后,导航到它并选择正确的时间范围,你应该会看到如下所示的内容。它显示了诸如采样率、带有 NER 的日志百分比、NER 分数趋势等指标。我们将在本博客的第 2 部分中检查评估和操作。

总结和后续步骤

在博客的第一部分中,我们完成了以下工作。

  • 回顾了我们可用于 PII 检测和评估的技术和工具
  • 回顾了 NLP / NER 在 PII 检测和评估中的作用
  • 构建了必要的可组合摄取管道以对日志进行采样并通过 NER 模型运行它们
  • 回顾了 NER 结果并准备转到第二篇博客

在本博客的即将发布的第二部分中,我们将介绍以下内容:

  • 使用 NER 和编辑处理器编辑 PII
  • 应用字段级安全性来控制对未编辑数据的访问
  • 增强仪表板和警报
  • 生产注意事项和扩展
  • 如何在传入或历史数据上运行这些流程

数据加载附录

代码

数据加载代码可在此处找到:

https://github.com/bvader/elastic-pii

$ git clone https://github.com/bvader/elastic-pii.git

如果不更改任何参数,这将在名为 pii.log 的文件中创建 10000 个随机日志,其中含有包含和不包含 PII 的日志。

编辑 load_logs.py 并设置以下内容:

# The Elastic User 
ELASTIC_USER = "elastic"

# Password for the 'elastic' user generated by Elasticsearch
ELASTIC_PASSWORD = "askdjfhasldfkjhasdf"

# Found in the 'Manage Deployment' page
ELASTIC_CLOUD_ID = "deployment:sadfjhasfdlkjsdhf3VuZC5pbzo0NDMkYjA0NmQ0YjFiYzg5NDM3ZDgxM2YxM2RhZjQ3OGE3MzIkZGJmNTE0OGEwODEzNGEwN2E3M2YwYjcyZjljYTliZWQ="

然后运行以下命令。

$ python load_logs.py

重新加载日志

注意要重新加载日志,你只需重新运行上述命令即可。在此练习期间,你可以多次运行该命令,日志将被重新加载(实际上是再次加载)。新日志不会与之前的运行发生冲突,因为每次运行都会有一个唯一的 run.id,它会在加载过程结束时显示。

$ python load_logs.py

原文:Using NLP and Pattern Matching to Detect, Assess, and Redact PII in Logs - Part 1 --- Elastic Observability Labs

相关推荐
MrJson-架构师3 分钟前
15款行业大数据报告下载网站
大数据
葡萄爱4 分钟前
OpenCV图像分割
人工智能·opencv·计算机视觉
探索云原生13 分钟前
在 K8S 中创建 Pod 是如何使用到 GPU 的: nvidia device plugin 源码分析
ai·云原生·kubernetes·go·gpu
启明真纳15 分钟前
elasticache备份
运维·elasticsearch·云原生·kubernetes
老周聊架构28 分钟前
聊聊Flink:Flink的状态管理
大数据·flink
www_3dyz_com30 分钟前
人工智能在VR展览中扮演什么角色?
人工智能·vr
刘不二35 分钟前
大模型应用—HivisionIDPhotos 证件照在线制作!支持离线、换装、美颜等
人工智能·开源
筒栗子37 分钟前
复习打卡大数据篇——Hadoop HDFS 01
大数据·hadoop·hdfs
feilieren1 小时前
AI 视频:初识 Pika 2.0,基本使用攻略
人工智能·ai视频
开放知识图谱2 小时前
论文浅尝 | HippoRAG:神经生物学启发的大语言模型的长期记忆(Neurips2024)
人工智能·语言模型·自然语言处理