什么是数据丢失预防(DLP)?
- 使用中的数据:通过认证用户和控制对敏感数据的访问,在积极处理端点或应用程序中的数据时保护其安全。 
- 运动中的数据。通过加密信息或使用电子邮件和信息传递安全工具,确保机密数据在网络上传输时得到保护。 
- 静止的数据。通过访问控制、加密和数据保留政策,保护存储在云、数据库或其他存储介质中的数据,如备份磁带和终端设备。 
数据丢失预防是如何工作的?
https://juejin.cn/post/7071106601939271687
一、简介
为了保障企业的数据安全和隐私安全,godlp 提供了一系列针对敏感数据的识别和处置方案, 其中包括敏感数据识别算法,数据脱敏处理方式,业务自定义的配置选项和海量数据处理能力。godlp 能够应用多种隐私合规标准,对原始数据进行分级打标、判断敏感级别和实施相应的脱敏处理。
二、关键能力
godlp 能够广泛支持结构化(JSON数据、KV数据、golang map)和非结构化数据(多语言字符串)
1. 敏感数据自动发现
DLP 内置多种敏感数据识别规则,能对原始数据进行敏感类型识别,确保敏感信息能被妥善处理。
2. 敏感数据脱敏处理
3. 业务自定义配置选项
除默认的敏感信息识别和处理规则外,业务可以根据实际情况,配置自定义的YAML规则,DLP 能够根据传入的配置选项,完成相应的数据处理任务。
三、接入方式
go get github.com/bytedance/godlp示例代码在 mainrun/mainrun.go 文件中
在godlp代码根目录下输入以下命令可以进行编译和运行
makemake runmake testmake bench
API 描述
dlpheader定义了 godlp SDK需要的数据结构,常量定义等。godlp SDK主要提供了以下API进行敏感信息识别和脱敏。
- ApplyConfig(conf string) error 
- ApplyConfig by configuration content 
- 传入conf string 进行配置 
- ApplyConfigFile(filePath string) error 
- ApplyConfigFile by config file path 
- 传入filePath 进行配置 
- Detect(inputText string) ([]*DetectResult, error) 
- Detect string 
- 对string进行敏感信息识别 
- DetectMap(inputMap map[string]string) ([]*DetectResult, error) 
- DetectMap detects KV map 
- 对map[string]string进行敏感信息识别 
- DetectJSON(jsonText string) ([]*DetectResult, error) 
- DetectJSON detects json string 
- 对json string 进行敏感信息识别 
- Deidentify(inputText string) (string, []*DetectResult, error) 
- Deidentify detects string firstly, then return masked string and results 
- 对string先识别,然后按规则进行打码 
- DeidentifyMap(inputMap map[string]string) (map[string]string, []*DetectResult, error) 
- DeidentifyMap detects KV map firstly,then return masked map 
- 对map[string]string先识别,然后按规则进行打码 
- ShowResults(resultArray []*DetectResult) 
- ShowResults print results in console 
- 打印识别结果 
- Mask(inputText string, methodName string) (string, error) 
- Mask inputText following predefined method of MaskRules in config 
- 根据脱敏规则直接脱敏 
- Close() 
- Close engine object, release memory of inner object 
- 关闭,释放内部变量 
- GetVersion() string 
- Get Dlp SDK version string 
- 获取版本号 
- RegisterMasker(maskName string, maskFunc func(string) (string, error)) error 
- Register DIY Masker 
- 注册自定义打码函数 
- NewLogProcesser() logs.Processor 
- NewLogProcesser create a log processer for the package logs 
- 日志脱敏处理函数 
- MaskStruct(inObj interface{}) (interface{}, error) 
- MaskStruct will mask a strcut object by tag mask info 
- 根据tag mask里定义的脱敏规则对struct object直接脱敏 
传送门:https://github.com/bytedance/godlp
实际场景使用案例分享:
1、通过端口代理拦截redis返回内容用godlp提供的敏感识别规则进行敏感数据脱敏
直接上代码
package mainimport ("fmt""net""strings""sync"dlp "github.com/bytedance/godlp")type ConnInfo struct {//数据库类型:预留字段dbType string//会话的pid:预留字段sessionId string//发送的连接sendConn net.Conn//接收数据的连接receConn net.Conn//当前信息状态:预留字段status int}var (wg sync.WaitGroupsplit stringsplitB = []byte{13, 10})func start() {wg.Add(1)split = string(splitB)//与客户端的连接,其实是客户端需要连接的服务端cliAdd, _ := net.ResolveTCPAddr("tcp4", "172.16.x.x:6666")cliListen, _ := net.ListenTCP("tcp4", cliAdd)fmt.Println("代理工具启动监听端口:", cliAdd)//无限循环,接收到来自于客户端的连接的时候船舰一个函数去处理,暂时用chan通道去处理chConn := make(chan ConnInfo)go func() {fmt.Println("准备开始监听通道")dealChannel(chConn)}()for {conn, _ := cliListen.Accept()fmt.Println("接收到新请求")//这里接收到一个客户端请求则直接创建一个与服务端相连接的请求serAdd, _ := net.ResolveTCPAddr("tcp4", "172.16.x.x:6379")serConn, _ := net.DialTCP("tcp4", nil, serAdd)//封装服务端的连接和客户端的连接对象建立关系serConnInfo := new(ConnInfo)serConnInfo.sessionId = "readcli"serConnInfo.receConn = connserConnInfo.sendConn = serConncliConnInfo := new(ConnInfo)cliConnInfo.sessionId = "readser"cliConnInfo.receConn = serConncliConnInfo.sendConn = connchConn <- *serConnInfochConn <- *cliConnInfo}}func dealChannel(chConn chan ConnInfo) {//这里无限循环一直从通道中去获取连接信息,目前这种处理方式可能并发性能差点,待研究深入一点再考虑更好的方案for {conInfo := <-chConn//当从通道读取到连接信息的时候创建一个协程去读取数据,这样会一直创建协程,因为go语言的协程本来久很轻量级,不确定这么处理是否有问题,待后续深入了解了再改进go func() {//获取到通道的信息data := make([]byte, 1024)dlen, err := conInfo.receConn.Read(data)if err != nil {//如果出错大概率是因为某个通道被断开了,会话断开直接连接,通道中丢掉这个连接信息fmt.Print("出现错误")conInfo.receConn.Close()conInfo.sendConn.Close()} else {//这里直接取了所有数据,如果返回的数据量大可能会分包,暂时不处理后续再想办法。content := strings.Split(string(data[:dlen]), split)for i, value := range content {if strings.HasPrefix(value, "*") || strings.HasPrefix(value, "$") {conInfo.sendConn.Write([]byte(value))} else {conInfo.sendConn.Write([]byte(maskString(value)))}if i < len(content)-1 {conInfo.sendConn.Write(splitB)}}//读取结束再把信息加入通道,等待下次循环再次读取chConn <- conInfo}}()}}//这里直接把数据交给godlp处理,至于能识别到什么内容需要看godlp只是什么,当然以前的项目是自定义的也没问题func maskString(inStr string) (outStr string) {caller := "replace.your.caller"if eng, err := dlp.NewEngine(caller); err == nil {eng.ApplyConfigDefault()if outStr, _, err := eng.Deidentify(inStr); err == nil {//fmt.Println(inStr, "--------->", outStr)return outStr}eng.Close()} else {fmt.Println("[dlp] NewEngine error: ", err.Error())}return inStr}//启动的主方法func main() {start()}
效果图:
2、基于godlp的实际应用,对mongodb的返回报文解析并对内容进行识别脱敏
技术核心
1.做关于tcp端口的端口双向代理,拦截到所有数据并且转发(相对来说比较简单)。
2.对拦截到的报文进行解析,提取出他的返回的数据(技术核心)。
3.对数据进行识别脱敏处理(这里java语言写的写死算法,go语言写的直接调用godlp的内置识别规则和脱敏算法)。
4.对脱敏后的数据封装成正确的报文格式返回给客户端解析(技术核心)。
5.对于返回的大量数据可能会存在分包转发的情况,需要正确识别并处理(这里目前只做了数据合包处理后当成一个数据包解析的,牺牲了性能来简化代码,后续再考虑优化)。
代码:
package mainimport ("bytes""fmt""net""sync"dlp "github.com/bytedance/godlp")type ConnInfo struct {//数据库类型:预留字段dbType string//会话的pid:预留字段sessionId string//连接的typeconType string//发送的连接sendConn net.Conn//接收数据的连接receConn net.Conn//当前信息状态status int//当前数据是否接收完整,默认是false//接收的bufferrebuffer bytes.Buffer//发送的buffer,预留属性,后续可能会用到sebuffer bytes.Buffer}var (wg sync.WaitGroup)func main() {start()}/***这里只写了点对点代理,完全可以多加循环做成用不同端口代理多个服务器*/func start() {wg.Add(1)//与客户端的连接,其实是客户端需要连接的服务端cliAdd, _ := net.ResolveTCPAddr("tcp4", "172.x.x.x:7003")cliListen, _ := net.ListenTCP("tcp4", cliAdd)fmt.Println("代理工具启动监听端口:", cliAdd)//无限循环,接收到来自于客户端的连接的时候创建一个函数去处理,暂时用chan通道去处理chConn := make(chan ConnInfo)go func() {fmt.Println("准备开始监听通道")dealChannel(chConn)}()for {conn, _ := cliListen.Accept()fmt.Println("接收到新请求")//这里接收到一个客户端请求则直接创建一个与服务端相连接的请求serAdd, _ := net.ResolveTCPAddr("tcp4", "172.x.x.x:27017")serConn, _ := net.DialTCP("tcp4", nil, serAdd)//封装服务端的连接和客户端的连接对象建立关系serConnInfo := new(ConnInfo)serConnInfo.conType = "mogcli"serConnInfo.receConn = connserConnInfo.status = 0serConnInfo.sendConn = serConncliConnInfo := new(ConnInfo)cliConnInfo.conType = "mogser"cliConnInfo.receConn = serConncliConnInfo.sendConn = conncliConnInfo.status = 0chConn <- *serConnInfochConn <- *cliConnInfo}}/**处理channel通道的数据*/func dealChannel(chConn chan ConnInfo) {//这里无限循环一直从通道中去获取连接信息,目前这种处理方式可能并发性能差点,待研究深入一点再考虑更好的方案for {conInfo := <-chConn//当从通道读取到连接信息的时候创建一个协程去读取数据,这样会一直创建协程,因为go语言的协程本来久很轻量级,不确定这么处理是否有问题,待后续深入了解了再改进go func() {dealConnInfo(conInfo, chConn)}()}}//对接收到的信息再进行分析处理func dealConnInfo(conInfo ConnInfo, chConn chan ConnInfo) {//获取到通道的信息datas := make([]byte, 1024)dlen, err := conInfo.receConn.Read(datas)if err != nil {//如果出错大概率是因为某个通道被断开了,会话断开直接连接,通道中丢掉这个连接信息fmt.Print("出现错误")conInfo.receConn.Close()conInfo.sendConn.Close()} else if conInfo.conType == "mogser" {//1.判断当前的数据包是否完整,如果数据包接收不完整就存入通道的连接对象中去,等数据接收完毕再进行解析处理后统一转发给客户端。//这里这种方式存在风险,当返回的数据量足够多的话这样一直把所有数据存入到buffer中去肯定会出现问题,本来处理大量的内容就比较慢了,还要把这些内容存储在缓存里肯定是对机器资源耗费的很多的,//这里需要自己分包去解析处理的,这里为了方便就暂时先这么玩了,也留一点思考的空间给大家自己扩展//说明当前数据包中没有数据,需要等待if conInfo.rebuffer.Len() == 0 {//获取整体数据包的长度allPkLen := getBytesLen(datas, 0)if allPkLen == int64(dlen) {result := dealDatas(datas[:dlen])conInfo.sendConn.Write(result)conInfo.rebuffer.Reset()} else {conInfo.rebuffer.Write(datas[:dlen])}} else {buffer := conInfo.rebufferbuffer.Write(datas[:dlen])reDatas := buffer.Bytes()allPkLen := getBytesLen(reDatas, 0)if allPkLen == int64(buffer.Len()) {result := dealDatas(reDatas)conInfo.sendConn.Write(result)conInfo.rebuffer.Reset()} else {conInfo.rebuffer.Write(datas[:dlen])}}chConn <- conInfo} else {//这里直接取了所有数据,如果返回的数据量大可能会分包,暂时不处理后续再想办法。conInfo.sendConn.Write(datas[:dlen])//读取结束再把信息加入通道,等待下次循环再次读取chConn <- conInfo}}/***对读取到的整体数据包进行解析处理,初学阶段就写流水帐了方便理解了,后续熟悉了再考虑用其他工具来封装简化处理。*/func dealDatas(datas []byte) (result []byte) {var position int64 = 26strTag, position := readBytesStr(datas, position)//说明当前收到的报文是数据接收报文if strTag == "cursor" {//读取数据包长度allPkLen := getBytesLen(datas, 0)//如果当前数据包长度等于整体数据包长度说明数据包接收完毕,执行正确的流程if allPkLen == int64(len(datas)) {//这里直接从49位开始读取,别问为啥,多读一下源码就知道了//先读取数据包长度dataPkLen := getBytesLen(datas, 49)//说明这里面有数据,需要进行脱敏处理position = 53endposition := position + dataPkLenif dataPkLen > 4 {for true {b := datas[position]position++if b == 0 || (int64(position) >= endposition) {break}position += 23for true {tag := datas[position]position++if tag == 0 {break} else if tag == 2 {_, dlen := readBytesStr(datas, position)position = dlen//读取这个value的数据包长度vlen := getBytesLen(datas, position)position += 4//读取value的值dealValueBytesStr(datas, position, vlen)position += vlen} else {// fmt.Println("读取到其他类型数据了")}}}}}}return datas}/***根据起始位获取数据包的长度*/func getBytesLen(datas []byte, position int64) (len int64) {b0 := int64(datas[position])b1 := int64(datas[position+1]) << 8b2 := int64(datas[position+2]) << 16b3 := int64(datas[position+3]) << 24return b3 + b2 + b1 + b0}/***处理数据把value读取处理后脱敏然后再把读取后的数据写入字byte种*/func dealValueBytesStr(datas []byte, position int64, vlen int64) {value := string(datas[position : position+vlen-1])nValue := maskValue(value)nByte := []byte(nValue)for i := int64(0); i < vlen-1; i++ {if i < int64(len(nByte)) {datas[position+i] = nByte[i]} else {datas[position+i] = 32}}}//脱敏传入的字符串并且返回脱敏后的结果,这里用godlp实现,所有的识别及脱敏算法全都用godlp的开源内容,当然也可以自己写或者扩展func maskValue(inStr string) (res string) {caller := "replace.your.caller"if eng, err := dlp.NewEngine(caller); err == nil {eng.ApplyConfigDefault()if outStr, _, err := eng.Deidentify(inStr); err == nil {//fmt.Println(inStr, "--------->", outStr)return outStr}eng.Close()} else {fmt.Println("[dlp] NewEngine error: ", err.Error())}return inStr}/***从当前位置读取数据一直读取到下一位为0的时候结束*/func readBytesStr(datas []byte, position int64) (str string, dlen int64) {buffer := new(bytes.Buffer)for true {b := datas[position]position++if b == 0 {break}buffer.WriteByte(b)}str = buffer.String()return str, position}
运行效果:
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……



 
		 
		 
		 
		

还没有评论,来说两句吧...