最近公司项目老有现场反馈,提交的字符被sql注入干掉了,我接到任务解决一下,过滤请求接口,判断一下是否存在会被屏蔽的字符,然后进行替换,在响应器之中再过滤一下替换过的字符,这样前端显示就没有问题了,在数据库里存储的就是被替换过的字符。
import {intercepts} from './intercepts';
// 请求拦截器
axios.interceptors.request.use((request) => {
return request= intercepts.formatSql(request);
});
// 响应拦截器
axios.interceptors.response.use(
response => {
response.data = intercepts.formatRes(response.data);
return response
}
);
下面是封装的js文件:
const intercepts = {
formatSql: function(request) {
let reg = /select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|drop|execute/;
let regArr = ['select', 'update', 'and', 'or', 'delete', 'insert', 'trancate', 'char', 'into', 'substr', 'ascii', 'declare', 'exec', 'count', 'master', 'drop', 'execute'];
for (let i in request.data) { // 判断data中每一项的数据类型
if (Array.isArray(request.data[i])) { // 为数组项
let isTwoArray = request.data[i].some(items => { // 判断是否为二维数组
return Array.isArray(items);
});
request.data[i].forEach(oneItem => {
if (Object.prototype.toString.call(oneItem) === '[object Object]') { // arr子项为obj
for (let a in oneItem) {
if (reg.test(oneItem[a])) {
oneItem[a] = this.checkSqlString(regArr, oneItem[a]); // 替换字符
}
}
} else { // arr子项为string
if (reg.test(oneItem)) {
oneItem = this.checkSqlString(regArr, oneItem); // 替换字符
}
}
});
if (isTwoArray) {
let twoIndex = request.data[i].findIndex(items => { // 寻找存在二维数组的项的index
return Array.isArray(items);
});
request.data[i][twoIndex].forEach(twoItem => {
if (Object.prototype.toString.call(twoItem) === '[object Object]') { // arr子项为obj
for (let b in twoItem) {
if (reg.test(twoItem[b])) {
twoItem[b] = this.checkSqlString(regArr, twoItem[b]); // 替换字符
}
}
} else { // arr子项为string
if (reg.test(twoItem)) {
twoItem = this.checkSqlString(regArr, twoItem); // 替换字符
}
}
});
}
} else if (Object.prototype.toString.call(request.data[i]) === '[object Object]') { // 为对象类型
for (let c in request.data[i]) {
if (reg.test(request.data[i][c])) {
request.data[i][c] = this.checkSqlString(regArr, request.data[i][c]); // 替换字符
}
}
} else { // 为字符串类型
if (reg.test(request.data[i])) {
request.data[i] = this.checkSqlString(regArr, request.data[i]);
}
}
}
},
formatRes: function(response) {
let str = JSON.stringify(data, (key, val) => typeof val === 'undefined' ? '' : val);
let reg = '`#@@#`';
if (str.indexOf(reg) > -1) {
str = str.replace(/`#@@#`/g, '');
}
return JSON.parse(str);
},
checkSqlString: function (regArr, string) {
regArr.forEach(regItem => {
if (string.indexOf(regItem) !== -1) {
switch (regItem) {
case 'select':
string = string.replace(/select/, 's`#@@#`elect');// 'select'
break;
case 'update':
string = string.replace(/update/, 'u`#@@#`pdate');// 'update'
break;
case 'and':
string = string.replace(/and/, 'a`#@@#`nd');// 'and'
break;
case 'or':
string = string.replace(/or/, 'o`#@@#`r');// 'or'
break;
case 'delete':
string = string.replace(/delete/, 'd`#@@#`elete');// 'delete'
break;
case 'insert':
string = string.replace(/insert/, 'i`#@@#`nsert');// 'insert'
break;
case 'trancate':
string = string.replace(/trancate/, 't`#@@#`rancate');// 'trancate'
break;
case 'char':
string = string.replace(/char/, 'c`#@@#`har');// 'char'
break;
case 'into':
string = string.replace(/into/, 'i`#@@#`nto');// 'into'
break;
case 'substr':
string = string.replace(/substr/, 's`#@@#`ubstr');// 'substr'
break;
case 'ascii':
string = string.replace(/ascii/, 'a`#@@#`scii');// 'ascii'
break;
case 'declare':
string = string.replace(/declare/, 'd`#@@#`eclare');// 'declare'
break;
case 'exec':
string = string.replace(/exec/, 'e`#@@#`xec');// 'exec'
break;
case 'count':
string = string.replace(/count/, 'c`#@@#`ount');// 'count'
break;
case 'master':
string = string.replace(/master/, 'm`#@@#`aster');// 'master'
break;
case 'drop':
string = string.replace(/drop/, 'd`#@@#`rop');// 'drop'
break;
case 'execute':
string = string.replace(/execute/, 'e`#@@#`xecute');// 'execute'
break;
default: break;
};
}
});
return string;
},
};
export {intercepts};
到这里就替换成功了 但是代码量实在是有点多,而且出于性能考虑,也不理想。于是就思考下其中的优化之道。在大佬指点下得到了可以使用 stringify 这个方法,而且只需要对post类型的接口进行请求过滤就可以了。而且stringify 这个方法有个弊端就是 会过滤掉值为 undefined 的 虚值,所以需要考虑到这种情况的出现。
下面是修改后的代码
import {intercept} from './intercept';
// 请求拦截器
axios.interceptors.request.use((request) => {
if (request.method === 'post' && request.data) {
request.data = intercept.formatSql(request.data);
}
return request;
});
// 响应拦截器
axios.interceptors.response.use(
response => {
response.data = intercept.formatRes(response.data);
return response;
}
);
下面是封装到js文件内的方法:
/**
* @description 替换 sql 正则字符串 , 添加 `#@@#` 至字符串 第一位。.
* formatSql 在拦截器中定位添加.
* formatRes 在响应器中拦截删除.
* @Date 2021-11-09
*/
const intercept = {
// sql注入
formatSql: function(data) {
let str = JSON.stringify(data);
let reg = /\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|drop|execute)\b/g;
let res = str.match(reg);
if (res && res.length) {
let list = Array.from(new Set(res));
for (let i = 0; i < list.length; i++) {
switch (list[i]) {
case 'select': str = str.replace(/\b(select)\b/g, 'se`#@@#`lect'); break;
case 'update': str = str.replace(/\b(update)\b/g, 'up`#@@#`date'); break;
case 'and': str = str.replace(/\b(and)\b/g, 'an`#@@#`d'); break;
case 'or': str = str.replace(/\b(or)\b/g, 'o`#@@#`r'); break;
case 'delete': str = str.replace(/\b(delete)\b/g, 'de`#@@#`lete'); break;
case 'insert': str = str.replace(/\b(insert)\b/g, 'in`#@@#`sert'); break;
case 'trancate': str = str.replace(/\b(trancate)\b/g, 'tra`#@@#`ncate'); break;
case 'char': str = str.replace(/\b(char)\b/g, 'ch`#@@#`ar'); break;
case 'into': str = str.replace(/\b(into)\b/g, 'i`#@@#`nto'); break;
case 'substr': str = str.replace(/\b(substr)\b/g, 'su`#@@#`bstr'); break;
case 'ascii': str = str.replace(/\b(ascii)\b/g, 'as`#@@#`cii'); break;
case 'declare': str = str.replace(/\b(declare)\b/g, 'dec`#@@#`lare'); break;
case 'exec': str = str.replace(/\b(exec)\b/g, 'ex`#@@#`ec'); break;
case 'count': str = str.replace(/\b(count)\b/g, 'co`#@@#`unt'); break;
case 'master': str = str.replace(/\b(master)\b/g, 'ma`#@@#`ster'); break;
case 'drop': str = str.replace(/\b(drop)\b/g, 'dr`#@@#`op'); break;
case 'execute': str = str.replace(/\b(execute)\b/g, 'ex`#@@#`ecute'); break;
default: break;
}
}
}
return JSON.parse(str);
},
formatRes: function(data) {
let str = JSON.stringify(data, (key, val) => typeof val === 'undefined' ? '' : val);
let reg = '`#@@#`';
if (str.indexOf(reg) > -1) {
str = str.replace(/`#@@#`/g, '');
}
return JSON.parse(str);
}
};
export {intercept};
到此就解决啦,但是还有细节可以优化,可以将 switch 进行优化,使用变量来定义正则,然后通过截取字符串进行拼接的方式进行优化,可以节约很多的代码量,并且对异常情况进行了处理,尽可能规避掉。
优化后:
/**
* @description 替换 sql 正则字符串 , 添加 `#@@#` 至字符串 第一位。.
* formatSql 在拦截器中定位添加.
* formatRes 在响应器中拦截删除.
* @Date 2021-11-09
*/
const intercept = {
// sql注入
formatSql: function(data) {
let str = JSON.stringify(data);
let reg = /\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|drop|execute)\b/g;
let res = str.match(reg);
if (res && res.length) {
let list = Array.from(new Set(res));
for (let i = 0; i < list.length; i++) {
let sqlReg = new RegExp('\\b(' + list[i] + ')\\b', 'g');
str = str.replace(sqlReg, list[i].substr(0, 1) + '`#@@#`' + list[i].substr(1));
}
return JSON.parse(str);
} else {
return data;
}
},
formatRes: function(response) {
let str = JSON.stringify(response, (key, val) => typeof val === 'undefined' ? '' : val);
let reg = '`#@@#`';
if (str.indexOf(reg) > -1) {
str = str.replace(/`#@@#`/g, '');
return JSON.parse(str);
} else {
return response;
}
}
};
export {intercept};
到这里就圆满结束啦,感谢观看!(多谢丁大佬的指导)。