java 开发必备的安全架构知识

 

1.XXS攻击

原因:

XXS攻击是js脚本攻击,将网站上传给后台的参数设置成js脚本格式:<script>...<script/>格式,当另外一个页面需要展示这个参数时就会加载此参数,从而造成<body>${name}<body/>变成<body><script>...<scritp/><body/>形式,执行脚本文件。

黑客会利用此漏洞进行页面非法跳转等,如钓鱼网站,或尝试用js读取本地cookie文件信息后发送给自己。

解决:

java后台创建自定义过滤器,将请求中的所有<>这样的特殊字段全部转义成%lt格式。

 

2.sql注入

原因:

后台sql拼接时没有用预编译格式,myabits中表现形式为${name},底层jdbc表现形式为statement拼接。如:where password = '' or 1=1。

另外,在一些项目中会有人使用java拼接sql语句,也会导致sql注入问题,因为sql在java中拼接不会进行转义。

解决:

mybatis中使用#{name}形式拼接,底层jdbc会使用preparestamtement预编译方式拼接sql。而preparestamtement的本质是将所有特殊符号比如=!()/等字符全部转义。

 

3.防盗链

原因:

其他网站、域名可以直接将本网站域名下的资源(可以是视频、图片等文件)引用,造成请求复用,没有访问本网站资源却在其他网站发送静态请求获取资源。

解决:

通过自定义filter,在filter处判断reference 字段,根据字段名称去数据库查询相应的域名,如果等于数据库中的白名单就放行,如果不等于就返回一张错误图片回去。可以放到网关处对静态资源进行优先判断

 

4.网络延迟引起的接口幂等性

原因:

因为网络延迟或者用户多次点击等原因,前台向后台发送多个请求,在表单提交处可能会引起多次提交造成数据重复。

解决:

不推荐悲观锁实现,建议使用临时token(实际最常用办法),在进入页面时,或者在发送请求前,就在后台生成一个唯一token存进sessionredis、或一个IOC容器(CurrentHashMap)中,将生成的token返回给前台。

前台发送请请求时带上token信息,前台一般可以存在隐藏input中或者,先从容器中取出并移除token,并重新生成一个唯一token存进容器,返回给前台。此办法可以解决多次提交性问题。

临时token推荐使用注解形式实现。

 

5.机器模拟请求攻击

原因:

黑客熟悉服务器临时token机制,通过代码不断获取临时token并且不断访问其他接口,造成服务器忙。

解决:

在请求发送前提示输入验证码,输入密码口令等。

 

6. 身份伪造请求问题(包含CSRF跨域攻击)

原因:

有两种形式。

第一种为伪造网站,让用户在伪造网站处调用目标服务器的接口,通过验证并执行程

序。

第二种为黑客通过抓包、XXS攻击等方式拿到持久性token(JWT、cookie等形式存到前台),

然后通过该token伪造身份去访问请求,服务器接收token后解析,执行程序。

解决:

第一种情况,因为不是服务器域名下可以采用禁止跨域,添加临时token,判断refere字段是否是本服务器白名单来解决。

第二种情况,因为是本服务器域名下,可以采用发送验证码,输入密码,验证身份等操作来保证是本人的操作。

 

7. 忘记密码漏洞

原因:

点击忘记密码,流程一般是发送验证码,然后填上几位数字验证码后设置新密码,如果这个步骤后台没有对接口进行限制,可以通过暴力**方法,多线程不断改变数字去访问接口,如果验证码位数过低很有可能短时间内就会**出来。

解决:

对修改密码接口进行限流和计数,超过5次就开启验证码功能,或者在网关API对短时间访问过多的异常IP直接封禁(黑名单系统)。

 

8. 上传文件漏洞

原因:

通常在一些jsp项目中,上传的静态资源没有存放到静态资源服务器中,而是存到了项目中,如果不对文件类型进行限制,黑客可以上传jsp文件并且在jsp文件中写上相应的java代码,直接调用该文件地址时就会执行内部的java代码,可能会危害整个服务器上的文件。

解决:

只对文件后缀进行判断无法阻止黑客上传可运行文件,正规做法是通过文件流的拼接,根据文件流字段上的二进制数据转成字符,根据字符判断文件类型。

 

9. 前端漏洞

原因:

部分系统讲用户的ID,手机号等信息保存在前端cookie或者隐藏域中,cookie中若不加密很可能会被别人篡改,同理隐藏域也是可以被篡改,在一些接口访问时会读取这些数据,造成他人数据泄露。

解决:

重要数据全部放到后台服务器上存取,前台只发送添加信息。

 

10. 注释漏洞

原因:

项目未上线时在js等静态文件上写注释,上线后使用本文件,造成他人看到注释内容,可能会泄露部分信息。

解决:

上限时将js静态文件全部压缩,压缩会自动取消注释并且节省空间。

 

11. API幂等性问题(手动实现redis-临时token注解)

这实际上和4.中的幂等性问题一样,是为了防止前台过多请求,使用redis+注解的形式,使用redis的好处是可以有一个过期时间,防止无用数据过多!前台可以通过JWT等方式把token信息存到用户客户端。

1.初始化redis配置,通过在redisService中创建对应的方法,常用的注解如下:

stringRedisTemplate.opsForValue().set(key, value);

stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);

stringRedisTemplate.opsForValue().get(key);

stringRedisTemplate.delete(key);

通过注入一个对应类型的redisTemplate,用该对象操作数据。

需要注意的是expire()方法是设置redis的过期时间key方法,注入IOC容器

2.创建redisUtils

创建getToken,该方法调用redisService获取token并存入reids并设置过期时间,findToken获取redis中的token并返回boolean,如果获取成功就抹去reids,注入IOC容器

3.创建自定义注解

4.创建自定义切面

切入点可以为execution表达式或注解,用表达式的话最好先判断一下AOP拦截到方法上是否有自定义注解,在环绕通知或前置通知处通过反射获取注解中的参数,调用对应tokenUtils的方法进一步判断返回结果,如果成功获取到token就执行原来的方法process()。

5.在进入某页面前就调用getToken方法,或者在发送请求前通过验证方式获取token

 

ps:需要注意的是,前台可能是通过表单提交也可能是ajax请求,前者通过隐藏域将token信息发到后台,后者通过requestHeader传参,注解需要加入参数类型判断是表单还是ajax请求。

 

12. 开放接口的安全性设计

个人理解,实际上不管是不是开发的接口,都应该有token机制防范模拟请求攻击和幂等性问题,但是开发性接口应该更加注重安全性,安全性是说必须保证是本系统去调用而不是其他系统(和单点登录安全相似),但是实现思想还是和之前一样。

比如调用微信的开发接口时要带上自己的token去访问,而自己的token实际上是根据微信官方的要求,用自己appId和appSecret调用接口拿到的,调用获取token接口会通过appId和appSecret共同通过算法得出新token返回给用户,同时更新到官方数据库中。

出去安全性,官方要将相关appId、appSecret(appId不变,appSecret会变)以及对应的生成token信息及时更新到数据库中,当对方发送请求时判断请求中的token和最新token对比,另外,还要通过isFalg字段判断该appId是否允许可用。

这样做即使appSecret被他人获取,也能通过修改appSecret的方式重新改变 token。

和之前一样,也可以用注解形式解决,因为开放接口相对不多,AOP灵活的特点符合此场景。

 

13. 个人理解的单点登录和异地登陆安全性解决方法

通过12中的例子,可以采用相同的办法来解决SSO和异地登陆。

异地登录:

可以通过用户名结合UUID等方式生成一个随机token,并且这些token都会有一个过期时间,前台通过jwt方式发送到客户端上,客户端发送请求时会带上此token信息,后台则将token信息存到redis中或数据库中,甚至session也行。

实际上,后台存到何处其实都行,主要考虑的是一些特殊情况,比如session过多会导致服务器内存紧张,数据库适用于访问量小的接口情况,redis则适用于很多场景但是需要定期从内存映射到硬盘上。

当第一次访问时,因为本地没有 token,服务器会产生一个新的token保存到后台,把新token发送给客户端。当A用户登录后B用户登录,因为之前的token还在,或者还未到期,此时就会发送一个不带token的请求去获取token,服务器生成新的token发给B,更新服务器上的token信息,同时A用户会出现重新登陆问题。

所以出现这种情况时,客户端获取token时可以顺便将自己的IP(手机是动态IP可能不行)或者设备号(可行,但是需要获取到权限)一起存到服务器端,服务器端收到未带有token的请求时就可以大概率判断出是不是本人来执行提示操作。

单点登录:

和异地登陆类似,多台服务器只要通过查询token方式就行,问题在于多台服务器上的数据同步问题,如果是将token存到了session中,同步的就需要是seesion,如果是存到了redis中,直接让多个服务器连同一个redis就行,如果是数据,同理也是查询同一个库。如何同步问题办法有很多,可以通过reids,mysql等方式,但是最好的办法还是通过redis进行存取。

服务器只要token数据同步,执行相同的token判断登陆程序,单点登录问题就自然解决了。

 

14.URL漏洞

原因:

传输数据时如果url上带有特殊字符,比如=+/?等,传输时如果不做特殊处理时会丢失信息,以httpClient为例,A传给B的数据没有参数中的特殊字符进行处理,B最后得到的数据就会是错误的。

解决:

参数中的特殊字符进行编码encode,使用java默认的编码工具就行,不要对整个参数编码。

 

15.对称加密

对称加密是双方约定使用同一把秘钥进行加密和解密,常见的如DES、AES等。

优点为加密解密相对快速省时间,缺点为有泄露风险。

因为加密解密是同一把秘钥,在一些C/S应用就很有可能被黑客反编译出源码,拿到加密的秘钥之后就可以**其他人的加密信息。

使用场景:服务器内部之间调用,如服务器用httpClient、RPC通讯。

16.非对称加密

非对称加密一般是说利用一对公钥加密,私钥解密,常见的如有RSA。

优点为相对安全,缺点为解析费事效率低。

秘钥存到服务器上黑客难以获取到,公钥存到客户端,即使被**也无法解析出原来的信息。开发人员提前用utils类生成一对公钥私钥保存到服务器上,之后用utils进行加密解密。

还有最近流行的Bcrypt属于特殊的非对称加密,每次加密的结果都不一样,但是可以通过和结果对比确定是否正确。

使用场景:C/S结构的项目,如APP,PC客户端

 

17.数字签名

单项加密常用于数字签名,通常都是传递参数+时间戳+固定盐生成一个数字签名,发送时携带数字签名,服务端只需要验证数字签名是否相等就能得知内容是否被篡改。

MD5加密特点是特定字符串只能生成相同的结果,最适合数字签名。

数字签名是最实用、必备的的加密手段。

使用场景:各种包含加密数据的请求

 

18.支付类接口安全性问题(基于令牌实现的接口调用)

在前台传输金额和商品信息以及用户id不能保证安全!因为在发送到第三方接口的时候,很有可能会被第三方抓包或篡改数据。

采用支付令牌方式解决,这里只讨论将数据隐藏起来。

前台在调用第三方接口时先将参数传给本服务器后台getPayToken接口,服务器后台将关键参数进行确认,比如用户id,金额等,判断无误后,通过服务器调用第三方接口,拿到第三方接口返回的token信息给前台,前台再拿token去访问第三方接口,这样就完成了数据的隐藏,防止黑客中途修改信息。

 

ps:前台传数据给后台时,价格类信息必须在后台查询数据库进行对比,必须以数据库中的数据为主,商品id这类信息即使被修改,实际上也无法达成篡改目的。

最终第三方支付接口也会回调付款参数,后台最后还要对比价格保证安全性。

 

19.https请求

https请求默认443端口,和http不是一个协议,默认会把带的参数进行加密。

想要使用https请求需要为自己域名申请一个SSL证书,如阿里云域名可以免费使用1年。

htttps相对安全,谷歌浏览器18年以将https作为默认协议,没有带有证书的网站都会显示不安全。

微信小程序必须是https协议,搜索引擎对https优先收录,外网映射工具自带https协议。

安全证书可以分配到nginx、tomcat、apach等服务器上,具体参考官方手册。

 

20.Https请求过程

1.客户端使用https请求访问拥有SSL证书的域名,服务器生成一对公钥和私钥,把公钥和相关证书信息返回给客户端,证书中包含一些加密算法,颁发机构,公钥,有效期等。

2.如果证书有效,客户端就生成一个随机数,然后用公钥将此随机数加密后发送给服务器。

3.服务器用私钥去解密数据,拿到随机数,用该随机数当成**去加密一条信息后返回给客户端。

4.客户端拿到加密信息,用随机数去解密,如果和之前结果一样,之后双方就会用此**(随机公钥)进行加密信息。

 

ps:https的过程是先用非对称加密,之后用对称加密,所以是https是一个混合加密模式。因为客户端较多而且每个客户端都要发送很多请求,非对称加密效率较低,需要用效率高德对称加密。

java 开发必备的安全架构知识