为了深入洞察用户行为,企业通常会从Web应用中收集浏览器日志,分析用户的设备信息、浏览历史和应用内互动等关键数据,进而改善产品功能并优化用户体验。通过启用阿里云日志服务(SLS)的WebTracking功能,企业能自动将终端用户的浏览器日志收集到SLS,既提高了数据处理效率,又减轻了业务服务器的负担。尽管便捷,但由于允许Logstore支持匿名写入,如浏览器中的配置信息泄露可能引致数据污染。为了降低该风险,推荐使用阿里云安全令牌服务(STS),为浏览器端提供时间和权限受限的访问令牌,从而增强数据上传过程的安全性。本文将以用户浏览行为日志收集为例,介绍如何利用SLS和STS为Web应用构建安全、高效的浏览器日志直传服务。
背景信息
企业A开发的这款Web应用为用户提供了一个功能丰富、界面友好的在线平台,旨在吸引用户前来浏览、购买商品或服务。为了更好地理解用户的需求、优化用户体验,并进一步提升网站的转化率,企业A认识到了对用户行为进行深入分析的重要性。这种分析需要基于用户在网站上的实际操作行为,比如点击、滑动、停留时间、搜索习惯以及购买行为等。因此,企业A的技术团队决定采集Web应用的浏览器端日志,并将这些日志上传到SLS进行更高效的数据分析和处理。
安全风险
为了减轻业务服务器的负担并提高处理效率,该Web应用被设计为允许用户的网页浏览器直接将日志数据上传到SLS,避免了日志采集与上传过程中经过业务服务器的中转。
企业A计划采取以下方案搭建浏览器日志直传服务:
然而,在以上方案中,允许Web应用在浏览器环境直接上传日志到SLS的同时,由于SLS的Logstore开启了支持匿名写入的WebTracking功能,企业将面临以下风险:
数据污染风险:恶意用户可能会上传虚假或蓄意破坏的数据,影响数据质量和分析结果的准确性。
服务滥用风险:未受限的写入访问可能会被利用进行拒绝服务(DoS)攻击,通过大量写入请求耗尽资源。
解决方案
为了应对上述风险,企业在原有方案的基础上增加临时授权。通过这种方式,企业A能够在确保数据直传效率的同时,实现以下效果:
增强的身份验证和授权:通过STS生成的具有时间限制的令牌,即便在短时间内泄露,也极大降低了安全风险。因为这些凭证很快就会失效,降低了被不当利用的可能性。
精细化权限控制:STS允许根据最小权限原则配置权限,仅授权Web应用必需的访问权限。这种精细化的权限控制方法限制了潜在泄露的影响范围,防止了过度权限的风险。
企业A最终采取以下方案搭建浏览器日志直传服务:
方案部署
下面将以一个简单的用户浏览行为日志收集场景为例,引导您一步步使用SLS和STS为Web应用部署浏览器日志直传服务。本方案部署的示例工程:simple-web-tracking-sts.zip
一键部署
执行以下操作使用资源编排一键部署示例工程。
单击一键部署链接。
在页面左上角的地域,选择地域,例如华东1(杭州)。
在配置参数页签,完成日志服务SLS配置和云服务器ECS配置,然后单击下一步。
单击创建。
创建完成后,单击输出页签,然后单击WebTrackingUrl,在页面中输入用户名、选择商品、并点击下单,模拟真实用户在浏览器的行为。
完成及清理
方案验证
完成以上操作后,您可以通过预览方式查看数据是否已上传到SLS。
在Project列表区域,单击目标Project。
在消费预览面板,查看成功上传的日志。
清理资源
在本方案中,您创建了1台ECS实例、1个SLS Project和Logstore、1个RAM用户和1个RAM角色。测试完方案后,您可以参考以下规则处理对应产品的资源,避免继续产生费用或产生安全风险。
登录资源编排控制台。
在左侧导航栏,单击资源栈。
在资源栈列表页面,找到创建的资源栈,然后在其操作列单击删除。
手动部署
准备工作
在开始部署前,您需要先创建云资源。
创建一个日志服务Project和Logstore,用于存储和分析从浏览器采集的日志。具体操作,请参见创建项目Project和创建Logstore。
参数
示例值
所属地域
华东1(杭州)
Project名称
web-tracking-project
Logstore名称
web-tracking-logstore
创建一台ECS实例作为业务服务器,用于生成临时身份凭证。具体步骤,请参见创建ECS实例。
说明在实际部署时,您可以将调用STS服务的接口集成到自己的业务服务器的接口中,而无需创建该ECS实例。
参数
示例值
付费类型
按量付费
地域
华东1(杭州)
公网 IP
分配公网 IPv4 地址
安全组
开放HTTP (TCP:80)端口
步骤一:创建RAM用户
步骤二:授权RAM用户授予调用AssumeRole接口
为RAM用户添加AliyunSTSAssumeRoleAccess权限,使其可以通过扮演RAM角色来获取临时身份凭证。具体操作,请参见为RAM用户授权。
步骤三:创建RAM角色
创建RAM用户要扮演的RAM角色,例如命名为:sls-web-tracking
。具体操作,请参见创建可信实体为阿里云账号的RAM角色。
步骤四:创建自定义权限策略
创建一个自定义权限策略,例如命名为:post-logs-policy
。其中在脚本编辑页签,请使用以下脚本替换配置框中的原有内容。具体操作,请参见通过脚本编辑模式创建自定义权限策略。
将以下脚本中的<Project名称>
和<Logstore名称>
替换为准备工作中创建的Project名称和Logstore名称。
该策略只能向指定的SLS Logstore上传日志。您可根据实际需求配置更细粒度的授权策略,防止出现权限过大的风险。更多信息,请参见RAM自定义授权示例。
{
"Version":"1",
"Statement":[
{
"Effect":"Allow",
"Action":[
"log:PostLogStoreLogs"
],
"Resource":[
"acs:log:*:*:project/<Project名称>/logstore/<Logstore名称>"
]
}
]
}
步骤五:为RAM角色授予权限
为RAM角色sls-web-tracking
授予创建的自定义权限post-logs-policy
,以便被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代码。
重要将代码中的
<YOUR_ROLE_ARN>
替换为步骤三中创建的RAM角色sls-web-tracking
的ARN。ARN获取方式请参见查看RAM角色。将代码中的
<YOUR_ROLE_SESSION_NAME>
设置为自定义的会话名称,例如role_session_test
。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)
使用步骤一中创建的RAM用户的访问密钥启动应用程序。
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
。成功返回示例如下:
步骤七:在浏览器使用临时身份凭证上传日志到日志服务Logstore
在业务服务器配置了获取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>WebTracking 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。
在Project列表区域,单击目标Project。
在消费预览面板,查看成功上传的日志。
清理资源
在本方案中,您创建了1台ECS实例、1个SLS Project和Logstore、1个RAM用户和1个RAM角色。测试完方案后,您可以参考以下规则处理对应产品的资源,避免继续产生费用或产生安全风险。
释放ECS实例。具体操作,请参见释放实例。
先删除Logstore,再删除Project。具体操作,请参见管理Logstore和管理Project。
后续操作
将终端用户的浏览器日志收集到SLS后: