速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

nginScript 系列:使用 nginScript 掩蔽用户隐私数据

  • 2017-06-27
  • 本文字数:4275 字

    阅读完需:约 14 分钟

这是 nginScript 系列文章的第四篇,将介绍如何使用 nginScript 掩蔽用户隐私数据。查看第一篇“ nginScript 简介”,第二篇“使用nginScript 将客户端重定向到新服务器”,查看第三篇“通过TCP 负载均衡和Galera 集群来扩展MySQL ”。

2016 年 10 月,欧洲联盟法院规定将IP 地址归为“个人信息”,于是IP 地址进入了数据保护指令一般性数据保护条例的保护范围。对于很多网站来说,这意味着如果数据离开了欧盟管辖的地区,那么对这些数据进行归档和分析就面临着法规上的挑战。对于进入美国的数据,西欧隐私盾协议提供了一定程度的保护,但仍然面临私权组织政府的法律质疑。

不过,保护日志里的隐私数据并不是EU 独有的问题。对于像 IOS/ICE 27001 这样的安全认证组织,将数据移出安全区域也会破坏认证的合规性。

在这篇文章里,我们介绍一些简单的方案用于处理 NGINX Plus 和 NGINX 的日志文件,这样它们就可以安全地导出到外部,不会暴露所谓的个人识别信息(Personal Identification Information)。

无效的方式

保护数据最简单的办法是在导出数据之前把 IP 地址信息剔除掉。通过标准的 Linux 命令行工具就能做到这点,不过日志分析系统希望日志文件具有标准的格式,缺少 IP 字段会导致无法导入日志。即使日志成功导入,如果分析系统依赖 IP 地址来追踪用户行为,那么分析结果的准确性就会大打折扣。

另外一种可能的办法是,使用随机的假 IP 地址替换原来的值,这样就可以保持完整的日志文件,但是这样仍然会影响日志分析结果的质量,因为 IP 地址是假的。

掩蔽客户端 IP 地址

最好的解决方案是使用一种叫作数据掩蔽的技术对真实的 IP 地址进行转换,既不会暴露用户身份信息,又能够用于分析用户网站活动行为。数据掩蔽算法为给定值生成不可逆的伪随机值,同一个 IP 地址总是能被转换成同样的伪随机值。

你可以在 NGINX 和 NGINX Plus 中使用 nginScript 模块来实现 IP 地址掩蔽。nginScript 是为 NGINX 和 NGINX Plus 专门实现的 JavaScript,专门为服务器端的使用场景而设计。在记录请求消息时,我们通过执行一小段 JavaScript 代码来掩蔽客户端的 IP 地址。

为掩蔽 IP 地址配置 NGINX 和 NGINX Plus

log_format 指令指定了哪些信息会显示在访问日志里。NGINX 和 NGINX Plus 提供了默认的日志格式,叫作combined,用它生成的日志可以被大多数日志处理工具处理。

我们创建一个新的日志格式masked,除了第一个字段与combined不一样,其他的都一样,我们使用 $remote_addr_masked 代替 $remote_addr。我们的 JavaScript 代码会计算这个变量,为每一个客户端 IP 地址生成一个掩蔽值。

复制代码
log_format masked '$remote_addr_masked - $remote_user [$time_local]'
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';

为了让 NGINX 和 NGINX Plus 以这种格式生成访问日志,我们在 server 配置块里使用 access_log 指令指定了masked格式。

复制代码
js_include mask_ip_uri.js;
js_set $remote_addr_masked maskRemoteAddress;
server {
listen 80;
access_log /var/log/nginx/access.log;
access_log /var/log/nginx/access_masked.log masked;
location / {
return 200 "$remote_addr -> $remote_addr_masked\n"; #只用于测试
}
}

因为我们打算使用 nginScript 掩蔽客户端 IP 地址,所以我们使用 js_include 指令指定 JavaScript 代码的位置。js_set 指令指定了一个 JavaScript 函数,这个函数会在计算 $remote_addr_masked 变量时被调用。

我们使用了两个 access_log 指令。第一个使用了默认的日志格式,它生成的访问日志是给管理员使用的,用于日常的运维。第二个指定了masked格式。于是,我们为每个请求生成两个访问日志——一个用于系统管理和运维,一个用于导出。

最后,location 配置块使用 return 指令定义了一个非常简单的返回结果,表示数据掩蔽功能是有效的。在生产环境,这里一般会包含一个 proxy_pass 指令,将请求重定向到后端的服务器上。

用于 IP 地址掩蔽的 nginScript 代码

我们使用了三个简单的 JavaScript 函数来生成掩蔽过的 IP 地址。被依赖的函数要出现在前面,所以我们按照它们的先后顺序逐个说明。

