在线、离线激活鉴权实战

时间:2022-07-25
本文章向大家介绍在线、离线激活鉴权实战,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

任务目标

实现一个客户端通过给与的指定激活码,激活仅限当前机器使用具体软件的功能。客户端可能处于能连接互联网无法连接互联网两种情况。同时均要实现在指定时间使权限过期的功能,激活码在使用时才开始计时。

在线激活模式

这个就非常简单了,使用最简单的哈希加盐就可以实现了,与正常 REST 接口的哈希加盐验证只有,验证服务器端需要预先存好设置的激活码这一步。

激活码有效期就通过一个取巧的方法,将有效期的秒数转换成16进制,埋藏在序列号的指定段上。

在将当前时间和过期时间一起加盐,实现用户无法通过修改或锁定系统时间破解我们的系统。

当然,激活后,也要每隔一段时间验证一下是否过期,并且同时更新验证的时间戳,防止用户修改或锁定系统时间。

可以将鉴权放在请求前检验登录情况的装饰器里,每次更新只更新到flush里,在其他操作commit的时候再更新到数据库,防止一个请求里多个commit会导致数据无法回滚。

比较有风险的一点就是,用户可能会抓包服务端发到客户端的鉴权结果请求,然后伪造成功信息实现越过真实的鉴权结果,所以我们要设置一些比较特殊的字符串成功信息的字符串。或者可以实现类似银行电子狗,客户端-服务端均会随时间产生的相同动态加密校验码,与成功或失败信息加密后,再返回给客户端,保证中间过程即时被抓包,也无法破解。以下伪代码不考虑此过程

# 激活部分client伪代码

def online_active():
	# 前端传递激活码
	serial_number = form.serial_number.data

	# 获取本机硬件信息
	device_info = get_device_info()

	# 盐
	secret = config.secret

	# 记录激活码过期时间

	expire_time = int(serial_number[6:], 16) * (60 * 60 * 24)
	expire_timestamp = now_timestamp + expire_time

	# 硬件信息加盐生成的指纹
	signature = md5(device_info, expire_timestamp, now_timestamp, secret)

	# 向激活服务器发起验证请求
	if verify_license(serial_number, device_info, expire_timestamp, now_timestamp, signature):

		# 写入数据库
		insert_active()
		return True
	else:
		return False
# 激活部分server伪代码

def verify_license(serial_number, device_info, expire_timestamp, now_timestamp, signature):

	# 验证是否存在对应的激活码
	if serial_number in db:
		# 与客户端一致,只要验证指纹是否一致就行了
		if signature == md5(device_info, expire_timestamp, now_timestamp, secret):
			return True

	return False
# 客户端检测权限是否仍然有效
def is_active():
	# 验证激活设备是否改变,同时验证了激活时间是否被篡改

	timestamp_or_false = is_active_equipment()

	if not expire_timestamp_or_false:
		return False

	# 获取下面每次鉴权写入数据库的时间last_timestamp,防止用户篡改系统时间,躲避过期机制
	expire_timestamp, last_timestamp = expire_timestamp_or_false

	# 是否因为超过过期时间而过期
	if now_time > expire_timestamp:
		return False

	# now_time 为当前系统时间
	# 防止用户锁定时间
	if last_timestamp >= now_time:
		return False

	# 更新数据库中的now_timestamp并重新生成指纹
	now_timestamp = now_time
	signature = md5(something)

	# 写入数据库
	insert_active()
	return True



# 客户端检测是否设备是否改变

def is_active_equipment():
	# 其实就是重复哈希加盐鉴权的过程,只不过是将验证客户端指纹、验证服务器指纹改为了,
	# 客户端指纹、客户端数据库指纹鉴权了

	db_signature, serial_number, expire_timestamp, now_timestamp = get_db_signature()

	# 获取本机硬件信息
	device_info = get_device_info()

	# 盐
	secret = config.secret

	# 硬件信息加盐生成的指纹
	signature = md5(device_info, expire_timestamp, now_timestamp, secret)

	if signature == db_signature:
		return expire_timestamp, now_timestamp
	return False

离线激活模式

这里逻辑就比较恶心,但是还是类似上面在线激活的思想。只不过从客户端-激活服务器改为了客户端-手机-激活服务器

客户端-手机这一部分通过扫描二维码,自动跳转网址,给激活服务器发起请求实现。

具体流程(非常绕!)就是,在客户端输入激活码,客户端生成一个携带设备等校验信息的二维码和一个用于校验的校验码,返回给前端的只有二维码,用户扫描二维码向激活服务器发起校验,如果校验成功,则生成一个和客户端一样的校验码返回到用户手机上,用户将该校验码再次输到客户端里。客户端自己生成的校验码和用户输入的校验码一致,则激活成功。如果校验失败,那向用户手机直接发送失败信息。

需要保证每次不同终端客户端-手机-服务端服务端-手机-客户端生成的校验码签名均是计算出来的,而不能存起来。

过期部分还是同在线激活方式相同,以下伪代码不做赘述。

与在线激活方式遇到的中间人攻击类似,这种方式最好在客户端-手机手机-服务器这两个过程中加密信息,但是这篇文章讨论的是两种激活模式的业务逻辑实现,不涉及具体实现。

# 客户端输入激活码,使用请求地址和参数生成二维码

def generate_qr_code():
	# 前端传递激活码
	serial_number = form.serial_number.data

	# 获取本机硬件信息
	device_info = get_device_info()

	# 盐
	secret = config.secret

	# 记录激活码过期时间

	expire_time = int(serial_number[6:], 16) * (60 * 60 * 24)
	expire_timestamp = now_timestamp + expire_time

	# 硬件信息加盐生成的指纹
	signature = md5(device_info, expire_timestamp, now_timestamp, secret)

	# 生成校验码
	pin_code = someting_Encrypt()

	# 激活服务器的激活接口
	request_url = "aaa.com/bbbb"

	return request_url, 上面的相关信息
# 用户手机向验证服务器发起请求,返回pin_code

def offline_active():
	# 鉴权,与在线激活同样
	verify_license()

	# 生成校验码
	pin_code = someting_Encrypt()

	return pin_code
# 客户端验证pin_code

def verify_pin():
	# 获取前端发送过来的pin_code
	pin_code = form.pin_code.data

	# 生成校验码
	if pin_code == someting_Encrypt():
		return True
	return False