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

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;
    }
}
  • CryptoUtil 文件
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,删除掉对应策略

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容