08.如何保证API接口的安全性问题01


1.互联网Api接口到底如何保证安全性问题?
2.代码落地实战防御XSS、CSRF攻击
3.代码落地如何防御接口数据被黑客抓包篡改?
4.接口数据加密对称还是非对称加密好

安全架构设计方案

如何防御xss攻击


XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括JavaVBScriptActiveX、 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=

模拟xss攻击


前端传递 js 脚本到服务器端

{
  "channel": "",
  "equipment": "",
  "password": "123456",
  "phoneNumber": "15921009758"
}

后端接口将该脚本存放数据库中

08.如何保证API接口的安全性问题01


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模拟攻击










08.如何保证API接口的安全性问题01


防御xss

方式1

将用户前端所提交的参数进行过滤。

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);
08.如何保证API接口的安全性问题01

方式2

接口接受参数 ?传递参数形式---

传递参数都是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;
    }
}

抓包如何防止篡改数据

Fiddler抓包工具的使用

1.可以使用第三方抓包工具,对请求前后实现代理,可以修改参数请求内容和参数响应内容,抓包工具http调试工具

2.Fiddler4下载地址:https://pc.qq.com/detail/10/detail_3330.html

使用Fiddler4篡改请求之前:


08.如何保证API接口的安全性问题01

08.如何保证API接口的安全性问题01

08.如何保证API接口的安全性问题01



防御篡改数据

使用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 条评论) “”
   
验证码:

相关文章

推荐文章