鉴权是网络安全的关键环节,主要用于确认请求者的身份,并确保其有权限执行请求的操作。本文介绍access_token、AK/SK、session/cookie
1 访问凭证access_token鉴权
1.1 原理
access_token是一种临时的访问令牌,用于在服务端验证客户端的身份。这种方式通常涉及以下几个步骤:
- 用户身份验证:客户端通过用户名和密码或其他身份验证方法向授权服务器认证其身份。
- 令牌生成:一旦身份验证成功,授权服务器会发放一个access_token给客户端。此令牌包含了足够的数据(如权限级别、有效期限等)以验证客户端请求。
- 发送请求:客户端在随后对API的调用中,将access_token作为请求的一部分发送给服务器。
- 服务端验证:服务器接收到请求后,会首先验证access_token的有效性。如果验证成功,服务器则处理请求;如果失败,返回错误信息。
1.2 使用场景
access_token适用于需要快速且频繁的身份验证场景,尤其是在Web和移动应用程序中。由于其具有固定的有效期,这种方法可以有效减少重复的身份验证过程,提高系统效率。
1.3 示例
1.3.1 服务器端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”,才能成功获取数据。
1.3.2 客户端access_token 示例
def toHexString(b: bytes) -> str:
hex_string = ''.join(f'{byte:02X}' for byte in b)
return hex_string.upper()
def get_auth_value_post_encrypt():
time_formatter = "%Y-%m-%d %H:%M:%S"
access_time = datetime.datetime.now().strftime(time_formatter)
imei_num = get_imei()
auth_value_pre_encrypt = f"{APPID}\n{ACCESS_KEY}\n{imei_num}\n{access_time}\n{get_phone_type()}\n{CLIENT_VERSION}\n{PHONE_TYPE_ANDROID}"
cipher = DES.new(SECRET_KEY.encode(ENCODING), DES.MODE_CBC, SECRET_KEY.encode(ENCODING))
auth_value_post_encryptaa = cipher.encrypt(pad(auth_value_pre_encrypt.encode('UTF-8'), DES.block_size))
auth_value_post_encrypt = toHexString(auth_value_post_encryptaa)
return auth_value_post_encrypt
def send_message(message):
auth_value_post_encryp = get_auth_value_post_encrypt()
headers = {
"Accept": 'application/json',
"Authorization": auth_value_post_encryp,
"Content-type": 'application/json; charset=UTF-8',
"Charset": 'UTF-8'
}
# 请求体需要根据石瑀的具体要求进行修改,这里提供一个基础模板
data = {}
response = requests.post(
SEVER_URL,
json=data,
headers=headers,
timeout=HTTP_SOCKET_TIME_OUT
)
提供了一个密钥 SECRET_KEY.encode(ENCODING) 和加密模式 DES.MODE_CBC,还明确指定了一个初始化向量(IV),即 SECRET_KEY.encode(ENCODING)。这种方式特别适用于那些需要 IV 的加密模式(如 CBC 模式)。在 CBC 模式 中,IV 是必须的,因为每个块的加密依赖于前一个块的密文(第一块除外),这就需要一个唯一的 IV 来加密第一块数据。提供了自定义的 IV(在这里使用了密钥作为 IV),可以避免使用默认的全零 IV,从而提高安全性。DES 加密要求输入的明文长度是 8 字节的倍数(因为 DES 的块大小是 8 字节)。为了满足这一要求,需要对不满足 8 字节倍数长度的明文进行填充。在代码中,使用了 pad 函数来完成这一操作。pad 函数通常会根据 PKCS5 或 PKCS7 填充标准来填充额外的字节,以确保数据长度是 8 字节的倍数。将加密后的字节数据转换为十六进制字符串进行返回。这样做的目的是将密文以可读的字符串形式传输(例如,HTTP 请求头的 Authorization 字段)、
在块加密算法中,ECB 模式(电子密码本模式)是最简单的,它直接对每个明文块进行加密。但是,由于 ECB 模式存在严重的安全问题:相同的明文块会被加密成相同的密文块,这种模式很容易被攻击者通过分析加密后的模式破解。相比之下,CBC 模式 在每个块加密时引入了前一个密文块的影响,这样即使明文块相同,得到的密文也会不同,从而增加了攻击难度。因此,CBC 模式 被广泛认为是更安全的选择。
2 access_token(基于 JWT)
2.1 JWT(JSON Web Token)原理
JWT是一种开放标准(RFC 7519),用于在客户端和服务端之间安全地传递信息。JWT 的内容是 签名 过的,确保数据不能被篡改。通常用来进行身份验证、授权或传递一些加密的信息。JWT 包含三部分:
- Header(头部):定义令牌的元数据,如签名算法。
- Payload(有效负载):包含用户信息、令牌的有效期等。
- Signature(签名):由 Header、Payload 和一个私密密钥组合生成,用于验证令牌的合法性和完整性。
JWT 结构是通过 .
分隔的三段:
Header.Payload.Signature
2.2 代码分析如何生成 Token
生成 Token 的函数
def generate_token(apikey: str, exp_seconds: int):
try:
id, secret = apikey.split(".")
except Exception as e:
raise Exception("invalid apikey", e)
payload = {
"api_key": id, # 订单的密钥ID
"exp": int(round(time.time())) + exp_seconds, # 过期时间(秒)
"timestamp": int(round(time.time())), # 生成Token的时间戳
}
return jwt.encode(
payload,
secret, # 使用API密钥的签名密钥部分作为密钥进行签名
algorithm="HS256",
headers={"alg": "HS256", "typ": "JWT", "sign_type": "SIGN"},
)
-
APIKEY 结构:
- 每个用户的 API 密钥(
apikey
)格式为密钥ID.签名密钥
,即类似于646ae749bcf5bc1a1498aeaf.lbIpYGaWQ8VwQ2HYTOkhDCKJP/aGgGAc
这种格式。 apikey.split(".")
将密钥拆分为id
(密钥ID)和secret
(签名密钥)。
- 每个用户的 API 密钥(
-
Payload(有效负载):
- api_key:API 的密钥 ID,用于标识请求的用户。
- exp:令牌的过期时间,生成时为当前时间加上指定的秒数(
exp_seconds
),例如 3600 秒(1 小时)。 - timestamp:生成 Token 时的时间戳,用于防止重放攻击。
-
签名:
- secret:使用 APIKEY 的第二部分(
secret
)作为签名密钥。 - HS256:JWT 使用的签名算法是 HMAC SHA256,用于对 Header 和 Payload 进行加密签名。
- secret:使用 APIKEY 的第二部分(
-
Header:
headers={"alg": "HS256", "typ": "JWT", "sign_type": "SIGN"}
:Header 中指定了签名类型为HS256
,JWT 类型为JWT
。
生成的 JWT Token 结构:
生成的 Token 会包含以下内容:
Header.Payload.Signature
- Header:包含 JWT 的元数据,如签名算法。
- Payload:包含
api_key
(密钥 ID)、exp
(过期时间)和timestamp
(生成时间)。 - Signature:用
secret
作为密钥、结合 Header 和 Payload 生成的签名,用于确保数据未被篡改。
生成示例
假设使用 API 密钥 646ae749bcf5bc1a1498aeaf.lbIpYGaWQ8VwQ2HYTOkhDCKJP/aGgGAc
,并设定 Token 有效期为 3600 秒(1 小时),生成的 Token 类似于:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhcGlfa2V5IjoiNjQ2YWU3NDliY2Y1YmMxYTE0OThhZWFmIiwiZXhwIjoxNjkyMDQ2MTAwLCJ0aW1lc3RhbXAiOjE2OTE5NzQxMDB9.OU3f690ZMNXbs69Jg303wWE471gkHlBRqCKz7k_A9q0
API 请求中的身份验证
生成的 Token 将作为 Bearer Token,放在 HTTP 请求的 Authorization
头部,用于身份验证。例如:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhcGlfa2V5IjoiNjQ2YWU3NDliY2Y1YmMxYTE0OThhZWFmIiwiZXhwIjoxNjkyMDQ2MTAwLCJ0aW1lc3RhbXAiOjE2OTE5NzQxMDB9.OU3f690ZMNXbs69Jg303wWE471gkHlBRqCKz7k_A9q0
当客户端请求 API 时,服务器会:
- 解码 JWT:从
Authorization
头中提取 JWT,解析 Header、Payload 和 Signature。 - 验证签名:使用服务器端的密钥验证签名,确保 Token 没有被篡改。
- 验证有效期:检查
exp
是否过期,确保 Token 在有效期内。 - 验证身份:通过
api_key
判断请求是否来自合法用户。
生成 JWT Token 的步骤
用户用 API Key 的 id 和 secret 创建 Payload。
使用 secret 作为密钥,根据指定算法(如 HS256)生成签名。
将 Header、Payload 和 Signature 合并,生成 JWT Token。
服务器端验证流程
解析请求头中的 Authorization,提取 JWT。
根据 Header 的签名算法,用用户的 secret 对 Token 进行签名验证,确保 Token 未被篡改。
检查 exp 和 timestamp,确保令牌在有效期内。
验证通过后,允许请求访问资源。
Token 安全性
- 防篡改:Token 的签名使用 HMAC SHA256 算法,确保 Token 在传输过程中不能被篡改。如果签名不正确,服务器会拒绝该请求。
- 时效性:Token 包含
exp
(过期时间),确保 Token 在特定时间后失效,避免长时间使用同一个 Token 带来的安全问题。 - 唯一性:每个用户、每次生成的签名密钥不同,确保了每个 Token 都是唯一的,防止重放攻击。
使用场景
这种基于 JWT 的身份验证通常用于:
- API 安全认证:在需要身份验证的 API 请求中,客户端通过
Authorization
头传递 Token,服务器解码和验证 Token 来确保请求的合法性。 - 无状态认证:JWT 使得服务器不需要保存用户的登录状态,所有信息都包含在 Token 中,减少服务器的负担。
- 分布式系统:多个服务可以通过共享密钥进行 Token 验证,无需在不同服务间共享状态。
3 基于安全认证AK/SK的签名计算鉴权
3.1 原理:
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 的用户才能生成正确的签名。
3.2 使用场景
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.3 签名验证的工作原理
服务器收到请求 当服务器接收到一个API请求时,除了常规的请求数据(如URL、请求体等),还会包含一些与签名相关的信息,例如:
AK(访问密钥,公开的标识)
签名(调用者使用SK生成的签名)
时间戳或 nonce(防止重放攻击)
请求头可能类似如下:
GET /api/v1/resource?id=123&name=test HTTP/1.1
Host: api.example.com
X-AK: ACCESS_KEY
X-Signature: GENERATED_SIGNATURE
X-Timestamp: 2024-05-10T12:00:00Z
提取相关信息 服务器会根据预先约定的规则,提取与签名计算相关的信息。这些信息通常包括:
HTTP请求方法(GET, POST等)
请求的URL(包括查询参数)
请求体(如果有,比如POST请求)
请求头中额外的鉴权信息(如时间戳、nonce等)
例如:
请求方法:GET
URL:/api/v1/resource?id=123&name=test
鉴权头:X-Timestamp: 2024-05-10T12:00:00Z
签名头:X-Signature: GENERATED_SIGNATURE
根据规则生成字符串 服务器会将提取的信息按照预定的顺序拼接成一个字符串,用于签名计算。这个顺序和内容必须与客户端生成签名时完全一致,确保双方计算结果相同。
假设需要生成的字符串是这样的(具体格式取决于API设计):
GET
/api/v1/resource
id=123&name=test
X-Timestamp=2024-05-10T12:00:00Z
其中包括:
请求方法
URL及查询参数
时间戳等附加信息
使用SK和哈希算法重新计算签名 服务器会使用与调用者相同的加密算法(如HMAC-SHA256),并使用调用者的SK(服务器已知的密钥)对生成的字符串进行加密计算,生成一个新的签名。
签名计算过程可能如下:
import hmac
import hashlib
import base64
def generate_signature(secret_key, string_to_sign):
# 使用 HMAC-SHA256 算法生成签名
hmac_obj = hmac.new(secret_key.encode('utf-8'), string_to_sign.encode('utf-8'), hashlib.sha256)
signature = base64.b64encode(hmac_obj.digest()).decode('utf-8')
return signature
假设服务器的 SK 为 “SECRET_KEY”
sk = "SECRET_KEY"
string_to_sign = """GET
/api/v1/resource
id=123&name=test
X-Timestamp=2024-05-10T12:00:00Z"""
calculated_signature = generate_signature(sk, string_to_sign)
print(calculated_signature) # 服务器端生成的签名
比对签名 服务器将自己计算出的签名(calculated_signature)与请求中包含的签名(X-Signature)进行比对:
如果签名匹配,说明请求是合法的、未被篡改。服务器会继续处理请求。
如果签名不匹配,说明请求可能被篡改、或者调用者没有使用正确的SK,服务器会拒绝该请求,通常返回401 Unauthorized。
比对伪代码:
if calculated_signature == request_signature:
# 签名匹配,处理请求
process_request()
else:
# 签名不匹配,返回401错误
return "401 Unauthorized"
处理请求或返回错误
如果签名验证通过,服务器会继续处理请求并返回正常的响应。
如果签名验证失败,服务器会返回一个错误响应,通常是 401 Unauthorized,并说明鉴权失败的原因。
防止重放攻击
为了防止重放攻击(即恶意用户截获合法请求并在稍后再次发送),通常会引入**时间戳(Timestamp)和随机数(Nonce)**来增强安全性:
时间戳:每次请求中包含一个时间戳,服务器会检查该时间戳是否在允许的时间窗口内(如5分钟内)。如果时间戳超时,请求将被拒绝。
Nonce:每个请求包含一个唯一的随机数,服务器会记录已处理的随机数,确保每个Nonce只能使用一次,防止相同请求的重复提交。
4 session/cookie
在Web开发中,Session 和 Cookie 是用来维持用户状态和进行身份验证的常用技术。理解它们的工作原理是保证Web应用安全的重要一环。
4.1 Cookie
Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。Cookie 主要用于以下几个方面:
- 会话状态管理(如用户登录状态、购物车内容或游戏分数)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
安全实践:
- Secure 属性:标记为 Secure 的 Cookie 只应通过被HTTPS协议加密过的请求发送给服务端。
- HttpOnly 属性:设置 HttpOnly 属性的 Cookie 无法通过客户端脚本访问。这可以有效防止跨站脚本攻击(XSS)窃取Cookie。
- SameSite 属性:可以用来防止跨站请求伪造(CSRF)攻击,它允许服务器声明某个Cookie不应该被浏览器在跨站请求中发送。
4.2 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
大小限制。但这也可能增加服务器端的复杂性和资源需求。
4.3 示例
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")
5 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应用中,其中服务器需要保持和管理用户的登录状态。
6 AK/SK 签名鉴权 和 session/cookie 的区别
-
目的:
- AK/SK签名鉴权主要用于服务间的认证,确保请求的安全性和数据的完整性,是一种服务器到服务器之间的通信验证方法。
- session和cookie则主要用于用户认证,管理用户的登录状态和会话信息。
-
实现方式:
- AK/SK 签名鉴权通过使用密钥对请求内容进行加密签名,然后在服务器端验证签名的合法性。这种方法不依赖于客户端的状态存储。
- session和cookie依赖于客户端和服务器之间交换cookie来识别和验证用户,通常涉及到将session ID 存储在cookie中。
-
安全性:
- AK/SK 签名鉴权由于其加密性质,提供较高的安全性,可以有效防止中间人攻击。
- session和cookie虽然方便但更容易受到攻击,如cookie盗窃或会话劫持等风险。
7 JWT(JSON Web Token)与access_token的区分
7.1 概念和定义
-
JWT(JSON Web Token):
- JWT 是一种开放标准(RFC 7519),用于在各方之间作为紧凑的、独立的方式传输声明。JWT 包含 Header、Payload 和 Signature 三部分,通常是自包含的(包含所有的用户信息、权限等)。
- JWT 主要通过 HMAC 或 RSA 进行签名,用于验证 Token 的完整性和合法性。
-
Access Token(访问令牌):
- access_token 通常是一个字符串,作为访问受保护资源的凭证,生成于用户登录后。OAuth 2.0 协议广泛使用 access_token 来授权和访问资源。
- Access Token 本身可能只是一个标识符,通常由授权服务器签发。它可以通过查询认证服务器获取到用户信息和权限。
7.2 格式差异
-
JWT:
- 格式:JWT 通常是一个由三部分组成的字符串,格式为
Header.Payload.Signature
,每部分通过 Base64 编码进行编码。 - 自包含:JWT 的
Payload
部分可以包含用户身份信息、权限以及令牌过期时间等。因此,JWT 是自包含的,不需要依赖认证服务器来解析用户信息。
- 格式:JWT 通常是一个由三部分组成的字符串,格式为
-
Access Token:
- 格式:Access Token 的格式可以是任意的字符串,可能是随机生成的,也可以是 UUID,具体格式取决于授权服务器的实现。
- 不一定自包含:Access Token 不一定包含用户信息或权限信息,可能只是一个标识符,通常需要通过 授权服务器 来解析并验证该 Token,获取用户身份和权限。
7.3 鉴权过程
-
JWT 鉴权:
- 客户端携带 JWT 令牌通过
Authorization: Bearer <token>
发送给服务器。 - 服务器收到请求后,不需要与认证服务器交互,可以直接使用 JWT 的签名密钥来解密和验证 JWT 的合法性。
- 验证通过后,可以从 JWT 的
Payload
中获取用户身份信息、权限和 Token 过期时间。
- 客户端携带 JWT 令牌通过
-
Access Token 鉴权:
- 客户端携带 Access Token 通过
Authorization: Bearer <token>
发送给资源服务器。 - 资源服务器通常需要通过认证服务器(授权服务器)来验证 Access Token 的合法性和有效性,或者使用自身的存储来检查 Token 的状态。
- Access Token 可能不包含用户信息,资源服务器需要向认证服务器查询用户的身份信息和权限。
- 客户端携带 Access Token 通过
7.4 存储和状态管理
-
JWT:
- 无状态:JWT 是自包含的,令牌中已经包含了用户的所有信息,因此服务器通常不需要存储用户的会话信息。
- 由于 JWT 包含所有信息,并且在签发时已经包含过期时间,因此它通常不需要依赖数据库或存储进行状态管理。
-
Access Token:
- 需要状态管理:Access Token 通常需要在服务器端存储(例如在数据库中),用于验证令牌的有效性和状态(如是否已过期、是否被撤销)。
- 认证服务器需要跟踪每个 Access Token 的状态,当用户注销或授权撤销时,认证服务器需要更新 Token 状态。
7.5 安全性
-
JWT:
- 签名验证:JWT 使用 HMAC 或 RSA 等算法对其进行签名,确保 Token 在传输过程中未被篡改。服务器可以独立验证 Token 的签名和完整性。
- 无撤销机制:由于 JWT 是无状态的,通常无法在用户注销或撤销授权后立即使 Token 失效,除非实现某种黑名单机制或缩短 Token 的有效期。
-
Access Token:
- 更灵活的安全控制:Access Token 的有效性通常通过认证服务器来控制。通过查询认证服务器,资源服务器可以立即得知 Token 是否有效,是否已被撤销等。
- 支持撤销:Access Token 通常支持撤销机制,认证服务器可以在用户注销或其他情况下使特定 Token 失效。
7.6 适用场景
-
JWT:
- 适合用于分布式系统或无状态认证场景,因为它可以直接在客户端和服务器之间传递,服务器无需每次请求都查询数据库或认证服务器。
- 适用于微服务架构中多个服务之间的身份验证。
-
Access Token:
- 适合用于需要强安全控制的场景,尤其是OAuth 2.0 授权框架下。Access Token 可以被认证服务器精确控制,有效支持 Token 撤销和续期机制。
- Access Token 适用于复杂的授权场景,如用户授权第三方应用访问资源等。
7.7 具体使用中的区别
-
JWT 鉴权:
- 服务器可以通过解析 Token 立即获取所有需要的信息,无需与认证服务器交互。
- 缺乏 Token 撤销机制,适用于用户在一定时间内的无状态认证。
-
Access Token 鉴权:
- Access Token 可能是无自包含信息的,需要通过认证服务器或数据库查询获取用户信息和权限。
- 适用于用户授权的动态管理和强安全控制的场景。
7.8 总结
特性 | JWT(JSON Web Token) | Access Token(访问凭证) |
---|---|---|
格式 | 结构化(Header.Payload.Signature) | 任意字符串 |
自包含性 | 自包含,包含用户信息、权限等 | 不一定自包含,可能只是标识符 |
鉴权方式 | 无需查询认证服务器,直接验证签名 | 需要查询认证服务器验证和获取信息 |
状态管理 | 无状态,Token 不需要存储 | 需要认证服务器维护状态(存储) |
安全性 | 依赖签名,缺乏即时撤销机制 | 支持即时撤销和续期机制 |
适用场景 | 无状态认证、分布式系统 | OAuth 2.0 授权框架、强安全控制场景 |
撤销机制 | 通常无撤销机制 | 支持撤销,动态管理 |
8 加密算法
加密算法可以分为 对称加密算法 和 非对称加密算法,它们在现代信息安全中发挥着重要作用。下面介绍一些常见的加密算法及其特点:
8.1 对称加密算法
对称加密算法使用同一个密钥进行加密和解密。即加密方和解密方都使用同样的密钥。
AES(Advanced Encryption Standard)
- 简介:AES 是一种广泛使用的对称加密算法,由美国国家标准与技术研究院(NIST)于 2001 年采纳。AES 被广泛认为是安全性和效率之间的良好平衡。
- 加密方式:分为 AES-128、AES-192 和 AES-256,表示密钥长度分别为 128 位、192 位和 256 位。
- 优点:
- 效率高:在软件和硬件实现上都非常高效,适合大规模数据加密。
- 安全性强:AES-256 被认为可以抵御绝大多数攻击。
- 缺点:密钥必须安全传递给解密方,密钥管理是一个挑战。
DES(Data Encryption Standard)
- 简介:DES 是早期广泛使用的对称加密算法,由 IBM 开发并被 NIST 采用。它使用 56 位的密钥。
- 加密方式:DES 使用块加密,将数据分成 64 位块进行加密。
- 优点:在 20 世纪非常流行。
- 缺点:
- 不安全:56 位密钥长度过短,已经被破解,且不能抵御现代攻击。
- 被替代:DES 被认为不再安全,已经被 AES 和 3DES 所替代。
DES 的工作原理如下:
明文分块:将明文分成 64 位(8 字节)一组。
初始置换 (Initial Permutation, IP):对每个分组进行初始置换操作,以打乱明文的比特顺序。
16 轮加密:DES 算法对每个分组进行 16 轮的 Feistel 加密操作。每一轮中,根据密钥生成的子密钥与数据进行特定的混淆与替换操作。
逆初始置换 (Inverse Initial Permutation, IP-1):最终,对每个加密后的分组再进行一次逆置换操作,生成密文。
在块加密算法中,ECB 模式(电子密码本模式)是最简单的,它直接对每个明文块进行加密。但是,由于 ECB 模式存在严重的安全问题:相同的明文块会被加密成相同的密文块,这种模式很容易被攻击者通过分析加密后的模式破解。相比之下,CBC 模式 在每个块加密时引入了前一个密文块的影响,这样即使明文块相同,得到的密文也会不同,从而增加了攻击难度。因此,CBC 模式 被广泛认为是更安全的选择。
3DES(Triple DES)
- 简介:为了解决 DES 的安全问题,提出了 3DES 算法。它相当于对每个数据块进行三次 DES 加密。
- 加密方式:3DES 使用三个 56 位的密钥,执行三次加密/解密。
- 优点:
- 比 DES 安全:增加了加密的强度。
- 缺点:
- 效率较低:由于进行了三次加密操作,3DES 的效率不如 AES。
Blowfish
- 简介:Blowfish 是一种对称加密算法,由 Bruce Schneier 在 1993 年设计,用于替代 DES。
- 加密方式:Blowfish 使用可变长度密钥(32-448 位)进行加密。
- 优点:
- 快速:在软件实现中速度非常快。
- 安全性强:虽然设计时间较早,但至今未被广泛破解。
- 缺点:
- 块大小有限:Blowfish 的块大小为 64 位,较小的块大小在某些应用中可能存在风险。
RC4(Rivest Cipher 4)
- 简介:RC4 是一种流加密算法,广泛应用于 SSL 和 WEP 等协议。
- 加密方式:RC4 是流加密算法,将数据流中的每个字节独立加密。
- 优点:
- 快速且简单:RC4 实现简单,运行速度快。
- 缺点:
- 不再安全:近年来发现了多种漏洞,SSL/TLS 协议已经弃用 RC4。
8.2 非对称加密算法
非对称加密算法使用一对密钥:公钥和私钥。公钥用于加密,私钥用于解密。
RSA(Rivest-Shamir-Adleman)
- 简介:RSA 是一种常用的非对称加密算法,由 Ron Rivest、Adi Shamir 和 Leonard Adleman 于 1977 年提出。
- 加密方式:基于大素数分解问题,使用公钥加密,私钥解密。
- 优点:
- 广泛应用:RSA 是最流行的非对称加密算法,广泛用于 HTTPS、电子签名和数字证书等领域。
- 安全性高:使用较长密钥(如 2048 位或更长)时,RSA 算法的安全性非常高。
- 缺点:
- 计算开销大:相较于对称加密,RSA 的计算效率较低,因此通常只用于加密小数据或用于加密对称密钥。
ECC(Elliptic Curve Cryptography)
- 简介:ECC 是基于椭圆曲线数学的非对称加密算法。相较于 RSA,ECC 在相同安全级别下使用的密钥长度更短。
- 加密方式:使用椭圆曲线的数学性质进行加密。
- 优点:
- 高效:与 RSA 相比,ECC 能在更短的密钥长度下提供相同级别的安全性,适合资源受限的设备(如移动设备)。
- 安全性强:椭圆曲线离散对数问题(ECDLP)尚无有效破解方法。
- 缺点:实现复杂度较高。
DSA(Digital Signature Algorithm)
- 简介:DSA 是一种用于数字签名的算法,由美国国家标准与技术研究院(NIST)制定。
- 加密方式:基于离散对数问题。通常与 SHA-256 等哈希算法结合使用。
- 优点:
- 高效:DSA 的签名速度比 RSA 快,适合数字签名场景。
- 缺点:
- 验证速度慢:相比 RSA,DSA 的签名验证速度较慢。
Diffie-Hellman 密钥交换(DH)
- 简介:Diffie-Hellman 算法用于安全地交换密钥,而不是加密数据。由 Whitfield Diffie 和 Martin Hellman 于 1976 年提出。
- 加密方式:通过数学计算在不传输私钥的情况下生成一个共享密钥。
- 优点:
- 用于密钥交换:可安全地在不可信的网络上生成共享密钥。
- 缺点:
- 无法加密数据:需要与对称加密算法结合使用。
8.3 哈希算法(加密散列函数)
哈希算法不是严格的加密算法,而是一种将任意长度的数据映射到固定长度输出的算法,广泛应用于数据完整性校验和密码学。
SHA-256(Secure Hash Algorithm)
- 简介:SHA-256 是 SHA-2 系列中的一种,输出固定 256 位的哈希值。
- 优点:
- 安全性强:SHA-256 是目前广泛使用且安全性较高的哈希算法。
- 应用广泛:用于密码学、数字签名、证书、区块链(如比特币)等领域。
- 缺点:计算较慢,不适合资源受限的设备。
MD5(Message Digest Algorithm 5)
- 简介:MD5 是一种早期的哈希算法,输出 128 位哈希值。
- 优点:曾经非常流行,计算速度快,适合用于简单的文件校验。
- 缺点:
- 不安全:MD5 存在严重的安全漏洞(碰撞攻击),已被广泛淘汰。
SHA-1
- 简介:SHA-1 是 SHA 系列中的一种,输出 160 位的哈希值。
- 优点:曾广泛应用于数字签名、证书等领域。
- 缺点:
- 已被弃用:SHA-1 已被证明不再安全,现代应用建议使用 SHA-256 或更高级别的哈希算法。