1、弱密码检测
弱密码检测
检测内容
- 检测长度,8-20位
- 检测是否包含数字、字母及特殊字符至少三种
- 检测键盘横向连续,键盘物理位置横向不允许连续个数4
- 检测键盘斜向连续,键盘物理位置斜向不允许连续个数4
- 检测逻辑位置连续a-z,z-a,逻辑连续不允许个数4
- 检测相邻字符是否相同,相同字符不允许连续个数4
- 检测常用词库
- 登录、修改密码时的旧密码,可以暂时先用之前的验证方法,避免之前的弱密码无法登录和修改
- 注册、修改密码时的新密码、重置密码、忘记密码等,需要改为新的校验规则
后端源代码
/**
* 密码校验工具类
*
*/
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();
}
}
后端使用方法
- 配置需要检查项,然后根据项目实际情况更新异常返回的代码。
- 注册、修改密码时调用检查方法evalPassword(password)。
- 调用检查方法后返回是否通过弱密码检测。
- 可根据实际情况选择以上检测内容进行检查。
- 登录时可以调用isSimplePassword(password)方法判断当前密码是否是弱密码,如果是弱密码,可以提示用户修改
2、登录RSA加密
加密规则
- 公钥及私钥每天随机生成一次。
使用方法
- 引入类RSAEncryptUtil,调用getPublicKey方法获取公钥。
- 前端通过调用后端接口那倒公钥后对密码及用户名进行加密。
- 后端可通过调用decrypt方法进行解密,需要传的参数为加密数据及私钥,私钥可通过调用getPrivateKey方法获取。
- 如果是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、等保二级验收规范
弱口令
- 在登录时增加密码复杂度校验,当用户密码复杂度不符合要求时强制修改密码。
- 在注册、修改时密码增加密码复杂度校验。
- 当用户密码复杂度不符合要求时不允许修改密码。
- 复杂密码应满足:口令至少8位,由数字、字母、特殊字符组成。密码中应避免出现常见英文单词以及易猜测的字母数字组合,如:qaz、wsx、asd、zxc、qwe、edc、12、123、1234、12345、123456、1234567、12345678、123456789、1234567890、666、888、111、000(字符不分大小写、不分数字正、反序)。
- 设置口令过期时间(90-180天)强制定期修改口令。
登录凭据爆破
- 用户是否存在或者密码错误,都统一提示“用户名或密码错误”。
- 增加账号锁定策略,登录错误次数在5-10则锁定账号5-30分钟以上。
- 加入验证码的形式,建议采用不易识别的验证码,防止机器识别的可能,最好能采用滑动验证、点击图片文字的验证方式。
- 登录过程中出现其他形式的异常如账号禁用、用户不存在、未审核的情况,均提示“用户名或密码错误”。并当错误次数达到锁定时,锁定账户。
其他
- 未授权访问,应对后台所有页面及接口做身份校验。
- Swagger接口文档泄露,删除Swagger的接口调试页面。
- 当登录后超时5-30分钟未使用,则退出登录状态。
- 不影响系统正常运行和运维的情况下重命名或禁用系统默认账户,若无法重命名或禁用,则需关闭其远程登录权限。删除应用系统默认账户名。
网络安全注意事项
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,删除掉对应策略
网友评论