# 内容安全
# 微信安全检测
微信官方提供了可疑用户与危险接口的检查:
接口安全扫描:
这两个功能在后台即可操作,平坦可以交由运营小姐姐来进行操作一下。
# 第三方内容安全推荐
各家云服务商:云盾 (opens new window) (阿里),易盾 (opens new window)(网易),天御 (opens new window)(腾讯)
# 文本内容安全
# 整体思路
- 下载网络上的一些敏感词的数据库,使用正则进行匹配过滤一次,减少成本;
- 使用官方的限额的api对文本进行查验;
- 使用第三方的api对内容进行查验;
# 官方介绍
检查一段文本是否含有违法违规内容,下面介绍的接口:官方链接 (opens new window)
1.0 版本接口文档【点击查看】 (opens new window)
应用场景举例:
- 用户个人资料违规文字检测;
- 媒体新闻类用户发表文章,评论内容检测;
- 游戏类用户编辑上传的素材(如答题类小游戏用户上传的问题及答案)检测等。 频率限制:单个 appId 调用上限为 4000 次/分钟,2,000,000 次/天*
调用方式:
# 工具js封装
请求地址
POST https://api.weixin.qq.com/wxa/msg_sec_check?access_token=ACCESS_TOKEN
请求参数
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
access_token / cloudbase_access_token | string | 是 | 接口调用凭证 (opens new window) | |
version | string | 是 | 接口版本号,2.0版本为固定值2 | |
openid | string | 是 | 用户的openid(用户需在近两小时访问过小程序) | |
scene | number | 是 | 场景枚举值(1 资料;2 评论;3 论坛;4 社交日志) | |
content | string | 是 | 需检测的文本内容,文本字数的上限为2500字 | |
nickname | string | 否 | 用户昵称 | |
title | string | 否 | 文本标题 | |
signature | string | 否 | 个性签名,该参数仅在资料类场景有效(scene=1) |
返回的 JSON 数据包
属性 | 类型 | 说明 |
---|---|---|
errcode | number | 错误码 |
errmsg | string | 错误信息 |
trace_id | string | 唯一请求标识,标记单次请求 |
result | object | 综合结果 |
detail | array | 详细检测结果 |
逻辑:
- 用户只用传文本内容,其他的openid等信息可以不传,设置一个默认值,方便其他的平台使用;
- 使用正则匹配掉一些无用的内容;
- 长度判断,分2500词进行多次判断;
- 判断结果非pass的全部进入risky部分;
// 文本内容安全
// content - 内容
// title - 标题 可选
// signature - 签名 也是可选
export const wxMsgCheck = async (content, {
user: {
openid,
name: nickname,
remark: signature
},
scene,
title
} = {
user: {},
scene: 3,
title: ''
}) => {
// POST https://api.weixin.qq.com/wxa/msg_sec_check?access_token=ACCESS_TOKEN
let accessToken = await wxGetAccessToken()
let res
try {
// 1.过滤掉一些如Html,自定义的标签内容
content = content.replace(/<[^>]+>/g, '').replace(/\sface\[\S{1,}]/g, '').replace(/img\[\S+\]/g, '').replace(/\sa\(\S+\]/g, '').replace(/\[\/?quote\]/g, '').replace(/\[\/?pre\]/g, '').replace(/\[\/?hr\]/g, '').replace(/[\r\n|\n|\s]/g, '')
// 2.如果content内容超过了2500词,需要进行分段处理
if (content.length > 2500) {
// 分段 —> arr -> method1: for , method2: reg
let arr = content.match(/[\s\S]{1,2500}/g) || []
// 多次请求接口
let mulResult = []
for (let i = 0; i < arr.length; i++) {
// 获取所有接口的返回结果 -> 结果判断 -> 返回
res = await instance.post(`https://api.weixin.qq.com/wxa/msg_sec_check?access_token=${accessToken}`, {
version: 2,
openid: openid || 'ooTjn5YPpogMWLtEQ_PxyUJkIp2I',
scene,
content: arr[i],
nickname: nickname,
title,
signature: scene === 1 ? signature : null
})
mulResult.push(res)
}
// 判断mulResult
console.log(mulResult)
const arrTemp = mulResult.filter(item => {
const { status, data: { errcode, result } } = item
return status !== 200 || errcode !== 0 || (result && result.suggest !== 'pass')
})
return !(arrTemp.length > 0)
} else {
res = await instance.post(`https://api.weixin.qq.com/wxa/msg_sec_check?access_token=${accessToken}`, {
version: 2,
openid: openid || 'ooTjn5YPpogMWLtEQ_PxyUJkIp2I',
scene,
content,
nickname: nickname,
title,
signature: scene === 1 ? signature : null
})
const { status, data: { errcode, result } } = res
return status === 200 && errcode === 0 && result && result.suggest === 'pass'
}
} catch (error) {
logger.error(`wxMsgCheck error: ${error.message}`)
}
}
# 自定义安全敏感词汇
在小程序管理后台,开发管理 -> 安全中心 -> 内容风控:
在这里添加的分值越高,越可能被屏蔽。
# 测试接口工具js
加入accessToken失效判断
instance.interceptors.response.use(async (res) => { const { data } = res if (data.errcode === 40001) { // 重新获取新的accessToken const accessToken = await wxGetAccessToken(true) const { url } = res.config // 重新发起请求 -> res if (url.indexOf('access_token') !== -1) { const arr = url.split('?') // ?key=value&key1=value1... -> ['域名', 'key=value&key1=value1...'] const params = qs.parse(arr[1]) const newParams = { ...params, access_token: accessToken } const newUrl = arr[0] + '?' + qs.stringify(newParams) const config = { ...res.config, url: newUrl } const result = await axios(config) return result } } return res })
有风险的词汇:
# 图片内容安全
# 整体思路
- 图片上传之后,创建tmp目录(使用make-dir (opens new window)),获取图片的基本信息;
- 判断图片的尺寸,如果超过了微信接口的分辨率要求750x1336,那需要使用sharp (opens new window)来进行压缩;
- nodejs侧对接微信接口需要使用formData数据类型,所以还需要安装form-data (opens new window)
- 最后还需要删除临时文件,使用del (opens new window)库,删除之前使用fs.access (opens new window)进行判断
# 官方介绍
请求地址
POST https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN
请求参数
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
access_token / cloudbase_access_token | string | 是 | 接口调用凭证 (opens new window) | |
media | FormData | 是 | 要检测的图片文件,格式支持PNG、JPEG、JPG、GIF,图片尺寸不超过 750px x 1334px |
返回的 JSON 数据包
属性 | 类型 | 说明 |
---|---|---|
errcode | number | 错误码 |
errmsg | string | 错误信息 |
errcode 的合法值
值 | 说明 | 最低版本 |
---|---|---|
0 | 内容正常 | |
87014 | 内容可能潜在风险 |
# 工具js封装
安装依赖:
npm i make-dir form-date sharp del
其中sharp (opens new window)需要配置加速:
npm config set sharp_binary_host "https://npm.taobao.org/mirrors/sharp"
npm config set sharp_libvips_binary_host "https://npm.taobao.org/mirrors/sharp-libvips"
工具js:
// 获取头部属性
export const getHeaders = (form) => {
return new Promise((resolve, reject) => {
form.getLength((err, length) => {
if (err) {
reject(err)
}
const headers = Object.assign({
'Content-Length': length
}, form.getHeaders())
resolve(headers)
})
})
}
// 删除文件
export const checkAndDelFile = async (path) => {
try {
accessSync(path, constants.R_OK | constants.W_OK)
await del(path)
} catch (err) {
// console.error('no access!')
}
}
// 图片内容安全
export const wxImgCheck = async (file) => {
// POST https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN
const accessToken = await wxGetAccessToken()
// 1.保证图片 -> 判断分辨率 -> sharp 750 * 1334
let newPath = file.path
const tmpPath = path.resolve('./tmp')
try {
const img = sharp(newPath)
const meta = await img.metadata()
if (meta.width > 750 || meta.height > 1334) {
// 判断临时路径是否存在,并创建
await mkdir(tmpPath)
// uuid -> 指定临时的文件名称
newPath = path.join(tmpPath, uuidv4() + path.extname(newPath) || '.jpg')
await img.resize(750, 1334, {
fit: 'inside'
}).toFile(newPath)
}
const stream = fs.createReadStream(newPath)
// 2.FormData类型的数据准备
const form = new FormData()
form.append('media', stream)
const headers = await getHeaders(form)
// 3.请求接口 -> 返回结果
const result = await instance.post(`https://api.weixin.qq.com/wxa/img_sec_check?access_token=${accessToken}`, form, { headers })
// 校验成功 -> 删除tmp数据 -> 判断路径中的文件是否存在
console.log('🚀 ~ file: WxUtils.js ~ line 232 ~ wxImgCheck ~ result', result)
await checkAndDelFile(newPath)
return result.status === 200 && result.data && result.data.errcode === 0
// if (result.status === 200 && result.data && result.data.errcode === 0) {
// // errcode 0 - 内容正常,否则 - 异常
// return true
// } else {
// return false
// }
} catch (error) {
await checkAndDelFile(newPath)
logger.error(`wxImgCheck error: ${error.message}`)
}
}
# 测试接口工具js
注意:
- 自行找一下网上可疑的图片进行测试;
- errcode为0说明校验通过,反之不通过;
- 通过之后,删除图片临时目录中的文件;
- 一般上传图片的接口需要对接云上的存储,所以采用了本地缓存的方式校验图片;