使用 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

相关推荐
开源社13 分钟前
一场开源视角的AI会议即将在南京举办
人工智能·开源
FreeIPCC13 分钟前
谈一下开源生态对 AI人工智能大模型的促进作用
大数据·人工智能·机器人·开源
梦幻通灵24 分钟前
ES分词环境实战
大数据·elasticsearch·搜索引擎
Elastic 中国社区官方博客29 分钟前
Elasticsearch 中的热点以及如何使用 AutoOps 解决它们
大数据·运维·elasticsearch·搜索引擎·全文检索
机器之心32 分钟前
全球十亿级轨迹点驱动,首个轨迹基础大模型来了
人工智能·后端
z千鑫32 分钟前
【人工智能】PyTorch、TensorFlow 和 Keras 全面解析与对比:深度学习框架的终极指南
人工智能·pytorch·深度学习·aigc·tensorflow·keras·codemoss
EterNity_TiMe_33 分钟前
【论文复现】神经网络的公式推导与代码实现
人工智能·python·深度学习·神经网络·数据分析·特征分析
机智的小神仙儿1 小时前
Query Processing——搜索与推荐系统的核心基础
人工智能·推荐算法
AI_小站1 小时前
RAG 示例:使用 langchain、Redis、llama.cpp 构建一个 kubernetes 知识库问答
人工智能·程序人生·langchain·kubernetes·llama·知识库·rag
Doker 多克1 小时前
Spring AI 框架使用的核心概念
人工智能·spring·chatgpt