等保安全弱密码、登录检测、安全注意事项等

2024-02-25  本文已影响0人  承诺一时的华丽

1、弱密码检测


弱密码检测

检测内容

  1. 检测长度,8-20位
  2. 检测是否包含数字、字母及特殊字符至少三种
  3. 检测键盘横向连续,键盘物理位置横向不允许连续个数4
  4. 检测键盘斜向连续,键盘物理位置斜向不允许连续个数4
  5. 检测逻辑位置连续a-z,z-a,逻辑连续不允许个数4
  6. 检测相邻字符是否相同,相同字符不允许连续个数4
  7. 检测常用词库
  8. 登录、修改密码时的旧密码,可以暂时先用之前的验证方法,避免之前的弱密码无法登录和修改
  9. 注册、修改密码时的新密码、重置密码、忘记密码等,需要改为新的校验规则

后端源代码

/**
 * 密码校验工具类
 *
 */
public class PasswordCheckUtil {
    /**
     * 密码最小长度,默认为8
     */
    private static String MIN_LENGTH = "8";
    /**
     * 密码最大长度,默认为20
     */
    private static String MAX_LENGTH = "20";
    /**
     * 是否检测键盘横向连续
     */
    private static boolean CHECK_LATERAL_KEYBOARD_SITE = true;
    /**
     * 是否检测键盘斜向连续
     */
    private static boolean CHECK_KEYBOARD_SLANT_SITE = true;
    /**
     * 是否检测逻辑位置连续
     */
    private static boolean CHECK_SEQUENTIAL_CHARS = true;
    /**
     * 是否检测相邻字符是否相同
     */
    private static boolean CHECK_SEQUENTIAL_SAME_CHARS = true;
    /**
     * 是否检测常用词库
     */
    private static boolean CHECK_SIMPLE_WORD = true;
    /**
     * 正则表达式规则
     */
    private static Pattern passwordRegex = Pattern.compile("^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[<>,.?/;:'\"\\[\\]{}|`~!@#$%^&*()\\-_+=]){" + MIN_LENGTH + "," + MAX_LENGTH + "}", Pattern.CASE_INSENSITIVE);
    /**
     * 特殊符号集合
     */
    private static String SPECIAL_CHAR = "!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~";
    /**
     * 键盘物理位置横向不允许最小的连续个数
     */
    private static String LIMIT_HORIZONTAL_NUM_KEY = "4";
    /**
     * 键盘物理位置斜向不允许最小的连续个数
     */
    private static String LIMIT_SLOPE_NUM_KEY = "4";
    /**
     * 密码口令中字符在逻辑位置上不允许最小的连续个数
     */
    private static String LIMIT_LOGIC_NUM_CHAR = "4";
    /**
     * 密码口令中相同字符不允许最小的连续个数
     */
    private static String LIMIT_NUM_SAME_CHAR = "4";
    /**
     * 键盘横向方向规则
     */
    private static String[] KEYBOARD_HORIZONTAL_ARR = {"01234567890", "qwertyuiop", "asdfghjkl", "zxcvbnm"};
    /**
     * 键盘斜线方向规则
     */
    private static String[] KEYBOARD_SLOPE_ARR = {"1qaz", "2wsx", "3edc", "4rfv", "5tgb", "6yhn", "7ujm", "8ik,",
            "9ol.", "0p;/", "=[;.", "-pl,", "0okm", "9ijn", "8uhb", "7ygv", "6tfc", "5rdx", "4esz"};
    /**
     * 常用词库
     */
    private static String[] SIMPLE_WORDS = {"admin", "szim", "epicrouter", "password", "grouter", "dare", "root",
            "guest", "user", "success", "pussy", "mustang", "fuckme", "jordan", "test", "hunter", "jennifer", "batman",
            "thomas", "soccer", "sexy", "killer", "george", "asshole", "fuckyou", "summer", "hello", "secret", "fucker",
            "enter", "cookie", "administrator",
            // 中国网民常用密码
            "xiaoming", "taobao", "iloveyou", "woaini", "982464",
            // 国外网民常用密码
            "monkey", "letmein", "trustno1", "dragon", "baseball", "master", "sunshine", "ashley", "bailey", "shadow",
            "superman", "football", "michael", "qazwsx"};
    /**
     * 评估密码中包含的字符类型是否符合要求
     *
     * @param password
     * @return 符合要求 返回true
     */
    public static boolean evalPassword(String password) {
        //判断密码规则是否合法,8-20位的数字、字母或符号(不能有中文和空格)
        if (!checkPasswordByRegex(password)) {
            throw new SaasException(DevicePlatformErrorCodeEnum.PASSWORD_FORMAT_ERROR.getCode(), "密码长度必须为8~20位之间,必须包含数字、字母和符号");
        }
        //检测键盘横向连续
        if (CHECK_LATERAL_KEYBOARD_SITE && checkLateralKeyboardSite(password)) {
            throw new SaasException(DevicePlatformErrorCodeEnum.PASSWORD_FORMAT_ERROR.getCode(), "密码检测到键盘横向连续");
        }
        //检测键盘斜向连续
        if (CHECK_KEYBOARD_SLANT_SITE && checkKeyboardSlantSite(password)) {
            throw new SaasException(DevicePlatformErrorCodeEnum.PASSWORD_FORMAT_ERROR.getCode(), "密码检测到键盘斜向连续");
        }
        //检测逻辑位置连续
        if (CHECK_SEQUENTIAL_CHARS && checkSequentialChars(password)) {
            throw new SaasException(DevicePlatformErrorCodeEnum.PASSWORD_FORMAT_ERROR.getCode(), "密码检测到逻辑连续");
        }
        //检测相邻字符是否相同
        if (CHECK_SEQUENTIAL_SAME_CHARS && checkSequentialSameChars(password)) {
            throw new SaasException(DevicePlatformErrorCodeEnum.PASSWORD_FORMAT_ERROR.getCode(), "密码检测相邻字符相同");
        }
        //检测常用词库
        if (CHECK_SIMPLE_WORD && checkSimpleWord(password)) {
            throw new SaasException(DevicePlatformErrorCodeEnum.PASSWORD_FORMAT_ERROR.getCode(), "密码检测到包含常用词");
        }
        return true;
    }
    /**
     * 是否是简单密码,如果是简单密码 建议用户修改
     * @param password
     * @return
     */
    public static boolean isSimplePassword(String password){
        //简答密码先只根据正则表达式判断
        return !checkPasswordByRegex(password);
    }
    /**
     * 检测密码中字符长度
     *
     * @param password
     * @return 符合长度要求 返回
     */
    private static boolean checkPasswordLength(String password) {
        boolean flag = false;
        // 如未设置最大长度,仅判断最小长度即可
        if ("".equals(MAX_LENGTH)) {
            if (password.length() >= Integer.parseInt(MIN_LENGTH)) {
                flag = true;
            }
        } else {
            if (password.length() >= Integer.parseInt(MIN_LENGTH)
                    && password.length() <= Integer.parseInt(MAX_LENGTH)) {
                flag = true;
            }
        }
        return flag;
    }
    /**
     * 检查密码中是否包含数字
     *
     * @param password
     * @return 包含数字 返回true
     */
    private static boolean checkContainDigit(String password) {
        char[] chPass = password.toCharArray();
        boolean flag = false;
        int numCount = 0;
        for (int i = 0; i < chPass.length; i++) {
            if (Character.isDigit(chPass[i])) {
                numCount++;
            }
        }
        if (numCount >= 1) {
            flag = true;
        }
        return flag;
    }
    /**
     * 检查密码中是否包含字母(不区分大小写)
     *
     * @param password
     * @return 包含字母 返回true
     */
    private static boolean checkContainCase(String password) {
        char[] chPass = password.toCharArray();
        boolean flag = false;
        int charCount = 0;
        for (int i = 0; i < chPass.length; i++) {
            if (Character.isLetter(chPass[i])) {
                charCount++;
            }
        }
        if (charCount >= 1) {
            flag = true;
        }
        return flag;
    }
    /**
     * 检查密码中是否包含小写字母
     *
     * @param password
     * @return 包含小写字母 返回true
     */
    private static boolean checkContainLowerCase(String password) {
        boolean flag = false;
        char[] chPass = password.toCharArray();
        int charCount = 0;
        for (int i = 0; i < chPass.length; i++) {
            if (Character.isLowerCase(chPass[i])) {
                charCount++;
            }
        }
        if (charCount >= 1) {
            flag = true;
        }
        return flag;
    }
    /**
     * 检查密码中是否包含大写字母
     *
     * @param password
     * @return 包含大写字母 返回true
     */
    private static boolean checkContainUpperCase(String password) {
        boolean flag = false;
        char[] chPass = password.toCharArray();
        int charCount = 0;
        for (int i = 0; i < chPass.length; i++) {
            if (Character.isUpperCase(chPass[i])) {
                charCount++;
            }
        }
        if (charCount >= 1) {
            flag = true;
        }
        return flag;
    }
    /**
     * 检查密码中是否包含特殊字符
     *
     * @param password
     * @return 包含特殊字符 返回true
     */
    private static boolean checkContainSpecialChar(String password) {
        boolean flag = false;
        char[] chPass = password.toCharArray();
        int specialCount = 0;
        for (int i = 0; i < chPass.length; i++) {
            if (SPECIAL_CHAR.indexOf(chPass[i]) != -1) {
                specialCount++;
            }
        }
        if (specialCount >= 1) {
            flag = true;
        }
        return flag;
    }
    /**
     * 键盘规则匹配器 横向连续检测
     *
     * @param password
     * @return 含有横向连续字符串 返回true
     */
    private static boolean checkLateralKeyboardSite(String password) {
        String tPassword = new String(password);
        // 将字符串内所有字符转为小写
        tPassword = tPassword.toLowerCase();
        int n = tPassword.length();
        /**
         * 键盘横向规则检测
         */
        boolean flag = false;
        int arrLen = KEYBOARD_HORIZONTAL_ARR.length;
        int limitNum = Integer.parseInt(LIMIT_HORIZONTAL_NUM_KEY);
        for (int i = 0; i + limitNum <= n; i++) {
            String str = tPassword.substring(i, i + limitNum);
            String distinguishStr = password.substring(i, i + limitNum);
            for (int j = 0; j < arrLen; j++) {
                String configStr = KEYBOARD_HORIZONTAL_ARR[j];
                String revOrderStr = new StringBuffer(KEYBOARD_HORIZONTAL_ARR[j]).reverse()
                        .toString();
                if (configStr.indexOf(str) != -1) {
                    flag = true;
                    return flag;
                }
                // 考虑逆序输入情况下 连续输入
                if (revOrderStr.indexOf(str) != -1) {
                    flag = true;
                    return flag;
                }
            }
        }
        return flag;
    }
    /**
     * 键盘规则匹配器 斜向规则检测
     *
     * @param password
     * @return 含有斜向连续字符串 返回true
     */
    private static boolean checkKeyboardSlantSite(String password) {
        String tPassword = new String(password);
        tPassword = tPassword.toLowerCase();
        int n = tPassword.length();
        /**
         * 键盘斜线方向规则检测
         */
        boolean flag = false;
        int arrLen = KEYBOARD_SLOPE_ARR.length;
        int limitNum = Integer.parseInt(LIMIT_SLOPE_NUM_KEY);
        for (int i = 0; i + limitNum <= n; i++) {
            String str = tPassword.substring(i, i + limitNum);
            String distinguishStr = password.substring(i, i + limitNum);
            for (int j = 0; j < arrLen; j++) {
                String configStr = KEYBOARD_SLOPE_ARR[j];
                String revOrderStr = new StringBuffer(KEYBOARD_SLOPE_ARR[j]).reverse().toString();
                // 检测包含字母(区分大小写)
                if (configStr.indexOf(str) != -1) {
                    flag = true;
                    return flag;
                }
                // 考虑逆序输入情况下 连续输入
                if (revOrderStr.indexOf(str) != -1) {
                    flag = true;
                    return flag;
                }
            }
        }
        return flag;
    }
    /**
     * 评估a-z,z-a这样的连续字符
     *
     * @param password
     * @return 含有a-z,z-a连续字符串 返回true
     */
    private static boolean checkSequentialChars(String password) {
        String tPassword = new String(password);
        boolean flag = false;
        int limitNum = Integer.parseInt(LIMIT_LOGIC_NUM_CHAR);
        int normalCount = 0;
        int reversedCount = 0;
        tPassword = tPassword.toLowerCase();
        int n = tPassword.length();
        char[] pwdCharArr = tPassword.toCharArray();
        for (int i = 0; i + limitNum <= n; i++) {
            normalCount = 0;
            reversedCount = 0;
            for (int j = 0; j < limitNum - 1; j++) {
                if (pwdCharArr[i + j + 1] - pwdCharArr[i + j] == 1) {
                    normalCount++;
                    if (normalCount == limitNum - 1) {
                        return true;
                    }
                }
                if (pwdCharArr[i + j] - pwdCharArr[i + j + 1] == 1) {
                    reversedCount++;
                    if (reversedCount == limitNum - 1) {
                        return true;
                    }
                }
            }
        }
        return flag;
    }
    /**
     * 相同连续字符判断
     *
     * @param password
     */
    private static boolean checkSequentialSameChars(String password) {
        String tPassword = new String(password);
        int n = tPassword.length();
        char[] pwdCharArr = tPassword.toCharArray();
        boolean flag = false;
        int limitNum = Integer.parseInt(LIMIT_NUM_SAME_CHAR);
        int count = 0;
        for (int i = 0; i + limitNum <= n; i++) {
            count = 0;
            for (int j = 0; j < limitNum - 1; j++) {
                if (pwdCharArr[i + j] == pwdCharArr[i + j + 1]) {
                    count++;
                    if (count == limitNum - 1) {
                        return true;
                    }
                }
            }
        }
        return flag;
    }
    /**
     * 检测常用词库
     *
     * @param password
     * @return 含有常见词库 返回true
     */
    private static boolean checkSimpleWord(String password) {
        List<String> simpleWords = Arrays.asList(SIMPLE_WORDS);
        return simpleWords.contains(password.toLowerCase());
    }
    /**
     * 判断密码规则是否合法,6-20位的数字、字母或符号(不能有中文和空格)
     */
    private static boolean checkPasswordByRegex(String str) {
        if (str == null || str.length() == 0) {
            return false;
        }
        Matcher matcher = passwordRegex.matcher(str);
        return matcher.find();
    }
}

