为了深入洞察用户行为,企业通常会从Web应用中收集浏览器日志,分析用户的设备信息、浏览历史和应用内互动等关键数据,进而改善产品功能并优化用户体验。通过启用阿里云日志服务(SLS)的Web Tracking功能,企业能自动将终端用户的浏览器日志收集到SLS,既提高了数据处理效率,又减轻了业务服务器的负担。尽管便捷,但由于允许Logstore支持匿名写入,如浏览器中的配置信息泄露可能引致数据污染。为了降低该风险,推荐使用阿里云安全令牌服务(STS),为浏览器端提供时间和权限受限的访问令牌,从而增强数据上传过程的安全性。本文将以用户浏览行为日志收集为例,介绍如何利用SLS和STS为Web应用构建安全、高效的浏览器日志直传服务。
背景信息
企业A开发的这款Web应用为用户提供了一个功能丰富、界面友好的在线平台,旨在吸引用户前来浏览、购买商品或服务。为了更好地理解用户的需求、优化用户体验,并进一步提升网站的转化率,企业A认识到了对用户行为进行深入分析的重要性。这种分析需要基于用户在网站上的实际操作行为,比如点击、滑动、停留时间、搜索习惯以及购买行为等。因此,企业A的技术团队决定采集Web应用的浏览器端日志,并将这些日志上传到SLS进行更高效的数据分析和处理。
安全风险
为了减轻业务服务器的负担并提高处理效率,该Web应用被设计为允许用户的网页浏览器直接将日志数据上传到SLS,避免了日志采集与上传过程中经过业务服务器的中转。
企业A计划采取以下方案搭建浏览器日志直传服务:
然而,在以上方案中,允许Web应用在浏览器环境直接上传日志到SLS的同时,由于SLS的Logstore开启了支持匿名写入的Web Tracking功能,企业将面临以下风险:
数据污染风险:恶意用户可能会上传虚假或蓄意破坏的数据,影响数据质量和分析结果的准确性。
服务滥用风险:未受限的写入访问可能会被利用进行拒绝服务(DoS)攻击,通过大量写入请求耗尽资源。
解决方案
为了应对上述风险,企业在原有方案的基础上增加临时授权。通过这种方式,企业A能够在确保数据直传效率的同时,实现以下效果:
增强的身份验证和授权:通过STS生成的具有时间限制的令牌,即便在短时间内泄露,也极大降低了安全风险。因为这些凭证很快就会失效,降低了被不当利用的可能性。
精细化权限控制:STS允许根据最小权限原则配置权限,仅授权Web应用必需的访问权限。这种精细化的权限控制方法限制了潜在泄露的影响范围,防止了过度权限的风险。
企业A最终采取以下方案搭建浏览器日志直传服务:
方案部署
下面将以一个简单的用户浏览行为日志收集场景为例,引导您一步步使用SLS和STS为Web应用部署浏览器日志直传服务。本方案部署的示例工程:simple-web-tracking-sts.zip
一键部署
部署步骤
执行以下操作使用资源编排一键部署示例工程。
单击一键部署链接。
在页面左上角的地域,选择地域,例如华东1(杭州)。
在配置参数页签,完成日志服务SLS配置和云服务器ECS配置,然后单击下一步。
单击创建。
创建完成后,单击输出页签,然后单击WebTrackingUrl,然后在页面中输入用户名、选择商品、并点击下单,模拟真实用户在浏览器的行为。
完成及清理
方案验证
完成以上操作后,您可以通过预览方式查看数据是否已上传到SLS。
在消费预览面板,查看成功上传的日志。
清理资源
在本方案中,您创建了1台ECS实例、1个SLS Project和Logstore、1个RAM用户和1个RAM角色。测试完方案后,您可以参考以下规则处理对应产品的资源,避免继续产生费用或产生安全风险。
登录资源编排控制台。
在左侧导航栏,单击资源栈。
在资源栈列表页面,找到创建的资源栈,然后在其操作列单击删除。
手动部署
准备工作
在开始部署前,您需要先创建云资源。
创建一个SLS Project和Logstore,用于存储和分析从浏览器采集的日志。
参数
示例值
所属地域
华东1(杭州)
Project名称
web-tracking-project
Logstore名称
web-tracking-logstore
具体步骤,请参见1. 创建Project和Logstore。
创建一台ECS实例作为业务服务器,用于生成临时身份凭证。
说明在实际部署时,您可以将调用STS服务的接口集成到自己的业务服务器的接口中,而无需创建该ECS实例。
参数
示例值
付费类型
按量付费
地域
华东1(杭州)
公网 IP
分配公网 IPv4 地址
安全组
开放HTTP (TCP:80)端口
具体步骤,请参见通过控制台使用ECS实例(快捷版)。
部署步骤
执行以下操作手动部署示例工程。
步骤一:在访问控制创建RAM用户
首先,创建一个调用方式为OpenAPI调用的RAM用户,并获取对应的访问密钥,作为业务服务器的应用程序的长期身份凭证。
使用云账号或账号管理员登录RAM控制台。
在左侧导航栏,选择身份管理 > 用户。
单击创建用户。
输入登录名称和显示名称。
在调用方式区域下,选择OpenAPI调用,然后单击确定。
单击操作下的复制,保存访问密钥(AccessKey ID和AccessKey Secret)。
步骤二:在访问控制为RAM用户授予调用AssumeRole接口的权限
创建RAM用户后,需要授予RAM用户调用STS服务的AssumeRole接口的权限,使其可以通过扮演RAM角色来获取临时身份凭证。
在左侧导航栏,选择身份管理 > 用户。
在用户页面,找到目标RAM用户,然后单击RAM用户右侧的添加权限。
在新增授权页面,选择AliyunSTSAssumeRoleAccess系统策略。
说明授予RAM用户调用STS服务AssumeRole接口的固定权限是AliyunSTSAssumeRoleAccess,与后续获取临时身份凭证以及通过临时身份凭证发起SLS请求所需权限无关。
单击确认新增授权。
步骤三:在访问控制创建RAM角色
创建RAM用户要扮演的RAM角色,并获取对应的角色的ARN(Aliyun Resource Name,阿里云资源名称),用于之后的角色扮演。
在左侧导航栏,选择身份管理 > 角色。
单击创建角色,可信实体类型选择阿里云账号,单击下一步。
填写角色名称,选择当前云账号。
单击完成。完成角色创建后,单击关闭。
在RAM角色管理页面,搜索框输入角色名称,例如
sls-web-tracking
。单击复制,保存角色的ARN。
步骤四:在访问控制创建自定义权限策略
按照最小授权原则,为RAM角色创建一个自定义权限策略,限制只能向指定的SLS Logstore上传日志。
在左侧导航栏,选择权限管理 > 权限策略。
单击创建权限策略。
在创建权限策略页面,单击脚本编辑,将以下脚本中的
<Project名称>
和<Logstore名称>
替换为准备工作中创建的Project名称和Logstore名称。重要以下示例仅供参考。您需要根据实际需求配置更细粒度的授权策略,防止出现权限过大的风险。关于更细粒度的授权策略配置详情,请参见RAM自定义授权示例。
{ "Version":"1", "Statement":[ { "Effect":"Allow", "Action":[ "log:PostLogStoreLogs" ], "Resource":[ "acs:log:*:*:project/<Project名称>/logstore/<Logstore名称>" ] } ] }
策略配置完成后,单击继续编辑基本信息。
在基本信息区域,填写策略名称,然后单击确定。
步骤五:在访问控制为RAM角色授予权限
为RAM角色授予权限,以便该RAM角色被RAM用户扮演时能获取所需的权限。
在左侧导航栏,选择身份管理 > 角色。
在角色页面,找到目标RAM角色,然后单击RAM角色右侧的新增授权。
在新增授权页面下的自定义策略页签,选择已创建的自定义权限策略。
单击确认新增授权。
步骤六:在业务服务器获取临时身份凭证
在Web应用中,通过在业务服务器集成STS SDK,实现一个获取临时STS身份凭证的接口。当这个接口(/get_sts_token
)通过HTTP GET方法被访问时,它会生成一个临时身份凭证,并将其返回给请求者。
在ECS实例上,使用Flask框架快速搭建Web应用,实现一个获取临时STS身份凭证的接口的操作示例如下:
连接ECS实例。
具体操作,请参见通过控制台使用ECS实例(快捷版)。
安装Python3。
创建项目文件夹,然后切换到项目目录。
mkdir my_web_sample cd my_web_sample
安装依赖。
pip3 install Flask pip3 install attr pip3 install yarl pip3 install async_timeout pip3 install idna_ssl pip3 install attrs pip3 install aiosignal pip3 install charset_normalizer pip3 install alibabacloud_tea_openapi pip3 install alibabacloud_sts20150401 pip3 install alibabacloud_credentials
编写后端代码。
创建一个
main.py
文件。在这个文件中,添加以下Python代码。
import json from flask import Flask, render_template from alibabacloud_tea_openapi.models import Config from alibabacloud_sts20150401.client import Client as Sts20150401Client from alibabacloud_sts20150401 import models as sts_20150401_models from alibabacloud_credentials.client import Client as CredentialClient app = Flask(__name__) # 将<YOUR_ROLE_ARN>替换为RAM角色的ARN。 role_arn_for_oss_upload = '<YOUR_ROLE_ARN>' # 设置为STS服务的地域,例如cn-hangzhou。 region_id = 'cn-hangzhou' @app.route("/") def hello_world(): return render_template('index.html') @app.route('/get_sts_token', methods=['GET']) def get_sts_token(): # 初始化 CredentialClient 时不指定参数,代表使用默认凭据链。 # 在本地运行程序时,可以通过环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID、ALIBABA_CLOUD_ACCESS_KEY_SECRET 指定 AK; # 在 ECS\ECI\容器服务上运行时,可以通过环境变量 ALIBABA_CLOUD_ECS_METADATA 来指定绑定的实例节点角色,SDK 会自动换取 STS 临时凭证。 config = Config(region_id=region_id, credential=CredentialClient()) sts_client = Sts20150401Client(config=config) assume_role_request = sts_20150401_models.AssumeRoleRequest( role_arn=role_arn_for_oss_upload, # 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称。 role_session_name='<YOUR_ROLE_SESSION_NAME>' ) response = sts_client.assume_role(assume_role_request) token = json.dumps(response.body.credentials.to_map()) return token app.run(host="0.0.0.0", port=80)
将代码中的
<YOUR_ROLE_ARN>
替换为步骤三获取的角色ARN。将代码中的
<YOUR_ROLE_SESSION_NAME>
设置为自定义的会话名称,例如role_session_test
。
使用步骤一获取的访问密钥启动应用程序。
ALIBABA_CLOUD_ACCESS_KEY_ID=<YOUR_AK_ID> ALIBABA_CLOUD_ACCESS_KEY_SECRET=<YOUR_AK_SECRET> python3 main.py
在浏览器中访问
http://<ECS实例公网IP地址>/get_sts_token
。成功返回示例如下:
步骤七:在浏览器使用临时身份凭证上传日志到SLS
在业务服务器配置了获取STS临时身份凭证的接口后,在Web应用的前端集成SLS的前端埋点SDK(@aliyun-sls/web-track-browser)和STS插件(@aliyun-sls/web-sts-plugin),实现实时监控用户行为并上传相关日志。当创建埋点跟踪器(tracker)实例时,它将自动请求之前部署的/get_sts_token接口,以便获取必要的临时STS凭证。这种机制保证了用户在网站交互过程中,如登录、浏览商品或提交订单等活动的数据会被安全地传输至SLS。
在ECS上,使用Parcel打包工具来编译并打包SLS的前端埋点SDK和STS插件,然后将这些资源集成到Web应用的前端代码的操作示例如下:
按
Ctrl + C
停止应用程序。安装npm。
yum install npm
安装Parcel。
npm install -g parcel-bundler
创建前端项目文件。
mkdir templates static src
安装项目依赖。
npm install --save @aliyun-sls/web-track-browser npm install --save @aliyun-sls/web-sts-plugin
编写前端代码。
在
src
目录中创建一个src/index.js
文件。vim src/index.js
在这个文件中,添加以下JavaScript代码。
import SlsTracker from "@aliyun-sls/web-track-browser"; import createStsPlugin from "@aliyun-sls/web-sts-plugin"; const opts = { host: "cn-hangzhou.log.aliyuncs.com", // 所在地域的服务入口。例如cn-hangzhou.log.aliyuncs.com project: "${project}", // Project 名称 logstore: "${logstore}", // Logstore 名称 time: 10, // 发送日志的时间间隔,默认是10秒 count: 10, // 发送日志的数量大小,默认是10条 topic: "topic", // 自定义日志主题 source: "source", tags: { tags: "tags", }, }; const stsOpt = { accessKeyId: "", accessKeySecret: "", securityToken: "", // 以下是一个 stsToken 刷新函数的简单示例 refreshSTSToken: () => new Promise((resolve, reject) => { const xhr = new window.XMLHttpRequest(); xhr.open("GET", "http://<ECS实例公网IP地址>/get_sts_token", true); xhr.send(); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status === 200) { let credential = JSON.parse(xhr.response); // 函数的本质目的:设置 stsOpt 的临时密钥和令牌 stsOpt.accessKeyId = credential.AccessKeyId; stsOpt.accessKeySecret = credential.AccessKeySecret; stsOpt.securityToken = credential.SecurityToken; resolve(); } else { reject("Wrong status code."); } } }; }), // refreshSTSTokenInterval: 300000, // stsTokenFreshTime: undefined, }; const tracker = new SlsTracker(opts); // 创建 sts 插件 const stsPlugin = createStsPlugin(stsOpt); // 使用 sts 插件 tracker.useStsPlugin(stsPlugin); // 用户登录跟踪 document.getElementById("loginButton").addEventListener("click", () => { const username = document.getElementById("username").value; tracker.send({ eventType: "login", username: username, }); console.log("Login event tracked for:", username); }); // 商品浏览跟踪 document.querySelectorAll(".product").forEach((productButton) => { productButton.addEventListener("click", (event) => { const productName = event.target.getAttribute("data-product-name"); const productPrice = event.target.getAttribute("data-price"); tracker.send({ eventType: "view_product", productName: productName, price: productPrice, }); console.log("Product view tracked for:", productName); }); }); // 订单提交跟踪 document.getElementById("orderButton").addEventListener("click", () => { tracker.send({ eventType: "place_order", orderDetails: "Order placed for example items", }); console.log("Order placed event tracked"); });
关于
opts
和stsOpt
的参数说明,请参见使用WebTracking JavaScript SDK的STS插件上传日志。将代码中的
${project}
和${logstore}
替换为准备工作中创建的Project名称和Logstore名称。将代码中的
<ECS实例公网IP地址>
替换为准备工作中创建的ECS实例的公网IP地址。如何查看公网IP地址,请参见查看IP地址。
创建HTML模板文件。
在
templates
目录中创建一个index.html
文件。vim templates/index.html
在这个文件中,添加以下HTML代码。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Web Tracking Static Example</title> </head> <body> <h1>Welcome to Simple Web Store</h1> <!-- 用户登录 --> <div> <label for="username">Username:</label> <input type="text" id="username" name="username"> <button id="loginButton">Login</button> </div> <!-- 商品列表 --> <div> <h2>Products</h2> <button class="product" data-product-name="Laptop" data-price="1200">Laptop - $1200</button> <button class="product" data-product-name="Smartphone" data-price="800">Smartphone - $800</button> <button class="product" data-product-name="Tablet" data-price="500">Tablet - $500</button> </div> <!-- 提交订单 --> <div> <h2>Your Order</h2> <button id="orderButton">Place Order</button> </div> <script type="module" src="{{ url_for('static', filename='js/index.js') }}"></script> </body> </html>
在项目根目录
my_web_sample
下,打包静态资源。使用步骤一获取的访问密钥启动应用程序。
在浏览器中访问
http://<ECS实例公网IP地址>
,然后在页面中输入用户名、选择商品、并点击下单,模拟真实用户在浏览器的行为。
parcel build src/index.js --out-dir static/js --public-url ./js
ALIBABA_CLOUD_ACCESS_KEY_ID=<YOUR_AK_ID> ALIBABA_CLOUD_ACCESS_KEY_SECRET=<YOUR_AK_SECRET> python3 main.py
完成及清理
方案验证
完成以上操作后,您可以通过预览方式查看数据是否已上传到SLS。
在消费预览面板,查看成功上传的日志。
清理资源
在本方案中,您创建了1台ECS实例、1个SLS Project和Logstore、1个RAM用户和1个RAM角色。测试完方案后,您可以参考以下规则处理对应产品的资源,避免继续产生费用或产生安全风险。
释放ECS实例。具体操作,请参见释放实例。
先删除Logstore,再删除Project。具体操作,请参见管理Logstore和管理Project。
后续操作
将终端用户的浏览器日志收集到SLS后: