为了防止重放攻击,我们可以使用一种叫做“时间戳+随机数+签名”的方法,其中签名通过密钥进行生成。
以下是一个基本的流程:
- 客户端生成请求参数,生成一个时间戳和随机数。
- 客户端使用特定的密钥和算法(例如SHA256)对请求参数、时间戳和随机数进行签名。
- 客户端将时间戳、随机数和签名一起作为请求参数发送到服务端。
- 服务端接收到请求后,使用同样的密钥和算法对接收到的请求参数、时间戳和随机数进行签名,然后比较这个签名和接收到的签名是否一致。如果一致,说明这个请求是合法的。
- 服务端还需要验证时间戳和随机数,来防止重放攻击。服务端可以保存过去一段时间内接收到的时间戳和随机数,如果接收到的时间戳和随机数在已保存的列表中,则拒绝请求。
预防重放攻击的随机数(Nonce)逻辑
生成的随机数(Nonce)主要是为了增加重放攻击的复杂性,每次请求的nonce应该都是不同的。在一个给定的时间戳内,即使请求参数相同,由于nonce不同,生成的签名也会不同,这样就可以防止重放攻击。
Nonce 的处理逻辑通常如下:
- 客户端生成请求时,创建一个随机数作为nonce。这个随机数应该有足够的长度和复杂性,以防止被轻易猜到。
- 生成签名时,将这个nonce作为签名的一部分,这样即使其他部分(如时间戳和请求参数)相同,由于nonce不同,签名也会不同。
- 将nonce和其他请求参数一起发送到服务端。
- 服务端收到请求后,会将nonce、请求参数和时间戳重新计算签名,并与客户端发送的签名进行比较,以验证请求的有效性。
- 服务端还需要检查nonce是否已经使用过。服务端需要维护一个记录已使用nonce的列表或数据库。如果接收到的nonce已经在这个列表中,那么请求应该被认为是重放的,服务端应该拒绝此请求。
- 如果nonce是新的,那么在处理请求后,应该将其添加到已使用的nonce列表中。为了防止列表过大,可以在nonce过期后从列表中删除。
Nonce表
防重放攻击的Nonce表至少需要两个字段:一个用于存储Nonce值,另一个用于存储Nonce生成或使用的时间戳。
这样在检查Nonce是否已被使用时,然后可以根据时间戳来删除过期的Nonce。
客户端请求代码
下面给出客户端的请求代码,使用python的requests库实现。
import requests
import hashlib
import time
import random
def generate_signature(secret_key, data, timestamp, nonce):
h = hashlib.sha256()
h.update((secret_key + data + str(timestamp) + str(nonce)).encode('utf-8'))
return h.hexdigest()
timestamp = int(time.time())
nonce = random.randint(10000, 99999)
secret_key = 'YourSecretKey'
data = 'YourRequestData'
signature = generate_signature(secret_key, data, timestamp, nonce)
response = requests.post('https://yourwebsite.com/api',
data={'data': data, 'timestamp': timestamp, 'nonce': nonce, 'signature': signature})
服务端处理代码
服务端处理代码使用python的apiflask实现。
from apiflask import APIFlask, input, output, Schema
from flask import request
import hashlib
import time
def generate_signature(secret_key, data, timestamp, nonce):
h = hashlib.sha256()
h.update((secret_key + data + str(timestamp) + str(nonce)).encode('utf-8'))
return h.hexdigest()
# 定义输入格式
class InputSchema(Schema):
data = String()
timestamp = Integer()
nonce = Integer()
signature = String()
app = APIFlask(__name__)
@app.post('/api')
@input(InputSchema)
def check_request():
input_data = request.json
secret_key = 'YourSecretKey'
# 校验时间戳是否在5分钟之内
if abs(int(time.time()) - input_data['timestamp']) > 300:
return {'message': 'Timestamp check failed'}, 400
# 检查 nonce 是否存在数据库中,防止重放攻击
# 如果存在则返回错误,不存在则存入数据库
# 请自行实现相关的数据库操作
# ...
# 校验签名
server_signature = generate_signature(secret_key, input_data['data'], input_data['timestamp'], input_data['nonce'])
if server_signature != input_data['signature']:
return {'message': 'Signature check failed'}, 400
return {'message': 'Request is valid'}