数据掩蔽的本质在于使用一种单向的散列算法来转换客户端 IP 地址。在我们的例子里,我们使用 FNV-1a 散列算法,这种算法紧凑、快速,而且具有很好的分布特征。它返回32 位的整数(与IPv4 地址一样的大小),非常适合用于表示IP 地址。fnv32a 函数实现了FNV-1a 算法。

i2ipv4 函数将 32 位整数转换成 IPv4 格式。它接收由 fnv32a() 生成的散列值,并将其转换成适当的格式。IPv4 地址和 IPv6 地址都用 IP4v 的格式来表示。

最后,我们使用 js_set 指令指定了 maskRemoteAddress() 函数,它接受一个单独的参数 req,req 表示 HTTP 请求对象。req 的 remoteAddress 属性包含了客户端 IP 地址。

复制代码
function fnv32a(str) {
var hval = 2166136261;
for (var i = 0; i < str.length; ++i ) {
hval ^= str.charCodeAt(i);
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
}
return hval >>> 0;
}
function i2ipv4(i) {
var octet1 = (i >> 24) & 255;
var octet2 = (i >> 16) & 255;
var octet3 = (i >> 8) & 255;
var octet4 = i & 255;
return octet1 + "." + octet2 + "." + octet3 + "." + octet4;
}
function maskRemoteAddress(req) {
return i2ipv4(fnv32a(req.remoteAddress));
}

IP 地址掩蔽

做好配置之后,我们向服务器发出一个简单的请求,然后检查返回的响应并查看访问日志。

复制代码
$ curl http://localhost/
127.0.0.1 -> 8.163.209.30
$ sudo tail --lines=1 /var/log/nginx/access*.log
==> /var/log/nginx/access.log <==
127.0.0.1 - - [16/Mar/2017:19:08:19 +0000] "GET / HTTP/1.1" 200 26 "-" "curl/7.47.0"
==> /var/log/nginx/access_masked.log <==
8.163.209.30 - - [16/Mar/2017:19:08:19 +0000] "GET / HTTP/1.1" 200 26 "-" "curl/7.47.0"

掩蔽查询字符串里的敏感数据

日志文件里也可能包含除 IP 地址之外的敏感数据。比如邮件地址、邮政编码,或者其他通过查询字符串传递过来的信息。如果你的应用是通过这种方式传递数据的,那么你可以通过扩展之前的 IP 地址掩蔽方案来处理查询字符串里的敏感数据。

为掩蔽查询字符串配置 NGINX 和 NGINX Plus

与默认的combined格式类似,之前定义的masked日志格式记录了 $request 变量,通过这个变量可以捕捉到三个 request 组件:HTTP 方法、URI(包含了查询字符串)和 HTTP 版本。我们只需要掩蔽查询字符串,但是为了代码的可读性,我们分别使用了三个独立的变量来表示这三个组件:$request_uri_masked 变量表示请求 URI,$request_method 表示 HTTP 方法,$server_protocol 表示 HTTP 版本。

复制代码
log_format masked '$remote_addr_masked - $remote_user [$time_local]'
'"$request_method $request_uri_masked $server_protocol" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent"';

server 配置块需要另一个 js_set 指令,用于定义如何计算 $request_url_masked 变量。

复制代码
js_include mask_ip_uri.js;
js_set $request_uri_masked maskRequestURI;
server {
listen 80;
access_log /var/log/nginx/access.log;
access_log /var/log/nginx/access_masked.log masked;
location / {
return 200 "$remote_addr -> $remote_addr_masked\n"; # 只用于测试
}
}

掩蔽查询字符串的 nginScript 代码

我们在 logmask.js 文件里增加了 maskRequestURI() 函数。与 maskRemoteAddress() 函数类似,maskRequestURI() 也需要调用 fnv32a() 散列函数,所以它出现在 fnv32a() 函数的下面。

复制代码
function maskRequestURI(req) {
var query_string = req.variables.query_string;
if (query_string.length) { // 如果有查询字符串就继续
var kvpairs = query_string.split('&'); // 转换成键值数组
for (var i = 0; i < kvpairs.length; i++) { // 遍历键值
var kvpair = kvpairs[i].split('='); // 将键值对拆分成一个数组
if (kvpair[0] == "zip") { // 掩蔽邮政编码
// 使用前 5 个数字作为掩蔽值
kvpairs[i] = kvpair[0] + "=" + fnv32a(kvpair[1]).toString().substr(5);
} else if (kvpair[0] == "email") { // Mask email
// 使用散列值作为前缀
kvpairs[i] = kvpair[0] + "=" + fnv32a(kvpair[1]) + "@example.com";
}
}
return req.uri + "?" + kvpairs.join('&'); // 构建掩蔽过的 URI
}
return req.uri; // 没有查询字符串,返回 URI
}

maskRequestURI() 函数遍历查询字符串里的每一个键值对,找出包含敏感数据的键,并掩蔽这些键所对应的值。

