悠扬随风 发表于 2024-9-1 10:39:47

【数据掩护】微软开源数据掩护项目Presidio-从入门到精通

文章目录
1 简介
2 敏感数据分析器
2.1 简单示例
2.2 敏感信息列表
2.3 正则表达式
2.4 自界说辨认器
2.5 上下文增强机制
2.6 白名单
2.7 追踪决策日志
2.8 YAML设置文件
3 敏感数据匿名器
3.1 简单示例
3.2 自界说匿名设置
3.3 自界说匿名器
3.4 加密和解密
4 NLP模型和语言类型

1 简介

Presidio,源自拉丁语,寓意"掩护"或"驻军",是由微软推出的一项开源数据掩护计划。该项目致力于协助企业与开辟者在处置惩罚数据时,快速辨认并脱敏敏感信息。它能够辨认文本和图像中的多种敏感数据,包括但不限于信用卡号码、个人姓名、地理位置和电话号码等,并通过定制化的格式举行脱敏处置惩罚,以增强数据的安全性。
        主要特点


[*]内置40多种敏感数据辨认器,包括日期、邮箱地点、银行账号、IP地点、地理信息等。
[*]支持用户自界说敏感数据辨认器,包括设置敏感数据列表、正则表达式以及自界说敏感数据检测模块等。
[*]支持利用NLP模型、上下文检测来提升扫描结果置信度。
[*]支持多种NLP模型,包括SpaCy、Transformers和Stanza。
[*]支持多种方式集成,可以作为Python库利用,也可以通过Docker容器化部署。
[*]支持多种脱敏方式,例如替换、哈希、掩码等,包括用户自界说方式。
[*]支持文本和图片类型敏感信息扫描,此中图片类型扫描依赖于OCR库。
        模块


[*]Presidio analyzer:该模块主要负责文本类数据敏感信息扫描。
[*]Presidio anonymizer:该模块主要负责对已检测到的敏感实体举行脱敏处置惩罚。
[*]Presidio image redactor:该模块主要利用OCR技能辨认图片中敏感信息并举行脱敏处置惩罚。
2 敏感数据分析器

Presidio分析器(Analyzer)中包罗多种敏感数据辨认器(Recognizer),每种辨认器支持特定类型的敏感数据扫描。辨认器支持正则表达式、敏感数据列表、自界说辨认逻辑,而且每个辨认器支持利用NLP模型以及上下文检测,对每个敏感信息目标举行评分来体现扫描结果的置信度。
必要留意的是,下面我在对Presidio介绍的时候会强调区分分析器和辨认器,分析器对象中会包罗若干个辨认器,而辨认器主要辨认特定类型敏感数据。
2.1 简单示例

下面是对Presidio分析器利用的最基础的示例,包括创建分析器对象、扫描文本数据以及打印结果:
# 导入分析器模块
from presidio_analyzer import AnalyzerEngine

# 待扫描的文本数据
text = "His name is Mr. Jones and his phone number is 212-555-5555"

# 创建分析器对象
analyzer = AnalyzerEngine()
# 执行分析器的分析函数,分析文本数据
analyzer_results = analyzer.analyze(text=text, language="en")

# 打印执行结果
print(analyzer_results) 上面示例中没有指定分析器执行哪种类型敏感信息扫描,因此默认情况下分析器将加载全部内置辨认器对文本数据执行扫描。扫描结果如下,结果以列表的形式呈现。
   
扫描结果中主要包罗四个关键字段分别是type、start、end和score,其含义如下:


[*]type:敏感信息类型;
[*]start:敏感信息起始位置;
[*]end:敏感信息竣事位置;
[*]score:该敏感信息扫描结果置信度。
2.2 敏感信息列表

Presidio具有极强的可扩展性,除了内置部门敏感信息辨认器外,支持用户自界说敏感辨认器并加入到分析器中。敏感信息列表是创建辨认器的一种方式。
用户可以将已确定的敏感词语汇总起来,以列表的形式添加到新创建的辨认器当中,并将新创建的辨认器添加到分析器中执行对目标数据的扫描。假如目标数据中包罗敏感信息列表中的词语,该词语会在返回结果中体现。
假设下面是已汇总的敏感单词信息:
   password、secret、China、Government
