# 信安附件校验问题 ## 1 问题发现 ### 时间 2023 年 1 月 3 日 10:00 ### 截图 ​![](../../../../attachment/images-uuid/3610f4dd6fbc4c4c808b7702584f0a26.png)​ ![](../../../../attachment/images-uuid/e944f6973c7344609d1f6d4569e34385.png) ### 描述 BBOSS 平台请求 IBS 平台**前置校验接口**时出现**信安附件校验不通过**响应,更换附件后依旧报错。 ## 2 问题排查 ### 第一个问题点 根据请求的时间筛选服务器日志。 ![](../../../../attachment/images-uuid/2fdbb271ec3e47cdb527025a8f922169.png) > 背景:目前灰度环境网络策略不支持直接请求对方的 FTP 文件服务器,需在我们自己模拟的服务器上预传文件,供文件拉取接口进行校验。 通过日志首先定位到的问题是拉取动作的 fileName 和预先上传到自己服务器的**文件名不一致**。 与请求人沟通后发现目前文件名使用的是**随机流水号**的方式进行命名,此时导致我们无法预知文件名并提前放到自己服务器进行模拟拉取动作。 ### 第二个问题点 在排查过程中发现,虽然没有拉取到,但也通过了文件的非空校验,位置如下: ​![](../../../../attachment/images-uuid/2b820ac36f68480f8d4dddb61dd85279.png)​ 经排查发现**file**是在**拉取文件后**进行**实体转换存储到库中**并返回 FileInfo 实体。正常逻辑如果拿不到文件应该不执行此位置,返回上层接口为空。 定位库中存储的内容如下,发现是把整个的**响应错误页面**进行了存储,造成无法将 file 判断为空。 ![](../../../../attachment/images-uuid/80df12d94b0b4f7586ef8e9bc29fd4bd.png) ## 3 问题解决 在从文件服务器拉取文件后进行一次文件**魔数校验**,如果校验出来不是正常的文件直接返回空,跳过校验。 > 在识别文件类型时,可以通过文件的后缀来识别的,如.xml、.doc。 使用后缀名识别文件类型不是特别准确。 > > 另外一种识别文件名的方式是: 利用文件的头部信息中的标记,这个标记为魔数。 **获取魔数工具类** ```java private static final int HEADER_LENGTH = 8; private static String getUpperCaseHexString(byte[] file) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < HEADER_LENGTH; i++) { int v = file[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { sb.append(0); } sb.append(hv); } return sb.toString().toUpperCase(Locale.ENGLISH); } ``` **具体实现逻辑** ```java log.info("开始校验是否是非法文件"); List defaultFileHeaderList = new ArrayList<>(); //XLS格式8位魔法数字 defaultFileHeaderList.add("D0CF11E0"); //XLSX格式8位魔法数字 defaultFileHeaderList.add("504B0304"); //获取信安附件文件类型-由文件名获得-fileName不为空,上方有初始化 String fileSuffix = fileName.substring(fileName.lastIndexOf(".") + 1); log.info("由文件名获取得文件类型是{}", fileSuffix); if (!ObjectUtils.isEmpty(bytes)) { //获取实际附件请求头-由附件bytes得到-8位 String fileHead = getUpperCaseHexString(bytes); log.info("由实际附件获取得文件16位校验码是{}", fileHead); //如果实际文件和获取的文件8位校验码不相符则为非法文件。 if (defaultFileHeaderList.stream().noneMatch(fileHead::startsWith)) { log.info("文件不合法,返回空,不进行信安附件校验"); return null; } else if (!ObjectUtils.isEmpty(httpResponse) && !ObjectUtils.isEmpty(bytes)) { //如果MongoDB找不到文件,从远程拉去的文件,保存到附件表 ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes); // 保存文件 FileInfoPO gridFSFile; try { gridFSFile = fileService.storageBackContent(byteIn, fileName); } catch (Exception e) { log.error("文件信息保存到数据库失败{}", e.getMessage(), e); return null; } byteIn.close(); return gridFSFile; } } ``` ## 4 修复后日志 ### Excle 文件存在 (xls) ![](../../../../attachment/images-uuid/695329fdab6f405b8495c64bdd676d9a.png) ### Excle 文件存在 (xlsx) ![](../../../../attachment/images-uuid/8737c2d1c21446d981899f3551f32318.png) ### 文件存在 (DOC) ![](../../../../attachment/images-uuid/6fd3f50217344d63b6b566417defeab0.png) ### 文件存在 (PPT) ![](../../../../attachment/images-uuid/51d98e021ac94e82b1927bb600e34820.png) ### 文件存在 (PDF) ![](../../../../attachment/images-uuid/f3b8ed00351f4946a0b9f773afb605e9.png) ### 文件不存在 ![](../../../../attachment/images-uuid/b4088004da8f4c30a446bc8a2353482b.png) ## 5 测试方法 ### 请求 ``` https://ibs-uops.cdn.10086.cn/sync/BBOSS/PreCheckServ ``` ### Body ```json { "accessToken": "202302031110554c86f028d48f47cd9e994ae892377ca3-0019-1672801855", "busType": "BBSS", "content": { "BusiType": "4", "Domain": [ "20221227001.komect.com" ], "ECID": "100A10020181218697", "ECMail": "182123412342@qq.com", "ECNumber": "18212341234", "InfoSecurityTable": "testxinan.pdf", "OrderType": "1", "ProvID": "000", "TransIdo": "2023010514351897f37e" }, "cutOffDay": "20230105", "domain": "CDNP", "envFlag": "0", "routeType": "00", "routeValue": "995", "sessionID": "30558d2c7eeb41b5969847066287285e", "sign": "52571BEB0DCB9CA4CAB38E0831FF9A1F", "signMethod": "md5", "timeStamp": "20230105143529", "transIDO": "30558d2c7eeb41b5969847066287285e", "userPartyID": "BBSS9980", "version": "1.0.0" } ``` ## 6 修复逻辑 执行 FTP 服务器拉取动作 ​ 拉取后打散为 byte 数组类型 ​ 对 byte 进行文件魔数校验 ​ 如果不符合文件类型则判定为未拉取到文件,返回空不进行内容校验。 ​ 如果符合为文件类型则进行后续的实体封装转换及校验。