鉴权是网络安全的关键环节,主要用于确认请求者的身份,并确保其有权限执行请求的操作。本文介绍access_token、AK/SK、session/cookie
1 访问凭证access_token鉴权
原理:
access_token是一种临时的访问令牌,用于在服务端验证客户端的身份。这种方式通常涉及以下几个步骤:
- 用户身份验证:客户端通过用户名和密码或其他身份验证方法向授权服务器认证其身份。
- 令牌生成:一旦身份验证成功,授权服务器会发放一个access_token给客户端。此令牌包含了足够的数据(如权限级别、有效期限等)以验证客户端请求。
- 发送请求:客户端在随后对API的调用中,将access_token作为请求的一部分发送给服务器。
- 服务端验证:服务器接收到请求后,会首先验证access_token的有效性。如果验证成功,服务器则处理请求;如果失败,返回错误信息。
使用场景:
access_token适用于需要快速且频繁的身份验证场景,尤其是在Web和移动应用程序中。由于其具有固定的有效期,这种方法可以有效减少重复的身份验证过程,提高系统效率。
示例
一个简单的 access_token 示例,实际应用中应更复杂且安全
ACCESS_TOKEN = "my_secret_token" @app.route('/api/data', methods=['GET']) def get_data(): # 从请求头中获取 access_token token = request.headers.get('Authorization') if token != ACCESS_TOKEN: return jsonify({'error': 'Unauthorized'}), 401 # 如果验证通过,返回数据 return jsonify({'data': 'Here is your data'})
在这个例子中,当客户端访问 /api/data 时,必须在请求头中提供正确的 Authorization 字段,其值应为 “my_secret_token”,才能成功获取数据。
2 基于安全认证AK/SK的签名计算鉴权
原理:
AK(Access Key)和SK(Secret Key)是一对密钥,用于通过加密签名的方式进行身份验证。这种方式通常包括以下几个步骤:
- 密钥分配:服务提供商向客户端分配一对密钥,即公开的AK和保密的SK。
- 生成签名:在发送HTTP请求之前,客户端使用SK对请求进行签名。签名过程通常包括选择合适的加密算法(如HMAC-SHA256),并用SK对请求的关键信息(如请求路径、时间戳、参数等)进行加密处理。
- 发送请求:将生成的签名和AK一同附加在HTTP请求中发送到服务器。
- 服务端验证:服务器接收到请求后,会使用同样的算法和SK重新生成签名,并与请求中的签名进行对比。如果两者一致,说明请求是合法的;否则,认为请求可能被篡改,返回错误信息。
在对称密钥系统中,相同的密钥(在此情景中为 SK,即 Secret Key)用于加密和解密数据。AK 是用来标识用户的,而 SK 用来生成和验证请求的签名。具体来说:
签名生成:当用户想要发送一个请求到服务器时,会利用 SK 对请求信息(如请求方法、URI、时间戳等)进行加密,生成一个独一无二的签名(Signature)。
签名验证:服务器收到请求后,也会用相同的 SK 对请求信息进行同样的加密操作。服务器生成的签名如果与用户发送的签名一致,那么认为请求是合法的,因为只有掌握正确 SK 的用户才能生成正确的签名。
使用场景:
AK/SK签名鉴权适用于对安全性要求较高的场景,尤其是在对数据完整性和请求的来源进行校验时。这种方法通过加密签名确保请求在传输过程中未被篡改,同时验证了请求者的身份。
示例:
ACCESS_KEY = "my_access_key" SECRET_KEY = "my_secret_key" @app.route('/api/secure-data', methods=['GET']) def secure_data(): client_ak = request.headers.get('AK') client_signature = request.headers.get('Signature') # 构造待签名的字符串 message = request.path + ACCESS_KEY # 使用 SK 对 message 进行 HMAC-SHA256 签名 signature = hmac.new(SECRET_KEY.encode(), message.encode(), hashlib.sha256).hexdigest() if client_ak != ACCESS_KEY or client_signature != signature: return jsonify({'error': 'Unauthorized'}), 401 return jsonify({'secure_data': 'Here is your secure data'})
3 session/cookie
在Web开发中,Session 和 Cookie 是用来维持用户状态和进行身份验证的常用技术。理解它们的工作原理是保证Web应用安全的重要一环。
Cookie
Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。Cookie 主要用于以下几个方面:
- 会话状态管理(如用户登录状态、购物车内容或游戏分数)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
安全实践:
- Secure 属性:标记为 Secure 的 Cookie 只应通过被HTTPS协议加密过的请求发送给服务端。
- HttpOnly 属性:设置 HttpOnly 属性的 Cookie 无法通过客户端脚本访问。这可以有效防止跨站脚本攻击(XSS)窃取Cookie。
- SameSite 属性:可以用来防止跨站请求伪造(CSRF)攻击,它允许服务器声明某个Cookie不应该被浏览器在跨站请求中发送。
Session
Session 是另一种服务器端的数据存储方案,用于存储有关用户会话的信息。不同于Cookie直接存储在客户端,Session数据默认存储在服务器上。当用户在应用中进行操作时,服务器会创建一个会话,并发给客户端一个唯一的标识符(通常名为 Session ID),而这个标识符通常通过Cookie发送给客户端,保存在用户的浏览器中。
工作原理:
- 会话创建:用户进行登录等操作后,服务器创建一个新的Session,并生成一个唯一的Session ID。
- 存储Session ID:Session ID 通过设置在Cookie中返回给用户的浏览器。
- 客户端请求:在后续的每个请求中,浏览器都会发送包含Session ID的Cookie。
- 服务器识别:服务器接收到请求后,提取Session ID,并在存储中查找对应的会话数据。如果找到,用户的状态被恢复;如果找不到,可能会要求用户重新登录。
安全实践:
- Session ID的安全性:确保Session ID具有足够的随机性且不易被预测。
- 有效期管理:为Session设置合理的过期时间,超时后自动销毁。
- 数据保护:在服务器端加密存储Session数据,确保即便数据被盗也难以解析。
在 Flask 中,session
的实现通常依赖于客户端的浏览器和服务器之间的交互方式,具体表现为以下两种主要存储机制.默认情况下,Flask 在客户端浏览器中存储加密的 session
信息。如果需要更高的安全性或存储大量的会话数据,可以通过扩展使用服务器端 session
存储
- 基于 Cookie 的 Session
在 Flask 的默认配置中,session
信息是存储在客户端的浏览器中的。这些信息以加密形式存放在一个名为 session cookie
的 cookie
中。服务器通过密钥(SECRET_KEY
配置项)对这些信息进行签名,确保在客户端返回给服务器时能够验证其完整性和未被篡改。
- 当用户与 Flask 应用交互时(比如登录),服务器将生成的
session
数据加密后保存在一个cookie
中。 - 客户端浏览器会保持这个
cookie
并在后续请求中发送给服务器。 - 服务器接收到
cookie
后,会解密并验证session
数据,用于确认用户状态或身份。
这种方式的优点是简单且易于实现,但安全性依赖于 SECRET_KEY
的保密性和 cookie
的安全设置(如使用 HTTPS、设置为 HttpOnly 等)。
- 服务器端 Session
虽然 Flask 默认使用基于 cookie
的 session
存储,你也可以配置 Flask 使用服务器端 session
存储机制。这通常通过第三方扩展如 Flask-Session 实现。在服务器端 session
存储中,session
数据存放在服务器端的存储系统中(如数据库、缓存系统等),只有一个唯一标识符存储在客户端的 cookie
中。
- 用户的每次请求都会携带一个
session ID
。 - 服务器使用这个
session ID
来查询存储系统中的session
数据。 - 这种方法更安全,因为敏感数据不会存储在用户的设备上,而且可以减轻客户端的负载。
使用服务器端存储可以增强安全性,并允许存储更多的数据,因为不受浏览器 cookie
大小限制。但这也可能增加服务器端的复杂性和资源需求。
示例:
def login_required(view): @functools.wraps(view) def wrapped_view(**kwargs): if g.user is None: return redirect(url_for("auth.login")) return view(**kwargs) return wrapped_view @bp.before_app_request def load_logged_in_user(): user_id = session.get("user_id") if user_id is None: g.user = None else: g.user = ( get_db().execute("SELECT * FROM user WHERE id = ?", (user_id,)).fetchone() ) @bp.route("/register", methods=("GET", "POST")) def register(): if request.method == "POST": username = request.form["username"] password = request.form["password"] db = get_db() error = None if error is None: try: db.execute( "INSERT INTO user (username, password) VALUES (?, ?)", (username, generate_password_hash(password)), ) db.commit() except db.IntegrityError: # The username was already taken, which caused the # commit to fail. Show a validation error. error = f"User {username} is already registered." else: # Success, go to the login page. return redirect(url_for("auth.login")) flash(error) return render_template("auth/register.html") @bp.route("/login", methods=("GET", "POST")) def login(): """Log in a registered user by adding the user id to the session.""" if request.method == "POST": username = request.form["username"] password = request.form["password"] db = get_db() error = None user = db.execute( "SELECT * FROM user WHERE username = ?", (username,) ).fetchone() if user is None: error = "Incorrect username." elif not check_password_hash(user["password"], password): error = "Incorrect password." if error is None: # store the user id in a new session and return to the index session.clear() session["user_id"] = user["id"] return redirect(url_for("index")) flash(error) return render_template("auth/login.html")
4 access_token 和 session/cookie 的区别
存储位置:
- access_token通常存储在客户端,如浏览器的本地存储或应用程序的存储系统中。它不依赖于浏览器的cookie机制。
- session和cookie则是利用浏览器的cookie存储机制来维持状态。session ID 通常保存在cookie中,服务器用这个 ID 来识别具体的用户会话。
安全性:
- access_token可以设计为自包含(如 JWT),含有加密的用户信息和权限数据,服务器通过解密token来认证用户,不需要额外的信息。
- session通常在服务器端存储用户数据,只有无法直接解读的session ID 存在cookie中,这使得session依赖于服务器的内存或数据库,而cookie本身容易受到跨站脚本(XSS)和跨站请求伪造(CSRF)等攻击。
使用场景:
- access_token适用于无状态的API服务,特别是在分布式系统或微服务架构中,各服务间需要独立验证用户身份的场景。
- session和cookie更常用于传统的Web应用中,其中服务器需要保持和管理用户的登录状态。
5 AK/SK 签名鉴权 和 session/cookie 的区别
目的:
- AK/SK签名鉴权主要用于服务间的认证,确保请求的安全性和数据的完整性,是一种服务器到服务器之间的通信验证方法。
- session和cookie则主要用于用户认证,管理用户的登录状态和会话信息。
实现方式:
- AK/SK 签名鉴权通过使用密钥对请求内容进行加密签名,然后在服务器端验证签名的合法性。这种方法不依赖于客户端的状态存储。
- session和cookie依赖于客户端和服务器之间交换cookie来识别和验证用户,通常涉及到将session ID 存储在cookie中。
安全性:
- AK/SK 签名鉴权由于其加密性质,提供较高的安全性,可以有效防止中间人攻击。
- session和cookie虽然方便但更容易受到攻击,如cookie盗窃或会话劫持等风险。