第一步:将这些敏感信息以列表的形式存储:
   sensitive_list = ["password", "secret", "China", "Government"]
第二步:新建敏感信息辨认器,并将敏感信息列表添加到辨认器当中:
from presidio_analyzer import PatternRecognizer

// 参数supported_entity标识敏感信息类型名称,同上面示例中PERSON、PHONE_NUMBER等
// 参数deny_list即为敏感信息列表。
sensitive_recognizer = PatternRecognizer(supported_entity="SENSITIVE", deny_list=sensitive_list) 举行到这一步,其实我们可以直接对文本内容举行扫描了:
from presidio_analyzer import PatternRecognizer

text1 = "I have a secret, but I can't tell you."
result = sensitive_recognizer.analyze(text1, entities=["SENSITIVE"])
print(f"Result:\n {result}")

# 下面是执行结果
'''
Result:

''' 末了我们必要将新创建的辨认器添加到分析器的辨认器列表中即可:
from presidio_analyzer import AnalyzerEngine

analyzer = AnalyzerEngine()
analyzer.registry.add_recognizer(sensitive_recognizer) 综上,支持自界说敏感信息列表扫描的分析器创建主要分为以下四步:
(1)汇总敏感信息词语,并以列表的形式呈现;
(2)创建敏感信息辨认器,将敏感信息列表添加到辨认器中;
(3)创建敏感信息分析器,将辨认器添加到辨认器列表中;
(4)执行分析器的分析函数扫描指定目标内容;
上面四个步骤的示例代码如下:
from presidio_analyzer import AnalyzerEngine, PatternRecognizer

# (1)汇总敏感信息词语,并以列表的形式呈现;
sensitive_list = ["password", "secret", "China", "Government"]

text1 = "I have a secret, but I can't tell you."

# (2)创建敏感信息识别器,将敏感信息列表添加到识别器中;
sensitive_recognizer = PatternRecognizer(supported_entity="SENSITIVE", deny_list=sensitive_list)

# (3)创建敏感信息分析器,将识别器添加到识别器列表中;
analyzer = AnalyzerEngine()
analyzer.registry.add_recognizer(sensitive_recognizer)

# (4)执行分析器的分析函数扫描指定目标内容;
result = analyzer.analyze(text=text1, language="en", entities=["SENSITIVE"])
print(f"Result:\n {result}") 2.3 正则表达式

除了添加敏感信息列表外,Presidio还支持用户通过正则表达式来形貌敏感数据特性。
假设我们必要扫描出文本内容中全部数字信息,那么我们可以计划一个正则表达式用于辨认器扫描:
from presidio_analyzer import Pattern, PatternRecognizer

# 正则表达式"\d+"表示匹配连续一个或多个数字
# 参数score为基础得分,即命中此正则表达式时,置信度得分0.5
numbers_pattern = Pattern(name="numbers_pattern", regex="\d+", score=0.5)

# 将带有正则表达式的Pattern对象添加到识别器中
number_recognizer = PatternRecognizer(
    supported_entity="NUMBER", patterns=
) 此时新创建的敏感数据分析器就可以执行扫描操纵了:
text2 = "I live in 510 Broad st."

numbers_result = number_recognizer.analyze(text=text2, entities=["NUMBER"])
print("Result:\n" ,numbers_result)

# 下面是执行结果
'''
Result:

''' 末了我们必要将新创建的辨认器添加到分析器的辨认器列表中即可。
综上,支持自界说正则表达式扫描的分析器创建主要分为以下四步:
(1)根据目标敏感信息特性,编写正则表达式,并创建模式对象(Pattern);
(2)创建敏感信息辨认器,将模式对象添加到辨认器中;
(3)创建敏感信息分析器,将辨认器添加到辨认器列表中;
(4)执行分析器的分析函数扫描指定目标内容;
上面四个步骤的示例代码如下:
from presidio_analyzer import Pattern, PatternRecognizer, AnalyzerEngine

# (1)根据目标敏感信息特征,编写正则表达式,并创建模式对象(Pattern);
numbers_pattern = Pattern(name="numbers_pattern", regex="\d+", score=0.5)

# (2)创建敏感信息识别器,将模式对象添加到识别器中;
number_recognizer = PatternRecognizer(
    supported_entity="NUMBER", patterns=
)

text2 = "I live in 510 Broad st."

