赞
踩
先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注网络安全)
@Override
public Object plugin (Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties (Properties properties) {
}
/**
对请求参数进行解密还原操作
@param target
*/
private void checkEncryptByQuery (Object target) {
try {
final Class<?> targetClass = target.getClass();
final Field parameterHandlerFiled = targetClass.getDeclaredField(“parameterHandler”);
parameterHandlerFiled.setAccessible(true);
final Object parameterHandler = parameterHandlerFiled.get(target);
final Class<?> parameterHandlerClass = parameterHandler.getClass();
final Field parameterObjectField = parameterHandlerClass.getDeclaredField(“parameterObject”);
parameterObjectField.setAccessible(true);
final Object parameterObject = parameterObjectField.get(parameterHandler);
handler.decryptFieldHandler(parameterObject);
} catch (Exception e) {
log.error(“对请求参数进行解密还原操作异常:”, e);
}
}
/**
对请求参数进行解密还原操作
@param args
*/
private void checkEncryptByUpdate (Object[] args) {
try {
Arrays.stream(args).forEach(handler::decryptFieldHandler);
} catch (Exception e) {
log.error(“对请求参数进行解密还原操作异常:”, e);
}
}
}
复制代码
在上述拦截器中,除了对入参进行加密和查询结果解密操作外,还多了一步对请求参数进行解密还原操作。
这是因为对请求参数进行加密操作时改动的是原对象,如果不还原解密数据,这个对象如果在后续还有其他操作,那就会使用密文,导致数据紊乱。
这里其实想过不改动原对象,而是把原请求对象克隆一份,在克隆对象上进行加密,然后在去查询数据库。可惜可能是自己对mybatis不够熟悉吧,试了很久也不能把mybatis内的原对象替换为克隆对象,所以才就想了这个还原解密参数的方式。
如果对请求参数对象和查询结果对象里的所有字段都进行加解密,那上述配置就基本完成。但在本次安全加解密需求中只针对指定字段(如手机号和真实姓名),现在这种全量字段加解密就不行,而且性能也低,毕竟加解密是很耗费服务器CPU运算资源的。
所以需要增加注解,在指定对象的属性字段才进行加解密。
/**
作用于类:标识当前实体需要进行结果解密操作.
作用于字段:标识当前实体的字段需要进行加解密操作.
作用于方法:标识当前mapper方法会被切面进行拦截,并进行数据的加解密操作.
注意:如果作用于字段,那当前类必须先标注该注解,因为会优先判断类是否需要加解密,然后在判断字段是否需要加解密,否则只作用于字段不会起效
@author zrh
@date 2022/1/4
*/
@Documented
@Inherited
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Crypt {
/**
*/
boolean decrypt () default true;
/**
*/
boolean encrypt () default true;
/**
*/
boolean subObject () default false;
/**
*/
int[] encryptParamIndex () default {};
}
复制代码
/**
AES加密工具
@author zrh
@date 2022/1/3
*/
@Slf4j
public final class AesTools {
private AesTools () {
}
private static final String KEY_ALGORITHM = “AES”;
private static final String ENCODING = “UTF-8”;
private static final String DEFAULT_CIPHER_ALGORITHM = “AES/ECB/PKCS5Padding”;
private static Cipher ENCODING_CIPHER = null;
private static Cipher DECRYPT_CIPHER = null;
/**
*/
private static final String KEY = “cab041-3c46-fed5”;
static {
try {
// 初始化cipher
ENCODING_CIPHER = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
DECRYPT_CIPHER = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
//转化成JAVA的密钥格式
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(“ASCII”), KEY_ALGORITHM);
ENCODING_CIPHER.init(Cipher.ENCRYPT_MODE, keySpec);
DECRYPT_CIPHER.init(Cipher.DECRYPT_MODE, keySpec);
} catch (Exception e) {
log.error(“初始化mybatis -> AES加解密参数异常:”, e);
}
}
/**
AES加密
@param content 加密内容
@return
*/
public static String encryptECB (String content) {
if (StringUtils.isEmpty(content)) {
return content;
}
String encryptStr = content;
try {
byte[] encrypted = ENCODING_CIPHER.doFinal(content.getBytes(ENCODING));
encryptStr = Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
log.info(“mybatis -> AES加密出错:{}”, content);
}
return encryptStr;
}
/**
AES解密
@param content 解密内容
@return
*/
public static String decryptECB (String content) {
if (StringUtils.isEmpty(content)) {
return content;
}
String decryptStr = content;
try {
byte[] decrypt = DECRYPT_CIPHER.doFinal(Base64.getDecoder().decode(content));
decryptStr = new String(decrypt, ENCODING);
} catch (Exception e) {
log.info(“mybatis -> AES解密出错:{}”, content);
}
return decryptStr;
}
}
复制代码
MybatisCryptHandler是对请求入参对象和查询结果对象进行加解密操作工具类。
代码稍许复杂,但实现逻辑简单,主要为了防止重复加密,内置缓存,对递归对象扫描检索,反射+注解获取需要加解密字段等。
/**
@author zrh
@date 2022/1/2
*/
@Slf4j
@Component
public class MybatisCryptHandler {
private final static ThreadLocal THREAD_LOCAL = ThreadLocal.withInitial(() -> new ArrayList());
private static final List EMPTY_FIELD_ARRAY = new ArrayList();
/**
*/
private static final Map<Class<?>, List> declaredFieldsCache = new ConcurrentHashMap<>(256);
/**
参数对外加密方法
@param handler
*/
public void parameterEncrypt (ParameterHandler handler) {
Object parameterObject = handler.getParameterObject();
if (null == parameterObject || parameterObject instanceof String) {
return;
}
encryptFieldHandler(parameterObject);
removeLocal();
}
/**
参数加密规则方法
@param sourceObject
*/
private void encryptFieldHandler (Object sourceObject) {
if (null == sourceObject) {
return;
}
if (sourceObject instanceof Map) {
((Map<?, Object>) sourceObject).values().forEach(this::encryptFieldHandler);
return;
}
if (sourceObject instanceof List) {
((List<?>) sourceObject).stream().forEach(this::encryptFieldHandler);
return;
}
Class<?> clazz = sourceObject.getClass();
if (!clazz.isAnnotationPresent(Crypt.class)) {
return;
}
if (checkLocal(sourceObject)) {
return;
}
setLocal(sourceObject);
try {
Field[] declaredFields = clazz.getDeclaredFields();
// 获取满足加密注解条件的字段
final List collect = Arrays.stream(declaredFields).filter(this::checkEncrypt).collect(Collectors.toList());
for (Field item : collect) {
item.setAccessible(true);
Object value = item.get(sourceObject);
if (null != value && value instanceof String) {
item.set(sourceObject, AesTools.encryptECB((String) value));
}
}
} catch (Exception e) {
}
}
/**
解析注解 - 加密密方法
@param field
@return
*/
private boolean checkEncrypt (Field field) {
Crypt crypt = field.getAnnotation(Crypt.class);
return null != crypt && crypt.encrypt();
}
/**
查询结果对外解密方法
@param resultData
*/
public Object resultDecrypt (Object resultData) {
if (resultData instanceof List) {
return ((List<?>) resultData).stream().map(this::resultObjHandler).collect(Collectors.toList());
}
return resultObjHandler(resultData);
}
/**
查询结果解密规则方法
@param result
*/
private Object resultObjHandler (Object result) {
if (null == result) {
return null;
}
Class<?> clazz = result.getClass();
//获取所有要解密的字段
Field[] declaredFields = getAllFieldsCache(clazz);
Arrays.stream(declaredFields).forEach(item -> {
try {
item.setAccessible(true);
Object value = item.get(result);
if (null != value && value instanceof String) {
item.set(result, AesTools.decryptECB((String) value));
}
} catch (Exception e) {
log.error(“DecryptException -> checkDecrypt:”, e);
}
});
Arrays.stream(declaredFields).filter(item -> checkSubObject(item)).forEach(item -> {
item.setAccessible(true);
try {
Object data = item.get(result);
if (data instanceof List) {
((List<?>) data).forEach(this::resultObjHandler);
}
} catch (IllegalAccessException e) {
log.error(“DecryptException -> checkSubObject:{}”, e);
}
});
return result;
}
/**
解析注解 - 解密方法
@param field
@return
*/
private static boolean checkDecrypt (Field field) {
Crypt crypt = field.getAnnotation(Crypt.class);
return null != crypt && crypt.decrypt();
}
/**
解析注解 - 子对象
@param field
@return
*/
private static boolean checkSubObject (Field field) {
Crypt crypt = field.getAnnotation(Crypt.class);
return null != crypt && crypt.subObject();
}
/**
对请求参数进行解密还原,
@param requestObject
*/
public void decryptFieldHandler (Object requestObject) {
if (null == requestObject) {
return;
}
if (requestObject instanceof Map) {
((Map<?, Object>) requestObject).values().forEach(this::decryptFieldHandler);
return;
}
if (requestObject instanceof List) {
((List<?>) requestObject).stream().forEach(this::decryptFieldHandler);
return;
}
Class<?> clazz = requestObject.getClass();
if (!clazz.isAnnotationPresent(Crypt.class)) {
return;
}
try {
Field[] declaredFields = clazz.getDeclaredFields();
// 获取满足加密注解条件的字段
final List collect = Arrays.stream(declaredFields).filter(this::checkEncrypt).collect(Collectors.toList());
for (Field item : collect) {
item.setAccessible(true);
Object value = item.get(requestObject);
if (null != value && value instanceof String) {
item.set(requestObject, AesTools.decryptECB((String) value));
}
}
} catch (Exception e) {
}
}
/**
统一管理内存
@param o
@return
*/
private boolean checkLocal (Object o) {
return THREAD_LOCAL.get().contains(o);
}
private void setLocal (Object o) {
THREAD_LOCAL.get().add(o);
}
private void removeLocal () {
THREAD_LOCAL.get().clear();
}
/**
获取本类及其父类的属性的方法
@param clazz 当前类对象
@return 字段数组
*/
private static Field[] getAllFields (Class<?> clazz) {
List fieldList = new ArrayList<>();
while (clazz != null) {
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
return fieldList.toArray(fields);
}
/**
获取本类及其父类的属性的方法
@param clazz 当前类对象
@return 字段数组
*/
private static Field[] getAllFieldsCache (Class<?> clazz) {
List fieldList = new ArrayList<>();
while (clazz != null) {
if (clazz.isAnnotationPresent(Crypt.class)) {
fieldList.addAll(getDeclaredFields(clazz));
}
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
return fieldList.toArray(fields);
}
private static List getDeclaredFields (Class<?> clazz) {
List result = declaredFieldsCache.get(clazz);
if (result == null) {
try {
// 获取满足注解解密条件的字段
result = Arrays.stream(clazz.getDeclaredFields()).filter(MybatisCryptHandler::checkDecrypt).collect(Collectors.toList());
// 放入本地缓存
declaredFieldsCache.put(clazz, (result.isEmpty() ? EMPTY_FIELD_ARRAY : result));
} catch (Exception e) {
log.error(“getDeclaredFields:”, e);
}
}
return result;
}
}
复制代码
数据表准备
用户的敏感信息包括有手机号、真实姓名、身份证、银行卡号、支付宝账号等几种。下面使用手机号和姓名字段进行加解密案例。
先准备一张Mysql数据表,表里有两个手机号和两个姓名字段,可以用于安全加解密对比。
CREATE TABLE phone_data
(
id
int(11) NOT NULL AUTO_INCREMENT COMMENT ‘主键’,
phone
varchar(122) DEFAULT NULL COMMENT ‘明文手机号’,
user_phone
varchar(122) DEFAULT NULL COMMENT ‘密文手机号’,
name
varchar(122) DEFAULT NULL COMMENT ‘明文姓名’,
real_name
varchar(122) DEFAULT NULL COMMENT ‘密文姓名’,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT=‘测试加解密数据表’;
复制代码
还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!
王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。
对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!
【完整版领取方式在文末!!】
93道网络安全面试题
内容实在太多,不一一截图了
最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。