如何保证接口的安全性?接口设计

如何保证接口的安全性?接口设计

【参数校验】

保证接口安全的第一步,也是最重要的一步,需要对接口的请求参数做校验。

我们可以按如下步骤做校验:

  • 校验参数是否为空,有些接口中可能会包含多个参数,有些参数允许为空,有些参数不允许为空,我们需要对这些参数做校验,防止接口底层出现异常。
  • 校验参数类型,比如:age是int类型的,用户传入了一个字符串:"123abc",这种情况参数不合法,需要被拦截。
  • 校验参数的长度,特别是对于新增或者修改数据接口,必须要做参数长度的校验,否则超长了数据库会报异常。比如:数据库username字段长度是30,新用户注册时,输入了超过30个字符的名称,需要提示用户名称超长了。虽说前端会校验字段长度,但接口对参数长度的校验也必不可少。
  • 校验枚举值,有些接口参数是枚举,比如:status,数据库中设计的该字段只有1、2、3三个值。如果用户传入了4,则需要提示用户参数错误。
  • 校验数据范围,对于有些金额参数,需要校验数据范围,比如:单笔交易的money必须大于0,小于10000。

我们可以自己写代码,对每个接口的请求参数一一做校验。也可以使用一些第三方的校验框架。

【统一封装返回值】

对接口返回值统一封装是为了让代码更规范,也是处于安全方面的考虑。

假如有这样一种场景:你写的某个接口底层的sql,在某种条件下有语法问题。某个用户请求接口之后,在访问数据库时,直接报了sql语法错误,将数据库名、表名、字段名、相关sql语句都打印出来了。

此时,如果你的接口将这些异常信息直接返回给外网的用户,有些黑客拿着这些信息,将参数做一些调整,拼接一些注入sql,可以对你的数据库发起攻击。

因此,非常有必要对接口的返回值做统一的封装。

例如下面这样:

{
    "code":0,
    "message":null,
    "data":[{"id":123,"name":"abc"}]
}

该json返回值中定义了三个字段:

  • code:表示响应码,0-成功,1-参数为空,2-参数错误,3-签名错误 4-请求超时 5-服务器内部错误等。
  • message:表示提示信息,如果请求成功,则返回空。如果请求失败,则返回我们专门在代码中处理过,让用户能看懂的错误信息。
  • data:表示具体的数据,返回的是一个json字段。

对返回值这样封装之后,即使在接口的底层出现了数据库的异常,也不会直接提示用户,给用户提示的是服务器内部错误。

对返回值统一封装的工作,没有必要在业务代码中做,完全可以在放到API网关。

业务系统在出现异常时,抛出业务异常的RuntimeException,其中有个message字段定义异常信息。

所有的API接口都必须经过API网关,API网关捕获该业务异常,然后转换成统一的异常结构返回,这样能统一返回值结构。

【做转义】

在用户自定义输入框,用户可以输入任意内容。有些地方需要用html的格式显示用户输入的内容,比如文章详情页或者合同详情页,用户可以自定义文案和样式。这些地方如果我们不做处理,可能会遭受XSS(Cross Site Scripting)攻击,也就是跨站脚本攻击。攻击者可以在输入的内容中,增加脚本,比如:<script>alert("反射型 XSS 攻击")</script>,这样在访问合同详情页时,会弹出一个不需要的窗口,攻击者甚至可以引导用户访问一些恶意的链接。

由此,我们需要对用户输入内容中的一些特殊标签做转义。

下面这张图中列出了需要转义的常见字符和转义后的字符:

如何保证接口的安全性?接口设计

图片我们可以自定义一个转义注解,打上该注解的字段,表示需要转义。

有个专门的AOP拦截器,将用户的原始内容,转换成转义后的内容。保存到数据库中是转义之后的内容。除此之外,为了防止SQL注入的情况,也需要将用户输入的参数做SQL语句方面的转义。

【做权限控制】

我们需要对接口做权限控制。主要包含了下面3种情况:

校验是否登录

接口功能权限控制
对于有些重要的接口,比如订单审核接口,只有拥有订单审核权限的运营账号,才有权限访问该接口。