后端使用方法

  1. 配置需要检查项,然后根据项目实际情况更新异常返回的代码。
  2. 注册、修改密码时调用检查方法evalPassword(password)。
  3. 调用检查方法后返回是否通过弱密码检测。
  4. 可根据实际情况选择以上检测内容进行检查。
  5. 登录时可以调用isSimplePassword(password)方法判断当前密码是否是弱密码,如果是弱密码,可以提示用户修改

2、登录RSA加密


加密规则

  1. 公钥及私钥每天随机生成一次。

使用方法

  1. 引入类RSAEncryptUtil,调用getPublicKey方法获取公钥。
  2. 前端通过调用后端接口那倒公钥后对密码及用户名进行加密。
  3. 后端可通过调用decrypt方法进行解密,需要传的参数为加密数据及私钥,私钥可通过调用getPrivateKey方法获取。
  4. 如果是MD5加密,可直接调用decryptMD5方法,改方法返回值为用户密码比对正确与否。

前端加密

1、安装 jsencrypt

npm i jsencrypt

2、封装 RSAcrypt.js

import JSEncrypt from 'jsencrypt'
/**
 * RSA公钥加密
 *
 * @param str 需加密字符串
 * @param publicKey 公钥
 * @returns
 */
export function RSAencrypt(str, publicKey) {
    const jse = new JSEncrypt();
    // 设置公钥
    jse.setPublicKey(publicKey);
    return jse.encrypt(str);
}
/**
 * RSA私钥解密
 *
 * @param str 需解密字符串
 * @param privateKey 私钥
 * @returns
 */