# (3)创建敏感信息分析器,将识别器添加到识别器列表中
analyzer = AnalyzerEngine()
analyzer.registry.add_recognizer(number_recognizer)

# (4)执行分析器的分析函数扫描指定目标内容;
result = analyzer.analyze(text=text2, language="en", entities=["NUMBER"])
print(f"Result:\n {result}") 2.4 自界说辨认器

自界说敏感数据辨认器是Presidio强大的扩展特性之一。在2.3章节我们提到对于数字的匹配我们可以编写正则表达式来举行扫描,但是所编写的正则表达式具有局限性,例如只能扫描出"1、2、3"这种,而对于"One、Two、Three"这种形式却一筹莫展。
针对上面这种情况,用户可以创建自界说敏感数据辨认器来执行对数据的扫描。除此之外,在自界说扫描器中还可以利用spaCy库对文本内容分词的结果辅助执行数据扫描。
第一步:创建自界说辨认器类,要求此类要继承EntityRecognizer类型,而且必要重写load和analyze方法。除此之外,analyze方法中传入一个NlpArtifacts类型的对象,此对象中包罗NLP模型对文本内容分词的结果。
from typing import List
from presidio_analyzer import EntityRecognizer, RecognizerResult
from presidio_analyzer.nlp_engine import NlpArtifacts

class NumbersRecognizer(EntityRecognizer):
    # 置信度
    expected_confidence_level = 0.7

    def load(self) -> None:
      pass

    def analyze(
      self, text: str, entities: List, nlp_artifacts: NlpArtifacts
    ) -> List:
      """
      下面算法既能找到"1、2、3",也能找到"One、Two、Three"。
      """
      results = []

      # iterate over the spaCy tokens, and call `token.like_num`
      for token in nlp_artifacts.tokens:
            if token.like_num:
                result = RecognizerResult(
                  entity_type="NUMBER",
                  start=token.idx,
                  end=token.idx + len(token),
                  score=self.expected_confidence_level,
                )
                results.append(result)
      return results


# 创建新的敏感信息识别器对象。
new_numbers_recognizer = NumbersRecognizer(supported_entities=["NUMBER"]) 第二步:创建新的分析器,并将自界说辨认器添加到辨认器列表中即可。
from presidio_analyzer import AnalyzerEngine

text3 = "Roberto lives in Five 10 Broad st."
analyzer = AnalyzerEngine()
analyzer.registry.add_recognizer(new_numbers_recognizer)

numbers_results2 = analyzer.analyze(text=text3, language="en")
print("Results:")
print("\n".join()) 上面示例中敏感信息辨认器既可以辨认数字,也可以辨认数字类单词。
下面是上面示例执行结果:
   Results:
type: PERSON, start: 0, end: 7, score: 0.85
type: LOCATION, start: 25, end: 34, score: 0.85
type: NUMBER, start: 17, end: 21, score: 0.7
type: NUMBER, start: 22, end: 24, score: 0.7
2.5 上下文增强机制

Presidio支持上下文检测机制,该机制能够提升对检测结果的置信度。除此之外,Presidio还支持由用户自己实现上下文检测逻辑。默认情况下,Presidio利用LemmaContextAwareEnhancer来实现上下文增强机制,该类会对所扫描句子中每个token的词元和上下文内容举行比较。
下面示例中我们将邮政编码作为敏感数据,创建该敏感数据的辨认器,并通过上下文机制增强对敏感数据扫描结果的置信度。
由于邮政编码的构成比较简单,利用六位数字构成。正因云云,扫描文本内容时假如将全部六位数字构成的信息当成邮政编码,那显然会有许多辨认结果是错误的。因此假如我们利用六位数字这个特性去匹配的话,匹配结果的置信度是比较低的,在这种情况下,可以利用上下文增强机制来提升检测结果的置信度。
第一步,创建邮政编码的模式对象(Pattern):
from presidio_analyzer import (
    Pattern,
    PatternRecognizer,
    RecognizerRegistry,
    AnalyzerEngine,
)

regex = r"\d{6}"
# score为得分,即文本内容命中此正则表达式时得分,得分大小标识着扫描结果的置信度,
# 由于当前敏感数据内容特征简单,因此即使正则表达式命中,那么得分也应该设置为较低值。
postal_code_pattern = Pattern(name="postal code", regex=regex, score=0.02) 第二步,创建邮政编码的辨认器,而且开启上下文增强机制:
from presidio_analyzer import PatternRecognizer

