如何可靠获取sendBeacon发起页面URL并安全传递至后端?
可靠传递sendBeacon请求发起页面pathname的方案
由于前端所有数据都存在被篡改的可能,核心思路是通过后端验证机制来确保传递的pathname可信,以下是几种可行方案:
方案1:后端生成带签名的页面标识
页面加载时由后端为当前pathname生成加密签名,前端同时传递pathname和签名,后端通过重新计算签名验证数据真实性。
后端实现示例(以Python为例)
import hmac import hashlib from flask import Flask, request, render_template app = Flask(__name__) SECRET_KEY = "your-secure-secret-key" # 务必使用安全的密钥,避免硬编码 @app.route('/page') def render_page(): current_path = request.path # 生成HMAC签名 signature = hmac.new( SECRET_KEY.encode(), current_path.encode(), hashlib.sha256 ).hexdigest() # 将签名传递到前端页面,比如通过meta标签 return render_template('your-page.html', path_signature=signature)
前端实现
<!-- 在页面的head中添加meta标签 --> <meta name="path-signature" content="{{ path_signature }}">
const url = "/some/url"; const data = new FormData(); const pathname = window.location.pathname; const signature = document.querySelector('meta[name="path-signature"]').content; data.append("pathname", pathname); data.append("signature", signature); navigator.sendBeacon(url, data);
后端验证逻辑
当接收到sendBeacon请求时,重新计算传入pathname的签名,与前端传递的signature对比:
@app.route('/some/url', methods=['POST']) def receive_beacon(): pathname = request.form.get('pathname') received_signature = request.form.get('signature') # 重新计算签名 computed_signature = hmac.new( SECRET_KEY.encode(), pathname.encode(), hashlib.sha256 ).hexdigest() # 验证签名是否一致 if received_signature == computed_signature: # 签名有效,pathname可信 process_pathname(pathname) return '', 204 else: # 签名无效,数据可能被篡改 return '', 403
方案2:利用Session关联页面路径
用户访问页面时,后端将当前pathname存入用户Session,前端sendBeacon时仅传递Session标识(通常由Cookie自动携带),后端通过Session直接获取可信的pathname。
后端实现示例
@app.route('/page') def render_page(): # 将当前路径存入Session session['current_path'] = request.path return render_template('your-page.html') @app.route('/some/url', methods=['POST']) def receive_beacon(): # 从Session中获取可信的pathname trusted_pathname = session.get('current_path') if trusted_pathname: process_pathname(trusted_pathname) return '', 204 else: return '', 400
前端实现
无需额外传递pathname,直接发送请求即可:
const url = "/some/url"; const data = new FormData(); // 可添加其他业务数据,pathname由后端从Session获取 navigator.sendBeacon(url, data);
注意:该方案需考虑多标签页场景,若用户打开多个页面,Session中会存储最后访问的路径,需根据业务需求判断是否适用。
方案3:页面唯一ID映射
后端渲染页面时生成唯一ID,将ID与对应pathname存储在数据库/缓存中,前端sendBeacon时传递该ID,后端通过ID查询获取可信的pathname。
后端实现示例
import uuid from flask import Flask, request, render_template from redis import Redis app = Flask(__name__) redis = Redis(host='localhost', port=6379, db=0) @app.route('/page') def render_page(): current_path = request.path page_id = str(uuid.uuid4()) # 将page_id与pathname存入Redis,设置过期时间(比如1小时) redis.setex(page_id, 3600, current_path) return render_template('your-page.html', page_id=page_id)
前端实现
<input type="hidden" id="page-id" value="{{ page_id }}">
const url = "/some/url"; const data = new FormData(); const pageId = document.getElementById('page-id').value; data.append("page_id", pageId); navigator.sendBeacon(url, data);
后端查询逻辑
@app.route('/some/url', methods=['POST']) def receive_beacon(): page_id = request.form.get('page_id') trusted_pathname = redis.get(page_id) if trusted_pathname: process_pathname(trusted_pathname.decode()) return '', 204 else: return '', 400
内容的提问来源于stack exchange,提问作者pileup
相关产品推荐
相关产品推荐