export function RSAdecrypt(str, privateKey) {
    const jse = new JSEncrypt();
    // 设置私钥
    jse.setPrivateKey(privateKey);
    return jse.decrypt(str);
}

3、使用

const str = '密码';
const publicKey = '公钥'; // 后端接口获取
const password = RSAencrypt(str, publicKey);

后端源代码

@Component
public class RSAEncryptUtil {
    @Value("${spring.application.name}")
    private String applicationName;
    @Autowired
    private StringRedisTemplate redisTemplate;
    private static final String PUBLIC_SECRET = "public";
    private static final String PRIVATE_SECRET = "private";
    /**
     * 获取公钥
     *
     * @return
     */
    public synchronized String getPublicKey() {
        String jsonString = redisTemplate.opsForValue().get(getRsaEncrypt());
        if (ObjectUtils.isEmpty(jsonString)) {
            Map<String, String> keyMap = genKeyPair();
            redisTemplate.opsForValue().set(getRsaEncrypt(), JSONObject.toJSONString(keyMap), getSecondDayDifference(), TimeUnit.SECONDS);
            return keyMap.get(PUBLIC_SECRET);
        } else {
            JSONObject jsonObject = JSONObject.parseObject(jsonString);
            return jsonObject.getString(PUBLIC_SECRET);
        }
    }
    private String getPrivateKey() {
        String jsonString = redisTemplate.opsForValue().get(getRsaEncrypt());
        if (!ObjectUtils.isEmpty(jsonString)) {
            JSONObject jsonObject = JSONObject.parseObject(jsonString);
            return jsonObject.getString(PRIVATE_SECRET);
        }
        return null;
    }
    /**
     * 随机生成密钥对
     */
    private Map<String, String> genKeyPair() {
        try {
            Map<String, String> keyMap = new HashMap<String, String>();  //用于封装随机产生的公钥与私钥
            // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
            // 初始化密钥对生成器,密钥大小为96-1024位
            keyPairGen.initialize(512, new SecureRandom());
            // 生成一个密钥对,保存在keyPair中
            KeyPair keyPair = keyPairGen.generateKeyPair();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
            String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
            // 得到私钥字符串
            String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
            // 将公钥和私钥保存到Map
            keyMap.put(PUBLIC_SECRET, publicKeyString);  //公钥
            keyMap.put(PRIVATE_SECRET, privateKeyString); //私钥
            return keyMap;
        } catch (Exception e) {
            throw new SaasException(DevicePlatformErrorCodeEnum.RSA_ENCRYPT_ERROR.getCode(), "生成秘钥失败", e);
        }
    }
    /**
     * RSA公钥加密
     *
     * @param str 加密字符串
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    public String encrypt(String str) {
        try {
            //base64编码的公钥
            byte[] decoded = Base64.decodeBase64(getPublicKey());
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            //RSA加密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            return Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            throw new SaasException(DevicePlatformErrorCodeEnum.RSA_ENCRYPT_ERROR.getCode(), "密码加密失败", e);
        }
    }
    /**
     * RSA私钥解密
     *
     * @param str 加密字符串
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public String decrypt(String str) {
        try {
            //64位解码加密后的字符串
            byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
            //base64编码的私钥
            byte[] decoded = Base64.decodeBase64(getPrivateKey());
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            //RSA解密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            return new String(cipher.doFinal(inputByte));
        } catch (Exception e) {
            throw new SaasException(DevicePlatformErrorCodeEnum.RSA_ENCRYPT_ERROR.getCode(), "密码解密失败", e);
        }
    }
    /**
     * RSA私钥解密
     *
     * @param str 加密字符串
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public String decrypt(String str, String privateKey) {
        try {
            //64位解码加密后的字符串
            byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
            //base64编码的私钥
            byte[] decoded = Base64.decodeBase64(privateKey);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            //RSA解密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            return new String(cipher.doFinal(inputByte));
        } catch (Exception e) {
            throw new SaasException(DevicePlatformErrorCodeEnum.RSA_ENCRYPT_ERROR.getCode(), "密码解密失败", e);
        }
    }
    public boolean decryptMD5(String str, String userSalt, String userPassword) throws Exception {
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(getPrivateKey());
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte));
        // MD5加密
        if (Strings.isNullOrEmpty(userSalt)) {
            return userPassword.equals(CryptoUtil.Md5(outStr));
        } else {
            return userPassword.equals(CryptoUtil.Md5(outStr, userSalt));
        }
    }
    private String getRsaEncrypt() {
        String format = DateUtil.format(System.currentTimeMillis(), "yyyy-MM-dd");
        return applicationName + "_rsa_encrypt:" + format;
    }
    public static long getSecondDayDifference() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        System.out.println(cal.getTime());
        return (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
    }
}
package com.cy.utils;


import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


/**
 * @author wyd10
 */
public class CryptoUtil {
    /**
     * 用于建立十六进制字符的输出的小写字符数组
     */
    private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