# 创建邮政编码识别器,并且将上下文信息传入context参数。
postal_recognizer_w_context = PatternRecognizer(
    supported_entity="ZH_ZIP_CODE",
    patterns=,
    context=["postal", "postalcode"],
) 第三步,创建邮政编码分析器,分析器引擎(AnalyzerEngine)默认创建LemmaContextAwareEnhancer类对象作为上下文增强引擎,该引擎通过比对扫描内容中各个Token的词元与上下文列表是否匹配来举行判断。
from presidio_analyzer import AnalyzerEngine

analyzer = AnalyzerEngine()
analyzer.registry.add_recognizer(postal_code_recognizer)

results = analyzer.analyze(text="The postal code for Beijing is 100000.", language="en")
print(f"Result:\n {results}") 综上,分析器已支持基于上下文的中国邮政编码扫描。
示例整体代码如下:
from presidio_analyzer import (
    Pattern,
    PatternRecognizer,
    RecognizerRegistry,
    AnalyzerEngine,
)

regex = r"\d{6}"
postal_code_pattern = Pattern(name="postal code", regex=regex, score=0.02)

postal_code_recognizer = PatternRecognizer(
    supported_entity="CH_POSTAL_CODE", patterns=
)

analyzer = AnalyzerEngine()
analyzer.registry.add_recognizer(postal_code_recognizer)

results = analyzer.analyze(text="The postal code for Beijing is 100000.", language="en")

print(f"Result:\n {results}") 执行结果如下:
   Result:
 
通过分析上面结果我们发现,这段文本除了扫描出我们界说的邮政编码外,还有包罗了其它类型敏感信息结果,由于上面代码中分析器除了包罗邮政编码的辨认器外,还有一些默认的是辨认器。更有意思的是,"100000"除了被辨以为邮政编码外,还被以为是车牌号,阐明这串数字特性过于普通导致。
下面创建分析器的方法可以不加载默认的辨认器:
from presidio_analyzer import AnalyzerEngine, RecognizerRegistry

registry = RecognizerRegistry()
registry.add_recognizer(postal_code_recognizer_w_context)
analyzer = AnalyzerEngine(registry=registry) 除此之外,我们还留意到扫描结果中,对于CH_POSTAL_CODE的置信度(score)为0.4,在创建模式特性(Pattern)时设置的正则表达式掷中的得分(score)是0.02,假如不添加上下文的话,扫描结果置信度或是0.02,但是由于开启了上下文增强机制,且上下文能掷中扫描的内容,因此得分就上升到0.4。
LemmaContextAwareEnhancer类默认将上下文匹配掷中的内容实体增加0.35分,而上下文增强模块要求假如内容串上下文也掷中的情况下,得分最小应该为0.4分。因此在模式特性为0.02,上下文掷中增加0.35分的情况下仍小于0.4分,因此末了得分为0.4。
固然我们可以通过更改context_similarity_factor和min_score_with_context_similarity参数来更改上下文检测引擎的默认值。context_similarity_factor为掷中上下文增加的得分。min_score_with_context_similarity为掷中上下文后最低分值。示例如下:
from presidio_analyzer import (
    Pattern,
    PatternRecognizer,
    RecognizerRegistry,
    AnalyzerEngine,
)
from presidio_analyzer.context_aware_enhancers import LemmaContextAwareEnhancer

regex = r"\d{6}"
postal_code_pattern = Pattern(name="postal code", regex=regex, score=0.02)

postal_code_recognizer = PatternRecognizer(
    supported_entity="CH_POSTAL_CODE", patterns=,
    context=["postal", "PostalCode"]
)

# 创建LemmaContextAwareEnhancer对象,并将想要更改的得分机制传入
context_aware_enhancer = LemmaContextAwareEnhancer(
    context_similarity_factor=0.45, min_score_with_context_similarity=0.4
)

# 下面这种创建分析器的方法只加载邮政编码的识别器,不加载默认的其它识别器。
registry = RecognizerRegistry()
registry.add_recognizer(postal_code_recognizer)
analyzer = AnalyzerEngine(registry=registry, context_aware_enhancer=context_aware_enhancer)

