美文网首页
等保安全弱密码、登录检测、安全注意事项等

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

作者: 承诺一时的华丽 | 来源:发表于2024-02-25 17:29 被阅读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;
        }
    }
    
    • 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,删除掉对应策略

    相关文章

      网友评论

          本文标题:等保安全弱密码、登录检测、安全注意事项等

          本文链接:https://www.haomeiwen.com/subject/yzuaadtx.html