这个话题很大,我只是把我经历的或者说知道的,写一写,总总结,我并不是这方面的高手。
1 安全基础
1.1 国密算法
国家商用密码定义了一系列算法,我了解到的是SM2、SM3、SM4,因为国家对一些系统有安全要求,必须通过支持这三种算法,颁布相应授权证书。国密算法(国家商用密码算法简介)。
SM2是替代RSA的算法,算法流程参考图解SM2算法流程(合
几个算法对比可以参见 国密算法SM1/SM2/SM3/SM4,我并不关心算法的实现,因为我只需要购买加密机就可以了,只要加密机支持这些算法,对接加密机就可以了。
从下图很容易想到几个问题
1、根密钥如何保证安全
应用系统使用的密钥是分散出来的,那么根密钥很容易想到也应该做分散。然后就是根密钥的灌装了。
2、这个前置与加密机之前的通讯安全如何保障?
应用系统不直连加密机,那么就需要设计一个前置系统。那么前置系统与机密机之间的安全设计呢?从下图可以看到加密机管理控制台有哪些功能,
这里面可以想象到加密机的前置系统,与加密机之间的接口通讯,采用了对称了加密,他们在内网,通过设置ip白名单进行授信。
3、应用系统通过前置与加密机通信?
前置和加密机也大都会采用对成加密,但是接口报文的加、解密是在交给加密机来处理,所以报文是安全的,而被截获的报文,即使知道了应用与加密机的加密密钥,那么也是密文,还是不知道如何解密。
1.2 MD5、SHA摘要算法
摘要算法很厉害,因为他不可逆,计算一个文件是否更改,只要看看他的摘要是否发生变化就可以。我的应用场景主要是在登录的时候使用。第2.3.3章 WEB系统最佳实践属性配置之shiro.properties,第2.1.7章 WEB系统最佳实践Spring文件配置之spring-shiro.xml这两篇文章提到我的计算使用的是SHA-1,应用的shiro权限框架,这里稍微带一带。下图的密码流程,很容易就想到几个问题
1、客户端做MD5值,再调用后台请求之前,MD5做几次呢?
客户端一定是计算摘要的,是不是选择MD5算法抛开一边。但是一次MD5是可以破解的,比如在线MD5破解,密码通常6位~20位,MD5值是32位,因此再做一次MD5值,就相当难破解了,所以客户端只需要做2次MD5就可以了。
2、SHA-1安全吗
2017年2月23日,CWI Amsterdam与Google宣布了一个成功的SHA-1碰撞攻击,我相信有人攻击我的代价和他的收益没那么大,所以可以放心使用了。但是shiro也可以支持sha2.
3、加盐值是怎么产生的?
给出8位的长度,随机生成16个字节的加盐值,不容易猜吧。
byte[] salt = DigestsUtil.generateSalt(saltSize);
record.setSalt(EncodesUtil.encodeHex(salt));
4、多少次迭代合适呢?
网上大多数采用的是1024,虽然我不知道为什么,应该够用了。
5、保存到数据库中的密码安全吗?
最终存放到数据的密码长度为40位,在数据库中是无法猜到用户的账号密码的,内贼是可以防住了。可是运维的权限呢,如果运维更改了账号和密码,临时登录进去,再改回来。这就需要数据库的审计功能了。
1.3 数字签名
数字签名是保障信息的不可抵赖性。现在的开放接口大多提供一个appId和appKey给到接入方,可以看看蚂蚁金赋开放平台的做法,照着做就是了。实际应用,大多会砍一砍。
从下面可以思考几个问题,既然是数字签名,那么其实算法是公开的,那么问题就在于如何让appKey保密。
1、appKey存放到哪里呢
很多时候appKey写到了后台配置文件中,如果人员离职,这个appKey就带走了,不过还好大多数开放平台,appKey都可以更新,appId是不变的。
2、前端后端分离的appKey暴露了怎么办?
比如使用springboot+vue做前后端分离或者js访问开放平台的,有些人把appKey写到了前端,直接暴露有人觉得很危险,于是想到了,将appKey做分散,或者js做了混淆,只是增加了难度,而那些做爬虫的人,才不关心你怎么封装的,直接抓包看请求,这就需要用到了数字证书了。
从下图可以看到密码传输到终端用户,为什么很多企业用户花钱需要手机短信把,因为解决了密码传输的问题。
appId作为参数传到后台
private <T extends AlipayResponse> RequestParametersHolder getRequestHolderWithSign(BatchAlipayRequest request) throws AlipayApiException {
List<AlipayRequestWrapper> requestList = request.getRequestList();
//校验接口列表非空
if (requestList == null || requestList.isEmpty()) {
throw new AlipayApiException("40", "client-error:api request list is empty");
}
RequestParametersHolder requestHolder = new RequestParametersHolder();
//添加协议必须参数
AlipayHashMap protocalMustParams = new AlipayHashMap();
protocalMustParams.put(AlipayConstants.METHOD, request.getApiMethodName());
protocalMustParams.put(AlipayConstants.APP_ID, this.appId);
protocalMustParams.put(AlipayConstants.SIGN_TYPE, this.signType);
protocalMustParams.put(AlipayConstants.CHARSET, charset);
protocalMustParams.put(AlipayConstants.VERSION, request.getApiVersion());
if (request.isNeedEncrypt()) {
protocalMustParams.put(AlipayConstants.ENCRYPT_TYPE, this.encryptType);
}
//添加协议可选参数
AlipayHashMap protocalOptParams = new AlipayHashMap();
protocalOptParams.put(AlipayConstants.FORMAT, format);
protocalOptParams.put(AlipayConstants.ALIPAY_SDK, AlipayConstants.SDK_VERSION);
requestHolder.setProtocalOptParams(protocalOptParams);
Long timestamp = System.currentTimeMillis();
DateFormat df = new SimpleDateFormat(AlipayConstants.DATE_TIME_FORMAT);
df.setTimeZone(TimeZone.getTimeZone(AlipayConstants.DATE_TIMEZONE));
protocalMustParams.put(AlipayConstants.TIMESTAMP, df.format(new Date(timestamp)));
requestHolder.setProtocalMustParams(protocalMustParams);
//设置业务参数
AlipayHashMap appParams = new AlipayHashMap();
//构造请求主题
StringBuilder requestBody = new StringBuilder();
// 组装每个API的请求参数
for (int index = 0; index < requestList.size(); index++) {
AlipayRequestWrapper alipayRequestWrapper = requestList.get(index);
AlipayRequest alipayRequest = alipayRequestWrapper.getAlipayRequest();
Map<String, Object> apiParams = alipayRequest.getTextParams();
apiParams.put(AlipayConstants.METHOD, alipayRequest.getApiMethodName());
apiParams.put(AlipayConstants.APP_AUTH_TOKEN, alipayRequestWrapper.getAppAuthToken());
apiParams.put(AlipayConstants.ACCESS_TOKEN, alipayRequestWrapper.getAccessToken());
apiParams.put(AlipayConstants.PROD_CODE, alipayRequest.getProdCode());
apiParams.put(AlipayConstants.NOTIFY_URL, alipayRequest.getNotifyUrl());
apiParams.put(AlipayConstants.RETURN_URL, alipayRequest.getReturnUrl());
apiParams.put(AlipayConstants.TERMINAL_INFO, alipayRequest.getTerminalInfo());
apiParams.put(AlipayConstants.TERMINAL_TYPE, alipayRequest.getTerminalType());
apiParams.put(AlipayConstants.BATCH_REQUEST_ID, String.valueOf(index));
// 仅当API包含biz_content参数且值为空时,序列化bizModel填充bizContent
try {
if (alipayRequest.getClass().getMethod("getBizContent") != null
&& StringUtils.isEmpty(appParams.get(AlipayConstants.BIZ_CONTENT_KEY))
&& alipayRequest.getBizModel() != null) {
apiParams.put(AlipayConstants.BIZ_CONTENT_KEY,
new JSONWriter().write(alipayRequest.getBizModel(), true));
}
} catch (NoSuchMethodException e) {
// 找不到getBizContent则什么都不做
} catch (SecurityException e) {
AlipayLogger.logBizError(e);
}
requestBody.append(new JSONWriter().write(apiParams, false));
if (index != requestList.size() - 1) {
requestBody.append(BATCH_API_DEFAULT_SPLIT);
}
}
appParams.put(AlipayConstants.BIZ_CONTENT_KEY, requestBody.toString());
// 只有新接口和设置密钥才能支持加密
if (request.isNeedEncrypt()) {
if (StringUtils.isEmpty(appParams.get(AlipayConstants.BIZ_CONTENT_KEY))) {
throw new AlipayApiException("当前API不支持加密请求");
}
// 需要加密必须设置密钥和加密算法
if (StringUtils.isEmpty(this.encryptType) || getEncryptor() == null) {
throw new AlipayApiException("API请求要求加密,则必须设置密钥类型和加密器");
}
String encryptContent = getEncryptor().encrypt(
appParams.get(AlipayConstants.BIZ_CONTENT_KEY), this.encryptType, this.charset);
appParams.put(AlipayConstants.BIZ_CONTENT_KEY, encryptContent);
}
requestHolder.setApplicationParams(appParams);
if (!StringUtils.isEmpty(this.signType)) {
String signContent = AlipaySignature.getSignatureContent(requestHolder);
protocalMustParams.put(AlipayConstants.SIGN,
getSigner().sign(signContent, this.signType, charset));
} else {
protocalMustParams.put(AlipayConstants.SIGN, "");
}
return requestHolder;
}
AlipaySignature
这个类可以看到参数根据字母的顺序进行排序
public static String getSignContent(Map<String, String> sortedParams) {
StringBuffer content = new StringBuffer();
List<String> keys = new ArrayList<String>(sortedParams.keySet());
Collections.sort(keys);
int index = 0;
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = sortedParams.get(key);
if (StringUtils.areNotEmpty(key, value)) {
content.append((index == 0 ? "" : "&") + key + "=" + value);
index++;
}
}
return content.toString();
}
DefaultSigner
中包含了签名的方法
public String sign(String sourceContent, String signType, String charset) {
String sign = null;
try {
sign = AlipaySignature.rsaSign(sourceContent, this.privateKey, charset, signType);
} catch (AlipayApiException e) {
throw new RuntimeException(e);
}
return sign;
}
1.4 数字证书
私钥加密,公钥解密,这个大家都很容理解,自己写的证书,浏览器是不识别的,因为没有授权。很容易理解,你自己颁布的证书,只有你自己可以认,虽然也是对称的,但是并不是CA机构所认可。数字证书解决通讯安全的问题,也就是说抓包是被加密的。但是它并不能解决源头授信的问题。
下图是参考一个spring-cloud项目,生成公私钥对,以及公私钥对存储的流程。
java学习-AES加解密之AES-128-CBC算法是对称算法,这里可自行指定对称算法。按照下图的流程,公私钥的存储周期,可以通过redis的销毁机制来控制。这里可以思考几个问题
1、私钥存到密钥的加密因子在哪里?
这个是固定的,很多人也都会想到写到配置文件中。
2、对离职人员的防范
哔哩哔哩源码的泄露是来自内部员工,史上最大账号密码数据库泄露,41GB数据文件出现在暗网,这些案例可以看到,基本出问题来自内部。采用下图的方式,是可以一定程度上防范。因为公私钥对有生命周期,即使开发人员或者SE知道算法,但是生成过程并不会干预。
2 访问控制
2.1 系统之间
2.1.1 每30秒获取允许访问的清单
既然Eureka作为服务中心,调用者订阅服务即可,不用关心服务提供者是谁,思考一下:
1 为何获取这个允许放单的清单从哪里来呢?
ServiceAuthRestInterceptor
这段代码可以看出这个配置实际上是从数据库中获取的,这是一个技术业务问题。
@Override
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 配置该注解,说明不进行服务拦截
CheckClientToken annotation = handlerMethod.getBeanType().getAnnotation(CheckClientToken.class);
IgnoreClientToken ignoreClientToken = handlerMethod.getMethodAnnotation(IgnoreClientToken.class);
if (annotation == null) {
annotation = handlerMethod.getMethodAnnotation(CheckClientToken.class);
}
if (annotation == null || ignoreClientToken != null) {
return super.preHandle(request, response, handler);
} else {
String token = request.getHeader(serviceAuthConfig.getTokenHeader());
try {
IJWTInfo infoFromToken = serviceAuthUtil.getInfoFromToken(token);
String uniqueName = infoFromToken.getUniqueName();
for (String client : serviceAuthUtil.getAllowedClient()) {
if (client.equals(uniqueName)) {
return super.preHandle(request, response, handler);
}
}
}catch(ClientTokenException ex){
response.setStatus(HttpStatus.FORBIDDEN.value());
logger.error(ex.getMessage(),ex);
response.setContentType("UTF-8");
response.getOutputStream().println(JSON.toJSONString(new BaseResponse(ex.getStatus(), ex.getMessage())));
return false;
}
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.getOutputStream().println(JSON.toJSONString(new BaseResponse(RestCodeConstants.EX_CLIENT_FORBIDDEN_CODE,"Client is Forbidden!")));
return false;
}
}
2 拦截器
授权系统拦截
@Configuration("securityWebConfig")
@Primary
public class WebConfiguration extends WebMvcConfigurerAdapter {
@Bean
GlobalExceptionHandler getGlobalExceptionHandler() {
return new GlobalExceptionHandler();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
/*
增加服务权限烂机器
*/
registry.addInterceptor(getServiceAuthRestInterceptor()).addPathPatterns("/**");
/*
增加用户权限拦截器
*/
registry.addInterceptor(getUserAuthRestInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
/**
* 配置服务权限拦截
* @return
*/
@Bean
ServiceAuthRestInterceptor getServiceAuthRestInterceptor() {
return new ServiceAuthRestInterceptor();
}
/**
* 配置用户用户token拦截
* @return
*/
@Bean
UserAuthRestInterceptor getUserAuthRestInterceptor() {
return new UserAuthRestInterceptor();
}
}
auth:
serviceId: ace-auth
user:
token-header: Authorization
limit-expire: 1440 # 一天过期
client:
id: your-system
secret: 123456
token-header: client-token
@Scheduled(cron = "0/30 * * * * ?")
public void refreshAllowedClient() {
log.debug("refresh allowedClient.....");
BaseResponse resp = serviceAuthFeign.getAllowedClient(serviceAuthConfig.getClientId(), serviceAuthConfig.getClientSecret());
if (resp.getStatus() == 200) {
ObjectRestResponse<List<String>> allowedClient = (ObjectRestResponse<List<String>>) resp;
this.allowedClient = allowedClient.getData();
}
}
2.1.2 access token
access token的产生使用到了一个第三方jar,通过私钥进行了加密,通过公钥进行解密.
在系统初始化的时候,公私钥就产生了。
1 token的产生
@Configuration
@Slf4j
public class AuthServerRunner implements CommandLineRunner {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String REDIS_USER_PRI_KEY = "AG:AUTH:JWT:PRI";
private static final String REDIS_USER_PUB_KEY = "AG:AUTH:JWT:PUB";
private static final String REDIS_SERVICE_PRI_KEY = "AG:AUTH:CLIENT:PRI";
private static final String REDIS_SERVICE_PUB_KEY = "AG:AUTH:CLIENT:PUB";
@Autowired
private KeyConfiguration keyConfiguration;
@Autowired
private AECUtil aecUtil;
@Autowired
private RsaKeyHelper rsaKeyHelper;
@Autowired
private GatewayRouteBiz gatewayRouteBiz;
@Override
public void run(String... args) throws Exception {
boolean flag = false;
if (redisTemplate.hasKey(REDIS_USER_PRI_KEY)&&redisTemplate.hasKey(REDIS_USER_PUB_KEY)&&redisTemplate.hasKey(REDIS_SERVICE_PRI_KEY)&&redisTemplate.hasKey(REDIS_SERVICE_PUB_KEY)) {
try {
keyConfiguration.setUserPriKey(rsaKeyHelper.toBytes(aecUtil.decrypt(redisTemplate.opsForValue().get(REDIS_USER_PRI_KEY).toString())));
keyConfiguration.setUserPubKey(rsaKeyHelper.toBytes(redisTemplate.opsForValue().get(REDIS_USER_PUB_KEY).toString()));
keyConfiguration.setServicePriKey(rsaKeyHelper.toBytes(aecUtil.decrypt(redisTemplate.opsForValue().get(REDIS_SERVICE_PRI_KEY).toString())));
keyConfiguration.setServicePubKey(rsaKeyHelper.toBytes(redisTemplate.opsForValue().get(REDIS_SERVICE_PUB_KEY).toString()));
}catch (Exception e){
log.error("初始化公钥/密钥异常...",e);
flag = true;
}
} else {
flag = true;
}
if(flag){
Map<String, byte[]> keyMap = rsaKeyHelper.generateKey(keyConfiguration.getUserSecret());
keyConfiguration.setUserPriKey(keyMap.get("pri"));
keyConfiguration.setUserPubKey(keyMap.get("pub"));
redisTemplate.opsForValue().set(REDIS_USER_PRI_KEY, aecUtil.encrypt(rsaKeyHelper.toHexString(keyMap.get("pri"))));
redisTemplate.opsForValue().set(REDIS_USER_PUB_KEY, rsaKeyHelper.toHexString(keyMap.get("pub")));
keyMap = rsaKeyHelper.generateKey(keyConfiguration.getServiceSecret());
keyConfiguration.setServicePriKey(keyMap.get("pri"));
keyConfiguration.setServicePubKey(keyMap.get("pub"));
redisTemplate.opsForValue().set(REDIS_SERVICE_PRI_KEY, aecUtil.encrypt(rsaKeyHelper.toHexString(keyMap.get("pri"))));
redisTemplate.opsForValue().set(REDIS_SERVICE_PUB_KEY, rsaKeyHelper.toHexString(keyMap.get("pub")));
}
log.info("完成公钥/密钥的初始化...");
List<GatewayRoute> gatewayRoutes = gatewayRouteBiz.selectListAll();
redisTemplate.opsForValue().set(RedisKeyConstants.ZUUL_ROUTE_KEY, JSON.toJSONString(gatewayRoutes));
log.info("完成网关路由的更新...");
}
}
public String generateToken(IJWTInfo jwtInfo) throws Exception {
return jwtHelper.generateToken(jwtInfo, keyConfiguration.getServicePriKey(), expire);
}
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.7.0version>
dependency>
@Scheduled(cron = "0 0/10 * * * ?")
public void refreshClientToken() {
log.debug("refresh client token.....");
BaseResponse resp = serviceAuthFeign.getAccessToken(serviceAuthConfig.getClientId(), serviceAuthConfig.getClientSecret());
if (resp.getStatus() == 200) {
ObjectRestResponse<String> clientToken = (ObjectRestResponse<String>) resp;
this.clientToken = clientToken.getData();
}
}
public IJWTInfo getInfoFromToken(String token) throws Exception {
try {
IJWTInfo infoFromToken = jwtHelper.getInfoFromToken(token, serviceAuthConfig.getPubKeyByte());
Date current = infoFromToken.getExpireTime();
if(new Date().after(current)){
throw new ClientTokenException("Client token expired!");
}
return infoFromToken;
} catch (ExpiredJwtException ex) {
throw new ClientTokenException("Client token expired!");
} catch (SignatureException ex) {
throw new ClientTokenException("Client token signature error!");
} catch (IllegalArgumentException ex) {
throw new ClientTokenException("Client token is null or empty!");
}
}
2 token检查
这里可以看到拦截器,通过反编译,将带CheckClientToken 注解的请求都拦截下来了。加了注解,就需要验证token。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 配置该注解,说明不进行服务拦截
CheckClientToken annotation = handlerMethod.getBeanType().getAnnotation(CheckClientToken.class);
IgnoreClientToken ignoreClientToken = handlerMethod.getMethodAnnotation(IgnoreClientToken.class);
if (annotation == null) {
annotation = handlerMethod.getMethodAnnotation(CheckClientToken.class);
}
if (annotation == null || ignoreClientToken != null) {
return super.preHandle(request, response, handler);
} else {
String token = request.getHeader(serviceAuthConfig.getTokenHeader());
try {
IJWTInfo infoFromToken = serviceAuthUtil.getInfoFromToken(token);
String uniqueName = infoFromToken.getUniqueName();
for (String client : serviceAuthUtil.getAllowedClient()) {
if (client.equals(uniqueName)) {
return super.preHandle(request, response, handler);
}
}
}catch(ClientTokenException ex){
response.setStatus(HttpStatus.FORBIDDEN.value());
logger.error(ex.getMessage(),ex);
response.setContentType("UTF-8");
response.getOutputStream().println(JSON.toJSONString(new BaseResponse(ex.getStatus(), ex.getMessage())));
return false;
}
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.getOutputStream().println(JSON.toJSONString(new BaseResponse(RestCodeConstants.EX_CLIENT_FORBIDDEN_CODE,"Client is Forbidden!")));
return false;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD,ElementType.TYPE})
public @interface CheckClientToken {
}
这里配置那些需要检查客户端token
@RestController
@RequestMapping("depart")
@CheckClientToken
@CheckUserToken
public class DepartController extends BaseController<DepartBiz,Depart> {}
token拦截器得到token后,如果token过期,那么就刷新token。那么客户端token是否过期呢,则需要在客户端调用时,设置token过期标志,那么带来的问题是,为什么客户端自己不去刷新token呢?这就是上图中的access token是1个小时刷新一次,那么当在调用的时候,刚好1个小时呢,不就出问题了,所以这里做了一下主动刷新token。
@Component
@Log
public class OkHttpTokenInterceptor implements Interceptor {
@Autowired
@Lazy
private ServiceAuthUtil serviceAuthUtil;
@Autowired
@Lazy
private ServiceAuthConfig serviceAuthConfig;
@Autowired
@Lazy
private UserAuthConfig userAuthConfig;
@Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request()
.newBuilder()
.header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
.build();
Response response = chain.proceed(newRequest);
if(HttpStatus.FORBIDDEN.value()==response.code()){
if(response.body().string().contains(String.valueOf(RestCodeConstants.EX_CLIENT_INVALID_CODE))){
log.info("Client Token Expire,Retry to request...");
serviceAuthUtil.refreshClientToken();
newRequest = chain.request()
.newBuilder()
.header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
.header(serviceAuthConfig.getTokenHeader(),serviceAuthUtil.getClientToken())
.build();
response = chain.proceed(newRequest);
}
}
return response;
}
}
2.1.3 获取公钥
获取公钥由系统自动完成,避免了人为干预。
@Scheduled(cron = "0 0/1 * * * ?")
public void refreshUserPubKey(){
BaseResponse resp = serviceAuthFeign.getUserPublicKey(serviceAuthConfig.getClientId(), serviceAuthConfig.getClientSecret());
if (resp.getStatus() == HttpStatus.OK.value()) {
ObjectRestResponse<byte[]> userResponse = (ObjectRestResponse<byte[]>) resp;
this.userAuthConfig.setPubKeyByte(userResponse.getData());
}
}
@Scheduled(cron = "0 0/1 * * * ?")
public void refreshServicePubKey(){
BaseResponse resp = serviceAuthFeign.getServicePublicKey(serviceAuthConfig.getClientId(), serviceAuthConfig.getClientSecret());
if (resp.getStatus() == HttpStatus.OK.value()) {
ObjectRestResponse<byte[]> userResponse = (ObjectRestResponse<byte[]>) resp;
this.serviceAuthConfig.setPubKeyByte(userResponse.getData());
}
}
3 网络安全
3.1 VPN
VPN是企业网在internet等公共网络上的延伸,它通过一个私有的通道在公共网络上创建一个安全的私有连接。
五分钟搞懂内网和外网之间的通信的原理,先搞明白公网ip和私有ip,大家知道企业宽带很贵,首先因为他有固定的公网ip,很容易理解出他是独享的,因为运营商可以让多个家庭宽带使用同一个公网ip,企业宽带凭什么比民用宽带贵?有没有更便宜的企业宽带,一般企业用户都要求是双线。那么什么是双线呢?
什么是双线宽带,这里提到了南电信北网通的问题,也就是南北网络互通的问题。
有了固定ip,就可以解决异地机房通信的问题。先看一下深信服IPSec VPN快速配置文档,通过防火墙的配置,可以实现家庭宽带与企业网络建立虚拟专用网,说白话,就是企业网访问家庭网络,就像访问内网一样。思考几个问题
1、家庭宽带和家庭宽带是不是没法实现呢?
没有固定ip,连对方是谁也不清楚,连通讯都建立不起来,又怎么建立转有通道呢,有防火墙也没有用。
2、企业网络与企业网络之前一定能建立VPN吗?
例如阿里云与企业网络建立,怎么建立呢?
3、IPSec协议的工作原理是什么?
建立网络专用通道,那么很容易理解IPSec是网络层协议,IPSec简介,VPN和IPsec协议
3.2 防火墙
防火墙监控到内网有一台机器一直在请求DNS服务器,分析:
1、先分析一下HTTP请求流程
一次完整的HTTP请求过程,域名解析会先搜索浏览器自身的DNS缓存,缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存。
如果通过程序请求,而不是浏览器,会有DNS缓存吗?再看看这篇文章一文搞懂DNS缓存,浏览器和应用程序以及IPS网络运营商都会对DNS进行缓存。如果后台程序只访问一个地址,为什么会一直发送DNS呢?
2、为什么总是在请求DNS服务器呢?
防火墙抓包,全是在请求DNS。什么情况才会导致不断请求DNS呢?如果后台程序有循环bug,一直在请求某个网站,那么是否DNS就请求频繁呢?
通过zabbix可以监控到网络的请求情况,那么
1、Incoming指的是什么呢?
2、Outgoing指的是什么呢?
3、网络流量算大吗?