results = analyzer.analyze(text="The postal code for Beijing is 100000.", language="en")

print(f"Result:\n {results}") 执行结果如下:
   Result:

2.6 白名单

Presidio支持白名单列表,我们只必要将一些关键字放入白名单列表中,即使这些关键字属于敏感信息,也会被忽略掉,不会包罗在扫描结果中。
下面是白名单的利用示例:
from presidio_analyzer import AnalyzerEngine

white_list = [
    "Beijing",
    "John"
]

text = "His name is Mr. John and his phone number is 212-555-5555, he live in Beijing."

analyzer_1 = AnalyzerEngine()
result = analyzer_1.analyze(text=text, language='en')
print("添加白名单前扫描结果:", result)

analyzer_2 = AnalyzerEngine()
result = analyzer_2.analyze(text=text, language='en', allow_list=white_list )
print("添加白名单后扫描结果:", result) 上面示例执行结果如下:
   添加白名单前扫描结果:
添加白名单后扫描结果:
通过执行结果可以看到,添加白名单之后,"john"和"Beijing"不再视为敏感数据实体。
2.7 追踪决策日志

Presidi的分析器支持决策日志输出。决策日志形貌了为什么内容实体被判断为敏感数据,决策日志内容包罗:


[*]哪个辨认器(Recognizer)检测出敏感内容实体;
[*]哪个正则表达式掷中敏感内容实体;
[*]上下文增强机制提升的得分值(score);
[*]上下文增强机制中掷中的上下文单词;
开启决策日志的输出只必要在执行分析方法时,将return_decision_process参数设置成True即可。示例如下:
from presidio_analyzer import (
    Pattern,
    PatternRecognizer,
    RecognizerRegistry,
    AnalyzerEngine,
)
from presidio_analyzer.context_aware_enhancers import LemmaContextAwareEnhancer
import pprint

regex = r"\d{6}"
postal_code_pattern = Pattern(name="postal code", regex=regex, score=0.02)

postal_code_recognizer = PatternRecognizer(
    supported_entity="CH_POSTAL_CODE", patterns=,
    context=["postal", "PostalCode"]
)

context_aware_enhancer = LemmaContextAwareEnhancer(
    context_similarity_factor=0.45, min_score_with_context_similarity=0.4
)

registry = RecognizerRegistry()
registry.add_recognizer(postal_code_recognizer)
analyzer = AnalyzerEngine(registry=registry, context_aware_enhancer=context_aware_enhancer)

results = analyzer.analyze(text="The postal code for Beijing is 100000.", language="en", return_decision_process=True)

decision_process = results.analysis_explanation

pp = pprint.PrettyPrinter()
print("Decision process output:\n")
pp.pprint(decision_process.__dict__) 执行结果如下:
Decision process output:

{'original_score': 0.02,
'pattern': '\\d{6}',
'pattern_name': 'postal code',
'recognizer': 'PatternRecognizer',
'regex_flags': regex.I|regex.M|regex.S,
'score': 0.47000000000000003,
'score_context_improvement': 0.45,
'supportive_context_word': 'postal',
'textual_explanation': 'Detected by `PatternRecognizer` using pattern `postal '
                        'code`',
'validation_result': None} 2.8 YAML设置文件

Presidio支持YAML设置文件解析,YAML设置文件使得即使不懂编码也可以向分析器中添加指定的辨认器。用户可将正则表达式和敏感信息列表添加到一个YAML格式的文件中,且此文件在Presidio中指定加载。
下面示例为只加载指定的YAML文件中包罗的辨认器:
from presidio_analyzer import AnalyzerEngine, RecognizerRegistry

yaml_file = "recognizers.yaml"
registry = RecognizerRegistry()
registry.add_recognizers_from_yaml(yaml_file)

analyzer = AnalyzerEngine(registry=registry)
analyzer.analyze(text="Mr. and Mrs. Smith", language="en") 下面示例为除了加载YAML文件指定的辨认器外,还加载内置的辨认器:
from presidio_analyzer import AnalyzerEngine, RecognizerRegistry

yaml_file = "recognizers.yaml"
registry = RecognizerRegistry()
registry.load_predefined_recognizers()
# 加载Presidio内置的识别器。
registry.add_recognizers_from_yaml(yaml_file)

analyzer = AnalyzerEngine(registry=registry)
analyzer.analyze(text="Mr. Plum wrote a book", language="en") 3 敏感数据匿名器

一旦我们辨认到内容中包罗敏感数据实体,应该利用匿名器对敏感数据举行脱敏处置惩罚。匿名器既支持默认的脱敏处置惩罚,还支持通过加载设置的形式指定特定敏感数据类型的脱敏方法。匿名器支持重编辑、哈希、掩码、替换和加密等脱敏手段。
3.1 简单示例

下面是匿名器根据分析器的扫描结果执行脱敏操纵的示例:
from presidio_anonymizer import AnonymizerEngine
from presidio_anonymizer.entities import RecognizerResult

# 创建分析器的执行结果。
analyzer_results = [
    RecognizerResult(entity_type="PERSON", start=11, end=15, score=0.8),
    RecognizerResult(entity_type="PERSON", start=17, end=27, score=0.8),
]

# 创建匿名器对象。
engine = AnonymizerEngine()

# 执行脱敏操作。text参数为分析器扫描的文本内容。analyzer_results参数为分析器
# 扫描结果。当前匿名其执行默认的匿名操作。
result = engine.anonymize(
    text="My name is Bond, James Bond", analyzer_results=analyzer_results
)

print("De-identified text")
print(result.text) 上面示例执行结果如下:
   De-identified text
My name is <PERSON>, <PERSON>
上面示例我们可以看到,"Bond"和"James Bond"都被姓名辨认器辨认出来,并由匿名器执行脱敏操纵。匿名器默认的脱敏操纵是将敏感实体替换为对应的辨认器名称。
3.2 自界说匿名设置

用户可以通过OperatorConfig类创建匿名设置来指定匿名器对于不同的敏感数据类型执行不同的脱敏操纵。
示例如下:
operators = {
    # 指定匿名器默认情况下将敏感数据实体替换成<ANONYMIZED>。
    "DEFAULT": OperatorConfig("replace", {"new_value": "<ANONYMIZED>"}),
    # 指定敏感数据类型为PHONE_NUMBER,则将内容替换成"*"。
    "PHONE_NUMBER": OperatorConfig(
      "mask",
      {
            "type": "mask",
            "masking_char": "*",
            "chars_to_mask": 12,
            "from_end": True,
      },
    ),
    # 指定敏感信息列表中内容重新编译为空。
    "SENSITIVE": OperatorConfig("redact", {}),
} 下面是示例展示了利用分析器扫描,并将结果和设置传入匿名器对文本内容执行特定的脱敏操纵。
from presidio_analyzer import AnalyzerEngine, PatternRecognizer
from presidio_anonymizer.entities import OperatorConfig
from presidio_anonymizer import AnonymizerEngine

from pprint import pprint
import json

# 待扫描的文本数据
text = "His name is Mr. Jones and his phone number is 212-555-5555, he live in Beijing."

sensitive_list = ["password", "secret", "phone"]
sensitive_recognizer = PatternRecognizer(supported_entity="SENSITIVE", deny_list=sensitive_list)

# 创建分析器对象
analyzer = AnalyzerEngine()
analyzer.registry.add_recognizer(sensitive_recognizer)

# 执行分析器的分析函数,分析文本数据
analyzer_results = analyzer.analyze(text=text, language="en")

operators = {
    "DEFAULT": OperatorConfig("replace", {"new_value": "<ANONYMIZED>"}),
    "PHONE_NUMBER": OperatorConfig(
      "mask",
      {
            "type": "mask",
            "masking_char": "*",
            "chars_to_mask": 12,
            "from_end": True,
      },
    ),
    "SENSITIVE": OperatorConfig("redact", {}),
}

anonymizer = AnonymizerEngine()
anonymized_results = anonymizer.anonymize(
    text=text, analyzer_results=analyzer_results, operators=operators
)