接口数据权限控制
对于有些订单查询接口,普通运营只能查看普通用户的数据。而运营经理可以查看普通用户和vip用户的数据。这种情况我们需要对该订单查询接口做数据权限控制。

不同的角色,能够查看的数据范围不同。可以在查询数据时,在sql语句中动态拼接过滤数据权限的sql。

【加验证码】

【限流】

【加ip白名单】

【校验敏感词】

对于某些用户可以自定义内容的接口,还需要对用户输入的内容做敏感词校验。

比如:在创建商品页面,用户输入了:傻逼商品,结果直接显示到了商城的商品列表页面,这种情况肯定是不被允许的。

当然你也可以做一个审核功能,对用户创建的商品信息做人工审核,如果商品数量太多,这样会浪费很多人力。

有个比较好的做法是:对用户自定义的内容,做敏感词校验。可以调用第三方平台的接口,也可以自己实现一个敏感词校验接口。

可以在GitHub上下载一个开源的敏感词库,将那些敏感词导入到数据库中。然后使用hanlp对用户输入的内容,进行分词。对分好的词,去匹配敏感词库中的那些敏感词。

如果匹配上了,则说明是敏感词,则验证不通过。如果没有匹配上,则说明非敏感词,则验证通过。

不可能在每个业务接口中都调用敏感词校验接口,我们可以自定义注解,在AOP拦截器中调用敏感词校验接口。

在调用业务接口之前,先触发拦截器,校验打了敏感词校验注解的那些字段,将他们里面包含的内容,作为入参传入敏感词校验接口做校验。

当然有时候hanlp分词器会把句子分错词,还需要添加一个敏感词的白名单,白名单中的词不是敏感词。

【使用https协议】

【数据加密】

有些信息是用户的核心信息,比如:手机号、邮箱、密码、身份证、银行卡号等,不能别泄露出去。

在保存到数据库时,我们要将这些字段,做加密处理。

后面即使这些数据被泄露了,获得数据的人,由于没有密钥,没办法解密。

这种情况可以使用AES对称加密的方式,因为后面系统的有些业务场景,需要把加密的数据解密出来。

为了安全性考虑,我们需要设置一个用于加密的密钥,这个密钥可以稍微复杂一点,包含一些数字、字母和特殊字符。

我们同样可以通过自定义注解的方式,给需要加密的字段添加该注解,在Mybatis拦截器中实现加解密的功能。

对于查询操作,需要将加了该注解的字段的数据做解密处理。对于写入操作,要将加了该注解的字段的数据做加密处理。

有些页面显示的地方,手机号一般不会显示完整的手机号,中间有一部分用代替,比如:182**3457。这种情况需要做特殊处理。

【做风险控制】

有些特殊的接口,比如用户登录接口,我们需要对该接口做风险控制,尽可能减小被盗号的风险。

用户登录失败之后,需要有地方,比如:Redis,记录用户登录失败的次数。

如果用户第一次输入账号密码登录时,出现的是一个稍微简单的验证码。如果用户把账号或密码连续输错3次之后,出现了更复杂的验证码。或者改成使用手机短信验证。

如果用户在一天之内,把账号或密码连续输错10次,则直接锁定该账号。这样处理是为了防止有人用一些软件,暴力破解账号和密码。

在用户登录成功之后,需要有一张表记录用户的ip、所在城市和登录的设备id。

如果你的账号被盗了。在盗号者在页面输入账号密码登录,会调用登录接口,此时登录接口中可以根据用户的ip和设备id,做一些风险控制。

接口判断如果用户当前登录的ip、所在城市和设备ip,跟上一次登录成功时记录的相差非常大。

比如:1小时之前,用的ip是100.101.101.101,城市是北京,设备id是1001,而1小时之后,用的ip是200.202.202.101,城市是广州,设备id是2002。

这种情况用户的账号极有可能被盗了。登录接口做安全性升级,需要校验用户手机验证码才能登录成功。

由于盗号者只有你的账号和密码,没有手机验证码,所以即使被盗号了,也没办法登录成功。

Tags: 接口

添加新评论