最近接触了一个react-native的项目,在浏览底层代码的时候发现他们的上传是直连oss的,并不像react/vue项目掉用后端接口去做的。这样就会有一个问题:如果用户手动修改了文件的后缀名那么还是能直接上传上去。这对项目的安全有一定的危害⚠️,并且像一切大型及保密性高的项目,他们会有不定时的风险漏洞扫描。

所以我翻阅了很多资料,但是并没有找到一个比较安全的npm包来使用。所以我尝试自己写了一套,欢迎大家评论沟通。

正片开始:

首先我们要用到两个npm包

import RNFS from 'react-native-fs' 
import base64 from 'base-64'

react-native-fs是来读文件的

base64是用来转base64字符串的

先定义了对应格式的16进制编码

const FileTypeCode = {
  jpg: 'FFD8FF',
  JPG: 'FFD8FF',
  png: '89504E47',
  PNG: '89504E47',
  gif: '47494638',
  GIF: '47494638',
  tif: '49492A00',
  TIF: '49492A00',
  bmp: '424D',
  BMP: '424D',
  dwg: '41433130',
  DWG: '41433130',
  html: '68746D6C3E',
  HTML: '68746D6C3E',
  rtf: '7B5C727466',
  RTF: '7B5C727466',
  xml: '3C3F786D6C',
  XML: '3C3F786D6C',
  zip: '504B0304',
  ZIP: '504B0304',
  rar: '52617221',
  RAR: '52617221',
  psd: '38425053',
  PSD: '38425053',
  eml: '44656C69766572792E3C',
  EML: '44656C69766572792E3C',
  dbx: 'CFAD12FEC5FD746F',
  DBX: 'CFAD12FEC5FD746F',
  pst: '2142444E',
  PST: '2142444E',
  xls: 'D0CF11E0',
  XLS: 'D0CF11E0',
  doc: 'D0CF11E0',
  DOC: 'D0CF11E0',
  mdb: '5374616E64617264204A',
  MDB: '5374616E64617264204A',
  wpd: 'FF575043',
  WPD: 'FF575043',
  eps: '252150532D41646F6265',
  EPS: '252150532D41646F6265',
  ps: '252150532D41646F6265',
  PS: '252150532D41646F6265',
  pdf: '255044462D312E',
  PDF: '255044462D312E',
  pwl: 'E3828596',
  PWL: 'E3828596',
  wav: '57415645',
  WAV: '57415645',
  avi: '41564920',
  AVI: '41564920',
  ram: '2E7261FD',
  RAM: '2E7261FD',
  rm: '2E524D46',
  RM: '2E524D46',
  mpg: '000001BA',
  MPG: '000001BA',
  mov: '6D6F6F76',
  MOV: '6D6F6F76',
  asf: '3026B2758E66CF11',
  ASF: '3026B2758E66CF11',
  mid: '4D546864',
  MID: '4D546864',
  jpeg: 'FFD8FFE0',
  JPEG: 'FFD8FFE0',
  mp4: '00000020667479706D70',
  MP4: '00000020667479706D70',
  mp3: '49443303000000002176',
  MP3: '49443303000000002176',
}

我们使用RNFS读取文件并转化为base64字符串,计算MD5的值

这里我是截取的前30位字符串来进行对比的,我们计算出来的MD5的值是一大串字符串,但是这里不可以使用包含来进行对比,MD5的值的开头就是这个文件原本格式对应的编码

但是我们不知道他有多少位(提前规定有点不现实了哈),所以我们直接去FileType,对应的值都是【image/jpg】... 这是图片的,文件的同样,所以我们可以直接截取 / 后面的,然后去我们定义的FileTypeCode里面找到对应的编码,最后和我们计算出来的MD5的值进行开头比较。

如果一样,那么他就是没有修改后缀名,相反,他的后缀名就并不是真实的

这个逻辑走完之后,我们就避免了一个相对来说比较大的安全性的问题

RNFS.readFile(fileUrl, 'base64')
    .then(res => {
      try {
        const binaryString = base64.decode(res)
        const head30 = binaryString.slice(0, 30)
        let hexString = ''
        for (let i = 0; i < head30.length; i++) {
          const hex = head30.charCodeAt(i).toString(16).padStart(2, '0')
          hexString += hex
        }
        const fileType = Info && Info.type ? Info.type.split('/')[1] : ''
        const expectedPrefix = (
          FileTypeCode && FileTypeCode[fileType]
            ? String(FileTypeCode[fileType])
            : ''
        ).toLowerCase()
        const isMatch =
          expectedPrefix && hexString.toLowerCase().startsWith(expectedPrefix)
        if (!isMatch) {
          Toast.info('请勿修改文件后缀')
          return
        }

        
      } catch (e) {
        console.error('Base64 转 16 进制失败:', e)
      }
    })
    .catch(err => {
      console.error(err.message, err.code)
    })

写完发现自己的组织能力有待提升😭(感觉像程序员的通病啊😭😭😭),欢迎大家给予批评、讨论!

更多推荐