print(f"sacn_result: {analyzer_results}")
print(f"text: {anonymized_results.text}")
print("detailed result:")
pprint(json.loads(anonymized_results.to_json())) 上面示例执行结果如下:
sacn_result:
text: His name is Mr. <ANONYMIZED> and hisnumber is ************, he live in <ANONYMIZED>.
detailed result:
{'items': [{'end': 85,
            'entity_type': 'LOCATION',
            'operator': 'replace',
            'start': 73,
            'text': '<ANONYMIZED>'},
         {'end': 60,
            'entity_type': 'PHONE_NUMBER',
            'operator': 'mask',
            'start': 48,
            'text': '************'},
         {'end': 37,
            'entity_type': 'SENSITIVE',
            'operator': 'redact',
            'start': 37,
            'text': ''},
         {'end': 28,
            'entity_type': 'PERSON',
            'operator': 'replace',
            'start': 16,
            'text': '<ANONYMIZED>'}],
'text': 'His name is Mr. <ANONYMIZED> and hisnumber is ************, he '
         'live in <ANONYMIZED>.'}
通过执行结果我们可以看到,"phone"为敏感信息列表中内容,匿名器根据设置直接文本中该内容替换成空。"Jones"和"Beijing"分别被PERSON辨认器和LOCATION辨认器辨认出来,由于设置中没有针对此两种类型敏感数据指定的脱敏操纵,则执行默认的脱敏操纵,即将敏感数据内容替换成""。"212-555-5555"被辨认处电话号码,由于设置中规则对于PHONE_NUMBER辨认器辨认的内容将替换成字符"*"。因此文本内容脱敏前后对好比下:
   # 脱敏前 His name is Mr. Jones and his phone number is 212-555-5555, he live in Beijing.
# 脱敏后 His name is Mr. <ANONYMIZED> and his number is ************, he live in <ANONYMIZED>.
3.3 自界说匿名器

Presidio支持用户自界说匿名器对敏感数据实体举行脱敏操纵。自界说匿名器以lambda函数形式传入。
下面示例展示怎样创建自界说匿名器:
from faker import Faker
from presidio_anonymizer.entities import OperatorConfig

fake = Faker()

# 创建脱敏函数,使用fake对象实现脱敏操作。
# 注意,该函数需要接收一个参数值。
def fake_name(x):
    return fake.name()

# 通过OperatorConfig类型创建匿名器配置对象。
operators = {"PERSON": OperatorConfig("custom", {"lambda": fake_name})} 下面展示利用自界说匿名器执行脱敏操纵的完备实现:
from presidio_anonymizer import AnonymizerEngine
from presidio_anonymizer.entities import OperatorConfig, EngineResult, RecognizerResult
from faker import Faker

fake = Faker()

# 创建脱敏函数。
def fake_name(x):
    return fake.name()

# 创建匿名器自定义配置对象。
operators = {"PERSON": OperatorConfig("custom", {"lambda": fake_name})}

# 分析器对文本内容分析结果。
analyzer_results =

text_to_anonymize = "My name is Raphael and I like to fish."

anonymizer = AnonymizerEngine()
anonymized_results = anonymizer.anonymize(
    text=text_to_anonymize, analyzer_results=analyzer_results, operators=operators
)

print(anonymized_results.text) 执行结果如下:
   My name is Elaine Austin and I like to fish.
通过执行结果我们可以看到,"Raphael"已经被随机替换成一个化名"Elaine Austin",fake.name()函数每次生成的化名是不一样的。
3.4 加密和解密

Presidio匿名器内置对敏感数据加密和解密实现。加密模块利用AES的CBC模式加密算法,在加密和解密时都必要输入一段字符串作为key。
对敏感数据举行加解密是可逆的操纵,即保障了敏感信息的安全,又能由接收着根据秘钥解密成原始的数据内容。
下面示例展示了怎样设置匿名器使其对指定类型敏感内容实体执行加密操纵:
from presidio_anonymizer import AnonymizerEngine, DeanonymizeEngine
from presidio_anonymizer.entities import (
    RecognizerResult,
    OperatorResult,
    OperatorConfig,
)

# 设置加密Key。
crypto_key = "WmZq4t7w!z%C&F)J"

engine = AnonymizerEngine()

# 执行脱敏操作。
anonymize_result = engine.anonymize(
    text="My name is James Bond",
    analyzer_results=[
      RecognizerResult(entity_type="PERSON", start=11, end=21, score=0.8),
    ],
    operators={"PERSON": OperatorConfig("encrypt", {"key": crypto_key})},
)

# 打印脱敏后的文本内容
print(anonymize_result.text)