    public static String Md5(String str) {

        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance(AlgorithmEnum.MD5.getValue());

            char[] chars = encodeHex(messageDigest.digest(str.getBytes("UTF-8")),DIGITS_LOWER);
            return new String(chars);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e.getMessage());
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e.getMessage());
        }

    }

    /**
     * 将字节数组转换为十六进制字符数组
     *
     * @param data byte[]
     * @param toDigits 用于控制输出的char[]
     * @return 十六进制char[]
     */
    private static char[] encodeHex(byte[] data, char[] toDigits) {
        int l = data.length;
        char[] out = new char[l << 1];
        // two characters from the hex value.
        for (int i = 0, j = 0; i < l; i++) {
            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
            out[j++] = toDigits[0x0F & data[i]];
        }
        return out;
    }

    public static String Md5(String str,String salt) {
        return Md5(Md5(str)+salt);
    }

    public enum AlgorithmEnum {
        /***/
        MD5("MD5");
        private String value;

        AlgorithmEnum(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
}

后端代码使用方式
1、提供获取公钥接口,用于前端获取公钥

  @Autowired
    private RSAEncryptUtil rsaEncryptUtil;
    @GetMapping("public-key")
    @ApiOperation("获取密码加密的公钥")
    @AllowAccess
    public ResponseMessage getPasswordPublicKey() {
        return ResponseMessage.success(rsaEncryptUtil.getPublicKey());
    }

2、调用解密方法解密密文,获取原始密码

String password = rsaEncryptUtil.decrypt(loginRequest.getPassword())

3、等保二级验收规范


弱口令

  1. 在登录时增加密码复杂度校验,当用户密码复杂度不符合要求时强制修改密码。
  2. 在注册、修改时密码增加密码复杂度校验。
  3. 当用户密码复杂度不符合要求时不允许修改密码。
  4. 复杂密码应满足:口令至少8位,由数字、字母、特殊字符组成。密码中应避免出现常见英文单词以及易猜测的字母数字组合,如:qaz、wsx、asd、zxc、qwe、edc、12、123、1234、12345、123456、1234567、12345678、123456789、1234567890、666、888、111、000(字符不分大小写、不分数字正、反序)。
  5. 设置口令过期时间(90-180天)强制定期修改口令。

登录凭据爆破

  1. 用户是否存在或者密码错误,都统一提示“用户名或密码错误”。
  2. 增加账号锁定策略,登录错误次数在5-10则锁定账号5-30分钟以上。
  3. 加入验证码的形式,建议采用不易识别的验证码,防止机器识别的可能,最好能采用滑动验证、点击图片文字的验证方式。
  4. 登录过程中出现其他形式的异常如账号禁用、用户不存在、未审核的情况,均提示“用户名或密码错误”。并当错误次数达到锁定时,锁定账户。

其他

  1. 未授权访问,应对后台所有页面及接口做身份校验。
  2. Swagger接口文档泄露,删除Swagger的接口调试页面。
  3. 当登录后超时5-30分钟未使用,则退出登录状态。
  4. 不影响系统正常运行和运维的情况下重命名或禁用系统默认账户,若无法重命名或禁用,则需关闭其远程登录权限。删除应用系统默认账户名。

网络安全注意事项


1、禁止将该敏感信息放在js文件中。
token,secret,key等敏感信息不能放到js文件中,例如携带token跳转其他系统时,不允许直接写到js代码中,

2、生产环境屏蔽swagger访问
如果是是使用的knife4j,需增加如下配置

knife4j:
  enable: true
  #开启生产环境屏蔽
  production: true

其他版本swagger需要针对性的处理。
另外采购的第三方软件或者开源软件也需要屏蔽

3、防止prometheus组件泄露后台路径
增加配置

management:
  metrics:
    enable:
      all: false

4、minio配置不要列出文件xml信息
处理方式:
参考:PDF预览

使用s3 Browser设置Policy,删除掉对应策略

上一篇 下一篇

猜你喜欢

热点阅读