1.互联网Api接口到底如何保证安全性问题?
2.代码落地实战防御XSS、CSRF攻击
3.代码落地如何防御接口数据被黑客抓包篡改?
4.接口数据加密对称还是非对称加密好
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。 [1]
脚本攻击:利用JavaScript 注入 到后台数据库中,在通过展示数据加载该脚本 该脚本中(
1.使用js获取cookie信息(jwt)
2.将该jwt数据 上传黑客服务器(ajax)
)
获取jwt---用户会话信息 让后模拟请求形式使用该jwt登录。
xss攻击典型网站:论坛、评论区
http://127.0.0.1:8080/getUserInfo?userName=
http://127.0.0.1:8080/getUserInfo?userName=
前端传递 js 脚本到服务器端
{
"channel": "",
"equipment": "",
"password": "123456",
"phoneNumber": "15921009758"
}
后端接口将该脚本存放数据库中
package com.mayikt.main.api.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mayikt.common.core.api.BaseApiService;
import com.mayikt.common.core.api.BaseResponse;
import com.mayikt.main.api.UserLoginLogService;
import com.mayikt.main.api.dto.res.UserLoginLogResDto;
import com.mayikt.main.api.impl.entity.SysUserLoginLog;
import com.mayikt.main.api.impl.mapper.SysUserLoginLogMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @author 余胜军
* @ClassName UserLoginLogImpl
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@RestController
@CrossOrigin
public class UserLoginLogImpl extends BaseApiService> implements UserLoginLogService {
@Autowired
private SysUserLoginLogMapper sysUserLoginLogMapper;
@Override
public BaseResponse> getUserLoginLog() {
// 模拟查询登录的日志记录
List sysUserLoginLogs = sysUserLoginLogMapper.selectList(new QueryWrapper<>());
List userLoginLogResDtos = new ArrayList<>();
for (int i = 0; i < sysUserLoginLogs.size(); i++) {
UserLoginLogResDto userLoginLogResDto = new UserLoginLogResDto();
BeanUtils.copyProperties(sysUserLoginLogs.get(i), userLoginLogResDto);
userLoginLogResDtos.add(userLoginLogResDto);
}
return setResultSuccessData(userLoginLogResDtos);
}
}
package com.mayikt.main.api;
import com.mayikt.common.core.api.BaseResponse;
import com.mayikt.main.api.dto.res.UserLoginLogResDto;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
/**
* @author 余胜军
* @ClassName UserLoginLogService
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public interface UserLoginLogService {
@GetMapping("/getUserLoginLog")
BaseResponse> getUserLoginLog();
}
前端html
xss模拟攻击
将用户前端所提交的参数进行过滤。
html 大于> 小于号 <
<>
String equipment = loginUserReqDto.getEquipment();
loginUserReqDto.setEquipment(StringUtils.isEmpty(equipment) ? null : StringEscapeUtils.escapeHtml(equipment));
SysUserLoginLog sysUserLoginLog = new SysUserLoginLog(sysUser.getId(), IPUtils.getIpAddr(request),
new Date(), token,
loginUserReqDto.getChannel(), loginUserReqDto.getEquipment());
该方式的缺陷:每个参数都需要像这样写 代码非常冗余
String str = "";
String s = StringEscapeUtils.escapeHtml(str);
System.out.println(s);
接口接受参数 ?传递参数形式---
传递参数都是json数据形式
spring mvc 接受 json数据提供 api回调
package com.mayikt.main.security;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.common.net.MediaType;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
/**
* 解决post 请求传递json数据 防御xss攻击
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Override
protected void extendMessageConverters(List> messageConverters) {
/**
* 替换默认的MappingJackson2HttpMessageConverter,过滤(json请求参数)xss
*/
ListIterator> listIterator = messageConverters.listIterator();
while (listIterator.hasNext()) {
HttpMessageConverter<?> next = listIterator.next();
if (next instanceof MappingJackson2HttpMessageConverter) {
listIterator.remove();
break;
}
}
messageConverters.add(getMappingJackson2HttpMessageConverter());
}
public MappingJackson2HttpMessageConverter getMappingJackson2HttpMessageConverter() {
// 创建自定义ObjectMapper
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, new JsonHtmlXssDeserializer(String.class));
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.getApplicationContext()).build();
objectMapper.registerModule(module);
// 创建自定义消息转换器
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
return mappingJackson2HttpMessageConverter;
}
}
/**
* 对入参的json进行转义
*/
class JsonHtmlXssDeserializer extends JsonDeserializer {
public JsonHtmlXssDeserializer(Class string) {
super();
}
@Override
public Class handledType() {
return String.class;
}
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
String value = jsonParser.getValueAsString();
if (!StringUtils.isEmpty(value)) {
return StringEscapeUtils.escapeHtml(value);
}
return value;
}
}
1.可以使用第三方抓包工具,对请求前后实现代理,可以修改参数请求内容和参数响应内容,抓包工具http调试工具
2.Fiddler4下载地址:https://pc.qq.com/detail/10/detail_3330.html
使用Fiddler4篡改请求之前:
使用MD5可以直接验证签名参数 MD5 属于单向加密,只能够暴力破解。
MD5应用场景 在nacos分布式配置中心中,使用MD5 比对文件内容是否发生改变
HasherPro比对文件内容是否发生改变。
MD5在线暴力破解地址:https://www.cmd5.com/
String userName="123456";
System.out.println( DigestUtils.md5Hex(userName));
黑客如何破解?自己需要根据参数内容 生成签名
如果只是改了参数内容---没有用的 所以我们需要该签名
{"password":"123456","phoneNumber":"phoneNumber","channel":"安卓","equipment":""}
{sign=325ab041d4889825a46d1e1e802ab5de, timestamp=1652537015771}
package com.mayikt.main.security;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
/**
* 参数验证签名
*/
public class SignUtil {
private static Logger logger = LoggerFactory.getLogger(SignUtil.class);
/**
* 加密密钥
*/
private final static String APP_KEY = "mykey123456";
public final static String SECRET_KEY = "mysecret123456";
/**
* 字符编码
*/
private final static String INPUT_CHARSET = "UTF-8";
/**
* 超时时间
*/
private final static int TIME_OUT = 30 * 60 * 1000;
/**
* 请求参数Map转换验证Map
*
* @param requestParams 请求参数Map
* @param charset 是否要转utf8编码
* @return
* @throws UnsupportedEncodingException
*/
public static Map toVerifyMap(Map requestParams, boolean charset) {
Map params = new HashMap<>();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
// 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
if (charset)
valueStr = getContentString(valueStr, INPUT_CHARSET);
params.put(name, valueStr);
}
return params;
}
/**
* 除去数组中的空值和签名参数
*
* @param sArray 签名参数组
* @return 去掉空值与签名参数后的新签名参数组
*/
public static Map paraFilter(Map sArray) {
Map result = new HashMap<>();
if (sArray == null || sArray.size() <= 0) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("") || key.equalsIgnoreCase("sign")) {
continue;
}
result.put(key, value);
}
return result;
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
*
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map params) {
return createLinkString(params, false);
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
*
* @param params 需要排序并参与字符拼接的参数组
* @param encode 是否需要UrlEncode
* @return 拼接后字符串
*/
public static String createLinkString(Map params, boolean encode) {
List keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (encode)
value = urlEncode(value, INPUT_CHARSET);
if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
/**
* 编码转换
*
* @param content
* @param charset
* @return
* @throws UnsupportedEncodingException
*/
private static byte[] getContentBytes(String content, String charset) {
if (charset == null || "".equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
/**
* 编码转换
*
* @param content
* @param charset
* @return
*/
private static String getContentString(String content, String charset) {
if (charset == null || "".equals(charset)) {
return new String(content.getBytes());
}
try {
return new String(content.getBytes("ISO-8859-1"), charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
/**
* URL转码
*
* @param content
* @param charset
* @return
*/
private static String urlEncode(String content, String charset) {
try {
return URLEncoder.encode(content, charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
/**
* 生成要请求的签名参数数组
*
* @return 要请求的签名参数数组
*/
public static Map signJson(String json) {
// 时间戳加入签名参数组中
HashMap sParaTemp = new HashMap<>();
sParaTemp.put("timestamp", String.valueOf(System.currentTimeMillis()));
// 生成签名结果
String mysign = DigestUtils.md5Hex(getContentBytes(json, INPUT_CHARSET));
// 签名结果加入请求提交参数组中
sParaTemp.put("sign", mysign);
return sParaTemp;
}
public static boolean verifyJson(String json, String sign, String timestamp) {
// 将json数据做一个格式化
JSONObject data = JSONObject.parseObject(json);
// 获得签名验证结果
String mysign = DigestUtils.md5Hex(getContentBytes(json, INPUT_CHARSET));
if (mysign.equals(sign)) {
// 是否超时
long curr = System.currentTimeMillis();
if ((curr - Long.valueOf(timestamp)) > TIME_OUT) {
logger.info("api is time out");
return false;
}
return true;
} else {
return false;
}
}
public static void main(String[] args) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("channel", "ios");
jsonObject.put("equipment", "
留言与评论(共有 0 条评论) “” |