# 打印匿名实体的详细内容
print(anonymize_result.items) 上面示例执行结果如下:
   My name is 1QZPhRiMCG52oUG99z+qsPFQcpFSwE50fLj7KtbbAFA=
[{'start': 11, 'end': 55, 'entity_type': 'PERSON', 'text': '1QZPhRiMCG52oUG99z+qsPFQcpFSwE50fLj7KtbbAFA=', 'operator': 'encrypt'}]
通过分析脱敏结果可以看到,匿名器将"James Bond"加密成"1QZPhRiMCG52oUG99z+qsPFQcpFSwE50fLj7KtbbAFA="。
下面示例展示了怎样对含有加密内容中文本数据举行解密操纵。
from presidio_anonymizer import AnonymizerEngine, DeanonymizeEngine
from presidio_anonymizer.entities import (
    RecognizerResult,
    OperatorResult,
    OperatorConfig,
)
from presidio_anonymizer.operators import Decrypt

crypto_key = "WmZq4t7w!z%C&F)J"

anonymizer_engine = AnonymizerEngine()
anonymize_result = anonymizer_engine.anonymize(
    text="My name is James Bond",
    analyzer_results=[
      RecognizerResult(entity_type="PERSON", start=11, end=21, score=0.8),
    ],
    operators={"PERSON": OperatorConfig("encrypt", {"key": crypto_key})},
)
print("加密后文本内容: ", anonymize_result.text)

# 创建解匿名引擎。
deanonymize_engine = DeanonymizeEngine()
deanonymized_result = deanonymize_engine.deanonymize(
    text=anonymize_result.text,
    entities=anonymize_result.items,
    operators={"DEFAULT": OperatorConfig("decrypt", {"key": crypto_key})},
)

# 解匿名引擎从加密文本中定位到加密内容。
print("提取到加密内容: ", anonymize_result.items.text)
# 执行解密操作。
print("解密后内容: ", Decrypt().operate(text=anonymize_result.items.text, params={"key": crypto_key})) 上面示例执行结果为:
   加密后文本内容: My name is O+LlBEHWW8ilbUiYFMZU7XXbrOkFVpj7KVJD86sOywA=
提取到加密内容: O+LlBEHWW8ilbUiYFMZU7XXbrOkFVpj7KVJD86sOywA=
解密后内容: James Bond
留意:通过上面示例可以看到,Presidio内容的加密算法每次加密相同内容,加密后的结果是不一样的。可以多执行频频就可以看出来。
4 NLP模型和语言类型

Presidio内部支持的NLP引擎包括spaCy和Stanza两种。因此使得Presidio能够正常运行的前提是至少已经下载安装两种NLP引擎中的一个模型。
下面示例通过设置,将spaCy作为Presidio的NLP引擎,而且使得NLP引擎既支持英语,也支持西班牙语。
from presidio_analyzer import AnalyzerEngine
from presidio_analyzer.nlp_engine import NlpEngineProvider

# 创建NLP配置对象,包含指定的NLP引擎以及支持的NLP模型。
configuration = {
    "nlp_engine_name": "spacy",
    "models": [
      {"lang_code": "es", "model_name": "es_core_news_md"},
      {"lang_code": "en", "model_name": "en_core_web_lg"},
    ],
}

# 基于NLP配置信息创建NLP对象。
provider = NlpEngineProvider(nlp_configuration=configuration)
# 创建NLP引擎对象。
nlp_engine_with_spanish = provider.create_engine()

# 创建分析器对象,NLP引擎对象作为参数传入,除此之外,还要传入NLP引擎支持的语言。
analyzer = AnalyzerEngine(
    nlp_engine=nlp_engine_with_spanish, supported_languages=["en", "es"]
)

# 分析西班牙语文本内容。
results_spanish = analyzer.analyze(text="Mi nombre es Morris", language="es")
print("Results from Spanish request:")
print(results_spanish)

# 分析英语文本内容。
results_english = analyzer.analyze(text="My name is Morris", language="en")
print("Results from English request:")
print(results_english) 下面是上面示例执行结果:
   Results from Spanish request:

Results from English request:

示例中spaCy支持西班牙语文本的解析,在这之前必要先下载西班牙语NLP模型,即es_core_news_md。spaCy语言模型下载方法可参考这篇文章spaCy语言模型下载。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【数据掩护】微软开源数据掩护项目Presidio-从入门到精通