• API 接口的安全设计


    API 接口的安全设计验证:ticket,签名,时间戳

    一.背景

    1.与前端对接的API接口,如果被第三方抓包并进行恶意篡改参数,可能会导致数据泄露,甚至会被篡改数据

    2.与第三方公司的接口对接,第三方如果得到你的接口文档,但是接口确没安全校验,是十分不安全的

    我主要围绕时间戳,token,签名三个部分来保证API接口的安全性

    二.请求过程

    1.用户成功登陆站点后,服务器会返回一个token,用户的任何操作都必须带了这个参数,可以将这个参数直接放到header里。

    2.客户端用需要发送的参数和token生成一个签名sign,作为参数一起发送给服务端,服务端在用同样的方法生成sign进行检查是否被篡改。

    3.但这依然存在问题,可能会被进行恶意无限制访问,这时我们需要引入一个时间戳参数,如果超时即是无效的。

    4.服务端需要对token,签名,时间戳进行验证,只有token有效,时间戳未超时,签名有效才能被放行。

    概念:

    (1)开放接口

    没有进行任何限制,简单粗暴的访问方式,这样的接口方式一般在开放的应用平台,查天气,查快递,只要你输入正确对应的参数调用,即可获取到自己需要的信息,我们可以任意修改参数值。

    (2)Token认证获取

    用户登录成功后,会获取一个ticket值,接下去任何接口的访问都需要这个参数。我们把它放置在redis内,有效期为10分钟,在ticket即将超时,无感知续命。延长使用时间,如果用户在一段时间内没进行任何操作,就需要重新登录系统。

    (3)Sign签名

    把所有的参数拼接一起,在加入系统秘钥,进行MD5计算生成一个sign签名,防止参数被人恶意篡改,后台按同样的方法生成秘钥,进行签名对比。

    (4)重复访问

    引入一个时间戳参数,保证接口仅在一分钟内有效,需要和客户端时间保持一致。

    (5)拦截器

    每次请求都带有这三个参数,我们都需要进行验证,只有在三个参数都满足我们的要求,才允许数据返回或被操作。

    三.具体代码实现

    1.编写获取tiket的接口

    复制代码
        /**
         * 获取tiket
         * @param receiveRequest
         * @return
         */
        @ResponseBody
        @RequestMapping(value = "/gettiket",method = RequestMethod.POST)
        public  String gettiket(@RequestBody String data){
            String result = "";
            String msg = "";
            try{
                log.info("gettiket,入参为==="+data);
    
                JdbcTemplate jdbcTemplate = new JdbcTemplate();
                String userTocken = UUID.randomUUID().toString();
                //cache.put(userTocken, userMap);//数据库方式或者redis方式,这里用数据库方式
                String insert_user_token_sql = "insert into user_token(pk_user_token,userid,user_token) VALUES (?,?,?)";
                long pk_user_token = KeyUtils.nextId();//主键
    
                jdbcTemplate.executeUpdate(insert_user_token_sql, new Object[]{
                        pk_user_token,"111",userTocken
                });
                result = userTocken;
                msg = "{\"success\" : true,\"errorCode\" : \"200\", \"errorMsg\" : \"查询完成\", \"tiket\" :" +result + "}";
                log.info("msg===="+msg);
                return msg;
            }catch(Exception e){
                msg = "{\"success\" : true,\"errorCode\" : \"500\", \"errorMsg\" : \"查询完成\", \"data\" :" +e + "}";
                return msg;
            }
    
    
        }
    复制代码

    2.服务端验证

    主程序入口

    复制代码
            Map<String, String> paramMap = new HashMap<>();
            String time = DateUtils.formatDate("yyyy-MM-dd HH:mm:ss.SSS");
            paramMap.put("time", time);
            String ticket = "056a3d29-eed3-4ee9-80aa-c03321d5302f";
            paramMap.put("ticket", ticket);//userTock为我第一次请求你的单点url时传给你的userTocken
            String serviceCode = "cs_demo";// 目标系统对应的密钥
            String sign = null;
            try {
                sign = SignUtils.sing(paramMap, serviceCode, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            log.info("sign==="+sign);
            CheckPerService checkPerService= new CheckPerService();
            Boolean istrue = checkPerService.TicketSignAndTime( ticket, sign, time, serviceCode);
            log.info("istrue==="+istrue);
    复制代码

    工具类SignUtils

    复制代码
    package tcc.test.utill;
    
    import org.apache.log4j.Logger;
    import org.springframework.util.DigestUtils;
    import java.io.UnsupportedEncodingException;
    import java.util.*;
    
    public class SignUtils {
        private static final Logger a = Logger.getRootLogger();
    
        public SignUtils() {
        }
    
        public static String getContent(Map params) {
            List keys = new ArrayList(params.keySet());
            Collections.sort(keys);
            String prestr = "";
            boolean first = true;
    
            for(int i = 0; i < keys.size(); ++i) {
                String key = (String)keys.get(i);
                if (!"sign".equals(key) && !"_r".equals(key) && !"_result_type".equals(key) && !"_".equals(key)) {
                    String value = String.valueOf(params.get(key));
                    if (value != null && value.trim().length() != 0) {
                        if (first) {
                            prestr = prestr + key + "=" + value;
                            first = false;
                        } else {
                            prestr = prestr + "&" + key + "=" + value;
                        }
                    }
                }
            }
    
            a.info("加密字符串:" + prestr);
            return prestr;
        }
    
    
        public static String sing(Map Params, String key, String charset) throws UnsupportedEncodingException {
            String signStr = null;
            signStr = DigestUtils.md5DigestAsHex((getContent(Params) + key).getBytes(charset));
            return signStr;
        }
    
    
        public static void main(String[] args) throws Exception {
            Map paramMap = new HashMap<String,String>();
            paramMap.put("name","tcc");
            paramMap.put("age","24");
            String serviceCode = "siruinet";
            String sing = SignUtils.sing(paramMap, serviceCode, "UTF-8");
            System.out.println(sing);
    
    
    
        }
    }
    复制代码

    权限校验工具类

    复制代码
    package tcc.test.utill;
    
    import com.alibaba.druid.util.StringUtils;
    import com.util.FieldList;
    import jos.engine.core.jdbc.JdbcTemplate;
    import jos.engine.des.util.DesEncryptUtils;
    import org.apache.log4j.Logger;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * Copyright (C) @2022
     *
     * @author: tcc
     * @version: 1.0
     * @date: 2022/1/31
     * @time: 2:08
     * @description:
     */
    public class CheckPerService{
        private static final Logger log = Logger.getRootLogger();
    
        /*
        接口权限校验方法1
        ticket:票据
        sign:签名
        time:时间戳
        serviceCode:服务编码*/
        public static boolean TicketSignAndTime(String ticket, String sign, String time, String serviceCode){
            time = time;
            ticket = ticket;
            sign = sign;
            Map<String, String> paramMap = new HashMap<>();
            paramMap.put("time", time);
            paramMap.put("ticket", ticket);//ticket为第一次调用获取ticket接口的数据
            serviceCode = serviceCode;// 目标系统对应的密钥
            String qm = DesEncryptUtils.sing(paramMap, serviceCode, "UTF-8");
            log.info("qm==="+qm);
            if (!StringUtils.equals(sign, qm)) { //密钥校验错误
                log.info("签名不正确");
                return false;
            }
            log.info("签名正确");
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            String qr_user_token_sql = "select count(1) as count from user_token where user_token = ?";//后期改成redis
            FieldList file_token = jdbcTemplate.queryField(qr_user_token_sql, new Object[]{ ticket });
            int count = Integer.parseInt(file_token.get("count"));
            if(count<1){
                return false;
            }
            return true;
        }
    
            /*
        接口权限校验方法2
        name:用户名
        pwd:密码
         */
        public static boolean UnmAndPwd(String name,String pwd){
            JdbcTemplate jdbcTemplate = new JdbcTemplate("mzdb");
            String qr_user_token_sql = "select count(1) as count from bd_user where USERNAME = ? and USERPASS = ?";//后期改成redis
            FieldList file_token = jdbcTemplate.queryField(qr_user_token_sql, new Object[]{ name,pwd });
            int count = Integer.parseInt(file_token.get("count"));
            if(count<1){
                return false;
            }
            return true;
    
        }
    
    
    
    
    
    }
    复制代码
     
    分类: 服务安全
  • 相关阅读:
    linux设备驱动学习笔记(1)
    linux 设备驱动程序中的一些关联性思考
    linux——(2)文件权限与目录配置
    linux——(1)初识linux
    设计模式-状态模式(State Pattern)
    设计模式-组合模式(Composite Pattern)
    设计模式-迭代器模式(Iterator Pattern)
    设计模式-模板方法模式(the Template Method Pattern)
    设计模式-外观模式(Facade Pattern)
    设计模式-适配器模式(Adapter Pattern)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/16220056.html
Copyright © 2020-2023  润新知