根据使用日志的不同情况,掩蔽过的值需要能够反映真实的数据。在上面的例子里,我们将邮政编码掩蔽成 5 个数字,邮件地址被掩蔽成符合 RFC 821 的格式。其他键可能要求更复杂的格式,需要使用特定的函数来生成。

查询字符串掩蔽

经过这些额外的配置,我们就可以看到掩蔽过的查询字符串。

复制代码
$ curl "http://localhost/index.php?foo=bar&zip=90210&email=liam@nginx.com"
127.0.0.1 -> 8.163.209.30
$ sudo tail --lines=1 /var/log/nginx/access*.log
==> /var/log/nginx/access.log <==
127.0.0.1 - - [16/Mar/2017:20:05:55 +0000] "GET /index.php?foo=bar&zip=90210&email=liam@nginx.com HTTP/1.1" 200 26 "-" "curl/7.47.0"
==> /var/log/nginx/access_masked.log <==
8.163.209.30 - - [16/Mar/2017:20:05:55 +0000] "GET /index.php?foo=bar&zip=38643&email=2852675791@example.com HTTP/1.1" 200 26 "-" "curl/7.47.0"

总结

NGINX 和 NGINX Plus 为自定义处理请求提供了一种简单而强大的方案。在这篇文章里,我们演示了如何使用 nginScript 来掩蔽敏感数据,在不违反数据安全合规的情况下,让日志文件可以被导出并用于离线分析。

查看英文原文: Data Masking for User Privacy with nginScript

2017-06-27 17:321544

评论

发布
暂无评论
发现更多内容

ARTS 打卡 第二周,按部就班

三掌柜

ARTS 打卡计划

无代码编程时代的到来:新兴工具和平台的前瞻展望

互联网工科生

低代码 数据可视化 JNPF

你知道Golang的模板怎么用吗?带你了解动态文本的生成!

王中阳Go

TuGraph Analytics 流图计算之行为路径归因

TuGraphAnalytics

实时计算 图计算 归因分析 行为分析

分布式锁的三种实现方式!

树上有只程序猿

乐观锁 悲观锁 分布式锁

百度智能云与蓝色光标共绘AI营销新篇章:袁佛玉亮相Blue AI行业模型发布会,千帆平台引领行业模型创新之路

Geek_2d6073

打败传统Scada系统的Web Scada是什么?

2D3D前端可视化开发

物联网 组态软件 工业控制 web scada scada系统

Docker和Kubernetes:各自的优势和适用场景

树上有只程序猿

Docker Kubernetes

自然语言处理的卓越未来

百度开发者中心

#人工智能 ChatGPT 文心一言 千帆大模型平台

一次性全讲透GaussDB(DWS)锁的问题

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号9月PK榜

矩视云平台SDK可以支持本地检测吗

矩视智能

机器视觉 深度学习、

苹果再发“黑科技”,合合信息扫描全能王新功能支持“360度立体式建模”

合合技术团队

苹果 科技 合合信息 #人工智能 扫描全能王

【案例教学】华为云API对话机器人的魅力—体验AI垃圾分类机器人

华为云PaaS服务小智

云计算 软件开发 华为云

AITO问界M9工信部申报信息曝光,或将是理想L9的最大对手?

Geek_2d6073

「DAPP」双币拆分理财项目系统开发 拆分盘系统开发

V\TG【ch3nguang】

ARTS 打卡 第三周,渐入佳境

三掌柜

ARTS 打卡计划

AutoCAD 2023 for Mac(cad2023) v2023.2.1注册激活版

mac

苹果mac Windows软件 AutoCAD 2023 三维绘图软件

区块链Dapp系统开发定制

V\TG【ch3nguang】

区块链搭建

2023-09-13:用go语言,给定一个整数数组 nums 和一个正整数 k, 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。 输入: nums = [4, 3, 2, 3, 5,

福大大架构师每日一题

福大大架构师每日一题

凝创新技术,汇数字力量 欧特克数字赋能『智』造汽车高峰论坛在沪开幕

E科讯

解锁 Postman 接口测试:完整指南

Liam

Java 程序员 Postman 接口测试 测试工具

去中心化区块链DAPP的优势及解决方案,DAPP系统开发

V\TG【ch3nguang】

拒做职场小白,如何入职就成为成熟工程师

小魏写代码

求职面试 就业辅导

科兴未来 | 2023年河北国际先进技术创新挑战赛

科兴未来News

马斯克回应盖茨;谷歌反垄断案开庭;苹果发布 3nm 芯片的 iPhone 15丨RTE开发者日报 Vol.48

声网

区块链dapp开发团队,快速搭建区块链dapp系统

V\TG【ch3nguang】

DAPP系统开发

推动长期成功:NFT 推广机构如何制定可持续战略

区块链软件开发推广运营

数字藏品开发 dapp开发 区块链开发 链游开发 NFT开发

nginScript系列:使用nginScript掩蔽用户隐私数据_大数据_Nginx_InfoQ精选文章