通用接口开放平台设计与实现——(35)消息服务之安全增强
时间:2022-10-15 08:30:00
我们对新闻服务的整体设计是在客户端连接到服务端后,发起身份认证,携带应用代码和密钥,并对密钥进行处理hash通过加密和服务端认证后,建立长连接。
对于长连接,我们认为它是安全的,恶意用户不能直接向长连接通道发送伪造信息,因此没有必要签署和检查每个信息,以提高性能。但这里还有一个小漏洞,未经认证的应用程序被阻止了。如何处理认证的应用程序?
例如,物流系统LMS与两家承运商对接TMS系统,如果A承运人系统发送消息确认,但消息中携带的应用代码为B,实际发生这种情况的可能性不大,但仍存在一定的隐患。
这个问题的关键在于识别应用程序和使用信息中携带的应用程序代码,而不是登录认证后连接中保存的应用程序代码。
有两种方案,一种是在使用应用程序代码的地方获得成功登录时保存的应用程序识别值,另一种是验证客户端信息携带的应用程序代码是否与通道中的应用程序代码一致。
综合考虑,方案1从通道中获取应用代码。在许多使用应用代码的方法中,需要获得额外的通道对象,这更麻烦;方案2更方便,只做一点变化,所以我们使用方案2.
相关改造如下:
因为只要服务端需要验证应用代码是否与消息通道一致,就不能放在客户端和服务端的基本数据验证处理中,而是放在服务端MessageHandler在应用验证环节中,比较新闻中携带的应用代码是否与通道对应的应用代码一致。如果不一致,则抛出异常。同时记录错误的日志,可以追溯到对接系统是否试图使用他人的应用标志发送信息。
/** * 验证应用 * * @param publishAppCode 消息应用编码 * @param appCode 通道应用编码 */ protected void validateAppCode(String publishAppCode,String appCode) {
///这个地方需要正确appCode判空,如登录操作、通道等appCode还不存在 if(appCode!=null && !appCode.equals(publishAppCode)){
log.error("应用标识与消息通道不一致,应用标识:{},消息通道{}",publishAppCode,appCode); throw new MessageException("S201", "应用标识与消息通道不一致"); } try {
ApiApp app =apiAppService.getByCode(publishAppCode); if(app.getStatus().equals(StatusEnum.DEAD.name())){
throw new MessageException("S203", "应用被停用"); } }catch (Exception ex){
throw new MessageException("S202", "应用标识无效");
}
}
通道的应用标识如何获取呢?我们改造下全局容器,新增一个有channel对象获取appCode的方法
/** * 根据应用编码获取相应通道 * @param channel */
public static String getAppCode(Channel channel) {
//从channel属性中读取到用户标识
AttributeKey<String> appCodeAttribute=AttributeKey.valueOf(APP_CODE_KEY);
return channel.attr(appCodeAttribute).get();
}
然后在请求/响应消息处理器中,调用全局容器的方法,获取到appCode,传入到框架验证环节
/** * 消息处理 * * @param message 消息 * @param channel 通道 */
public void handleMessage(RequestMessage requestMessage, Channel channel) {
// 记录消息请求日志
apiMessageLogService.createRequestPart(requestMessage);
//验证框架
String appCode = MessageServerHolder.getAppCode(channel);
validateFramework(requestMessage,appCode);
//将请求消息状态默认设置为无需发送
apiMessageLogService.updateStatus(MessageStatusEnum.NOT_TO_REQUEST.name(),requestMessage.getId());
//特殊处理
messageOperation(requestMessage, channel);
//发送响应至消息发送者
sendResponse(requestMessage, channel);
//消息处理(复制及转发)
if(isNeedRepost()){
repostMessage(requestMessage.getTopic(),requestMessage.getContent());
}
}
改造完成,进行测试,故意造一条不一致的数据,服务端会打印错误日志:
应用标识与消息通道不一致,应用标识:ABC,消息通道SCS
同时响应客户端一条错误消息
{
"errorCode":"500","errorMessage":"应用标识与消息通道不一致",
"id":"1489404728953479170","messageType":"RESPONSE","publishAppCode":"MessageServer",
"publishTime":"2022-02-04 09:05:33","requestMessageId":"1489404726461988866","result":"ERROR",
"topic":"framework.error.response"}