如何打造一个安全的服务接口
相关文章
从数据库层谈安全措施
这篇文章是通过对话的形式展现,也是我第一次尝试这样的风格。不知道阅读起来的感觉和以前 这种感觉哪种好,希望多多指点。
正文
老A: 老大交代下来任务了,要写一套接口服务,用来查询订单的。主要接口有这么几个,登录、注册、查询用户订单、添加用户订单、删除用户订单、查询用户信息、修改用户信息、注销。小Z,这个就交给你做了,先把接口文档写一下。ヽ(ー_ー)ノ
小Z: 好的没问题,交给我吧。(▽)
十分钟之后·····
小Z :接口文档好了,老A你看一下。(▽)
登录接口 | |
---|---|
接口地址 | /login.do |
请求方法 | post |
请求参数 | username /password |
返回内容 | User对象 |
注册接口 | |
---|---|
接口地址 | /regist.do |
请求方法 | post |
请求参数 | username /password /nickname |
返回内容 | User对象 |
查询用户订单 | |
---|---|
接口地址 | /orderList.do |
请求方法 | get |
请求参数 | userId |
返回内容 | OrderList对象 |
添加用户订单 | |
---|---|
接口地址 | /addOrder.do |
请求方法 | post |
请求参数 | Order对象 |
返回内容 | 成功/失败 |
删除用户订单 | |
---|---|
接口地址 | /removeOrder.do |
请求方法 | post |
请求参数 | orderId |
返回内容 | 成功/失败 |
查询用户信息 | |
---|---|
接口地址 | /getUser.do |
请求方法 | get |
请求参数 | userId |
返回内容 | User对象 |
修改用户信息 | |
---|---|
接口地址 | /editUser.do |
请求方法 | post |
请求参数 | User对象 |
返回内容 | User对象 |
注销 | |
---|---|
接口地址 | /logout.do |
请求方法 | post |
请求参数 | userId |
返回内容 | 成功/失败 |
老A看完后,语重心长的对小Z说:“写接口,不光要考虑功能的实现,你还得考虑一下安全方面啊!”ヽ(ー_ー)ノ
小Z道:“我也考虑到了,请求使用HTTPS,提交的用post方法,获取的用get方法,这样应该比较安全吧。” !!!∑(゚Д゚ノ)ノ
老A:“那我问你,你的登录和后面的接口有什么关系?” o(´^`)o
小Z:“登录进来获取到user对象,user里有userId,通过userId请求后面的数据。不登录是没有的” ─=≡Σ(((つ•̀ω•́)つ
老A:“那么如果我知道UserId,是不是意味着我不需要登录就可以获取到数据了?” <( ̄ ﹌  ̄)>
小Z:“不登录你怎么获取?” (〃‘▽’〃)
老A:“userId是不是不变的,那我上次登录过后我记录下来UserId,下次再用可不可以?我换个电脑还用这个userId,是不是还能获取到数据?”
(▼ヘ▼#)
小Z:“好像是这么个情况,那我该怎么办呢?” ⊙(・◇・)?
老A:“不是好像,就是这么个情况!登录请求不是仅仅为了验证密码返回用户信息的,而是登录要管理起来后面所有跟用户权限有关的接口,不登录是不能用的。”
“我们登陆接口中可以加上一个token验证机制,就是说,如果登录成功,我们随机生成一串字符,把这个字符作为key,关联存储user对象。然后我们返回这个key。之后的所有请求都需要携带这个key,后面的请求过来后,先通过key查找缓存中是否有User对象。如果有,说明已经登录过了,就有权限访问,反之则没有权限访问。”
“同时,由于我们使用了key关联user对象,那么我们也不需要返回user对象。后面的请求也不需要携带user对象中的信息过来。因为我们缓存中已经存储了user对象,通过key查了出来,我们直接使用查出来的user对象作为参数就可以了。这样也避免了user对象的泄露。”
“key也可以添加有效时间的信息,比如说我们可以记录上一次请求key的时间,然后下一次请求就有了一个现在的时间。如果说登录操作有效时间是2小时,那么就可以拿现在的时间减去上一次请求的时间,得到一个差值,然后比较这个差值是否小于2小时,如果小于的话则是在有效时间内,允许请求。同时更新一下最后一次请求时间为当前时间,下一次请求继续判断。如果是超时了,就可以将缓存的user给删掉,直接返回错误,重新登录。用户退出也是一样的,删除掉缓存的user对象,那么以后的请求就找不到user对象,就不允许请求了”
小Z:“不愧是老A,我醍醐灌顶,豁然开朗。我甘拜下风,五体投地。对您的崇拜犹如滔滔江水连绵不绝” (。♥ᴗ♥。)
老A:“别拍马屁了,还不快回去改接口” (▼ヘ▼#)
小Z:“好的师傅,马上就去”
又十分钟后。。。
小A:“我的接口改完了,再看一下吧”
登录接口 | |
---|---|
接口地址 | /login.do |
请求方法 | post |
请求参数 | username /password |
返回内容 | token |
注册接口 | |
---|---|
接口地址 | /regist.do |
请求方法 | post |
请求参数 | username /password /nickname |
返回内容 | 成功 |
查询用户订单 | |
---|---|
接口地址 | /orderList.do |
请求方法 | get |
请求参数 | token |
返回内容 | OrderList对象 |
添加用户订单 | |
---|---|
接口地址 | /addOrder.do |
请求方法 | post |
请求参数 | Order对象、token |
返回内容 | 成功/失败 |
删除用户订单 | |
---|---|
接口地址 | /removeOrder.do |
请求方法 | post |
请求参数 | orderId、token |
返回内容 | 成功/失败 |
查询用户信息 | |
---|---|
接口地址 | /getUser.do |
请求方法 | get |
请求参数 | token |
返回内容 | User对象 |
修改用户信息 | |
---|---|
接口地址 | /editUser.do |
请求方法 | post |
请求参数 | User对象、token |
返回内容 | User对象 |
注销 | |
---|---|
接口地址 | /logout.do |
请求方法 | post |
请求参数 | token |
返回内容 | 成功/失败 |
老A:用户登录验证是解决了,但这还不够安全。
小Z:“这还不够安全?那还缺什么呢?”
老A:“你怎么知道给你发消息的就是就是真的用户呢?或者说,你怎么知道给你接口进行请求的,是你想让请求的客户端呢?”
小Z:“这个··· 我还真不知道请求是不是用户发的,我用请求工具填这些参数也是可以请求成功的,也就是说请求是可以被伪造的。”
老A:“是的,你这脑袋还算灵光”
小Z:“那该如何解决呢?”
老A:“我们可以使用验签机制,来防止参数的伪造篡改”
“验签的机制是,将参数进行某个规则的加密,这个规则只有你的服务端和客户端知道,参数中除了正常的请求参数外,多了一个验签参数 sign。后台拿到请求参数后,可以将参数用同样的方法加密,然后与sign比对,如果一致就认为没有被篡改。”
小Z:“那如果别人直接拿抓包的数据进行请求,参数不变,那不是没什么作用吗?他甚至都不用知道加密规则是什么”
老A:“孺子可教也,知道思考了。为了应对这个问题,我们可以在请求中再加一个参数,时间戳”
小Z:“我明白了。请求参数和时间戳一起加密,因为时间戳是变化的,所以说加密出来的结果也是变化的。同时请求参数中带着时间戳参数,服务端可以根据时间戳来限制单次请求的有效时间,比如说单次请求1分钟内有效。那么之后的请求,就算原封不动的拿过来,因为时间戳的限制,不是当时的请求就是无效的,获取不到数据。然而要修改参数中的时间戳,又跟加密验签sign不一致,也得不到返回。这一招真是高啊”
老A:“还有更高的呢。如果说黑客通过你的参数和解密后的结果,得到了加密规则,你咋办?”
老A:“拿到原文和密文,想要破译加密规则也不是不可能的事情。那么如果我们呈现出来的不是原文,那是不是就更安全一些了?”
小Z:“您快别绕关子了,到底是该怎么做?”
老A:“你也不多动动脑袋。服务端个客户端都是我们自己的程序,或者说都是我们授权的。那么我就可以在服务端设置一个参数secret。然后我们把secret告诉给客户端。当然不是走网络 请求,而是通过线下的方式,比如说发邮件啊啥的。然后客户端会把secret存在自己本地,服务器也存一个secret。加密的时候,将secret拼接在要加密的字符串上,secret本身不进行网络传输。这样,在原文中就有一个不可见的字符,别人通过网络也拿不到真正的原文。这样破解起来就不容易了”
小Z:“老A你真是天才啊!”
老A:“什么天才,我也是慢慢学的。这也不是我第一个发明的。”
小Z:“那么我们还可以设置加密秘钥,服务端和客户端知道,也不参与传输。这样也更安全。而且加密不用选择对称加密,采用MD5这种非对称加密就可以,因为服务器不需要解密,服务器只用通过相同的加密规则得到一个相同的结果就可以了”
老A:“学会举一反三了,不错不错。其实 我们也可以产生动态秘钥,用两个渠道传输。比如说以前银行会用一个动态令牌,你要登录网银就需要动态令牌的验证码。”
小Z:“其实现在的手机验证码也感觉像是这种,通过短信这个渠道去发送验证码,也提高了安全性。”
老A:“不错嘛,还想到了手机短信。不过一般是这种操作只有在登录的时候使用。因为虽然是安全,但是用户操作麻烦。为了提高用户的便捷性,只有在关键时刻才会使用,比如登录,比如转账这些。”
小Z:“难怪呢,这下对于 安全又了更深刻的认识。”
老A:“还有一些能够提高安全的你可以做的,那就是传输内容加密和混淆。”
小Z:“加密和混淆?”
老A:“对。你看你现在的接口,参数名别人一看就知道是啥,username这不就是用户名?你的请求参数不也直接明文,别人一眼就看到你传的值?你返回的内容User,别人不也就看到了用户信息?别人也不需要仿造请求,只是进行一个拦截,就看到了数据。”
小Z:“说的对啊,这么重要的事情给忘记了,这就需要双方进行一个对称加密规则,和名称定义了。对称加密的秘钥同样的也需要线下放到客户端和服务端两边。”
老A:“哈哈哈,脑袋转的挺快”
小Z:“那我回去改改,您再来看”
叕十分钟后
登录接口 | |
---|---|
接口地址 | /login.do |
请求方法 | post |
请求参数 | u 用户名/n 密码 |
返回内容 | 加密的json字符 token |
注册接口 | |
---|---|
接口地址 | /regist.do |
请求方法 | post |
请求参数 | s 用户名/ a 密码 /e 昵称 |
返回内容 | 成功 |
查询用户订单 | |
---|---|
接口地址 | /orderList.do |
请求方法 | get |
请求参数 | tn |
返回内容 | 加密的json字符 OrderList对象 |
添加用户订单 | |
---|---|
接口地址 | /addOrder.do |
请求方法 | post |
请求参数 | 加密的json字符 Order对象、tn |
返回内容 | 成功/失败 |
删除用户订单 | |
---|---|
接口地址 | /removeOrder.do |
请求方法 | post |
请求参数 | od 订单id、tn |
返回内容 | 成功/失败 |
查询用户信息 | |
---|---|
接口地址 | /getUser.do |
请求方法 | get |
请求参数 | tn |
返回内容 | 加密的json字符 User对象 |
修改用户信息 | |
---|---|
接口地址 | /editUser.do |
请求方法 | post |
请求参数 | 加密的json字符 User对象、tn |
返回内容 | 成功 |
注销 | |
---|---|
接口地址 | /logout.do |
请求方法 | post |
请求参数 | tn |
返回内容 | 成功/失败 |
小Z:“这下够安全了吧”
老A:“这是比之前安全等级高了很多,但不能说百分百安全。比如以前的SQL注入,就是直接将SQL写到参数中,让查询结果直接返回。当然现在是很少了,也不能说没有。我们都会对入参和返回值做处理,很少有直接返回的情况,你应该没这么干吧?”
小Z:“我当然知道,我的Dao层是用的MyBatis,传参用的还是 # 号,#号 就是为了防止SQL注入的”
老A:“那你知道为什么#号能防止SQL注入么?”
小Z:“我还真不知道,您给我讲讲吧”
老A:“唉,看样子你知道SQL注入这回事,但也是没真正明白。以后换了Hibernate或者其他的,你该咋办?你这还只是考虑java开发,安全是要考虑通用性的。你看上面的接口,是不是并不关心你用什么语言实现?”
小Z:“哎呀,您老先别教训我了,我这如饥似渴的小眼神瞅着您,您快说说。”
老A:“SQL注入,说白了就是在参数中写SQL语句,然后后台服务操作数据库时,参数中的SQL语言直接当SQL运行了,就查出来了数据。其实防范也很简单,就是不让参数当SQL语句执行,而是当成字符串。mybatis的#号,实际上就是将传入的参数在SQL中显示为字符串,这样SQL语句就不会被执行。同时,返回的结果也需要进行处理,不能直接返回,这样就避免了SQL注入”
小Z:“原来如此啊”
老A:“除了考虑接口数据的安全外,还需要考虑稳定性、可靠性。这也是安全要考虑的事情。因为不是所有的黑客攻击都是冲着你的数据来的,也有就是冲着你破坏你来的。让你的服务失效,不能用。比如DDoS攻击,就是 通过大规模的访问,让你的服务拥挤不堪,最后崩溃。这种情况下就该考虑限制请求和提高服务器并发量等去解决了”
小Z:“原来安全有这么多事情啊”
老A:“安全方面,还有很多事情呢,慢慢学吧”
如何打造一个安全的服务接口相关推荐
- Vue全家桶+Socket.io+Koa2打造一个智能聊天室 接口已开放
Vue.js+Socket.io+Koa2打造一个智能聊天室 Vue.js全家桶+Socket.io+Express/Koa2 打造的一个智能聊天室. 已经开源啦!为了方便大家学习,智能机器人.IP定 ...
- 服务接口API限流 Rate Limit
转载:https://www.cnblogs.com/exceptioneye/p/4783904.html https://blog.csdn.net/zrg523/article/details/ ...
- Java互联网架构-如何设计服务接口API限流功能
API 概念的出现,远远早于个人计算机的诞生,更不用说网络的诞生了.在公用数据处理的早期,为了一个应用能够与其它系统交互,开发者便已开始设计可公开访问并描述清晰的"接入点".早在那 ...
- 打造一个TB级微服务日志监控平台
本文主要介绍怎么使用 ELK Stack 帮助我们打造一个支撑起日产 TB 级的日志监控系统.在企业级的微服务环境中,跑着成百上千个服务都算是比较小的规模了.在生产环境上,日志扮演着很重要的角色,排查 ...
- 如何打造一个TB级微服务海量日志监控平台
前沿技术早知道,弯道超车有希望 积累超车资本,从关注DD开始 来源:性能与架构.图文编辑:xj 本文主要介绍怎么使用 ELK Stack 帮助我们打造一个支撑起日产 TB 级的日志监控系统.在企业级的 ...
- 如何打造一个抗住千万级流量短信服务(续)
前言 在之前写过一篇博文<短信服务设计>当时讲述了设计的思路,有很多读者朋友反馈说想了解具体的设计思路:今天又重新回顾下当时的具体实现细节发现当时实现的还是有一些巧妙的地方,值得大家参考, ...
- SmartSql使用教程(1)——初探,建立一个简单的CURD接口服务
一.引言 最近SmartSql被正式引入到了NCC,借着这个契机写一个使用教程系列 二.SmartSql简介[摘自官方文档] 1. SmartSql是什么? SmartSql = MyBatis + ...
- 手把手教你打造一个可视化接口自动化测试系统
现如今,接口开发几乎成为一个互联网公司的标配了,无论是web还是app,哪怕是小程序,都离不开接口作为支撑,当然,这里的接口范围很广,从http到websocket,再到rpc,只要能实现数据通信的都 ...
- python写rest服务_Python 如何创建一个简单的REST接口
问题 你想使用一个简单的REST接口通过网络远程控制或访问你的应用程序,但是你又不想自己去安装一个完整的web框架. 解决方案 构建一个REST风格的接口最简单的方法是创建一个基于WSGI标准(PEP ...
最新文章
- python中变量的作用域有几种_Python中变量的作用域(variable scope)
- mysql修改binlog格式_mysql binlog格式...
- 回顾线程的竞争机制-偏向锁
- mongodb自动备份脚本
- 曾鸣[长江商学院教授]_互动百科
- 通过SD卡来安装Linux系统
- 最经典的权限设计同样也是最糟糕的权限设计,权限设计理念最关键第一步之间的PK...
- PLSQL 的简单命令之三
- 转:前端js、jQuery实现日期格式化、字符串格式化
- 关于凸包算法和叉积的应用
- 手把手教如何制作数字证书并进行程序的数字签名
- NERO8注册码序列号
- 中标麒麟Neokylin7桌面版安装指南——基于VirtualBox虚拟机
- 烧光20亿不够续命,快狗打车IPO找钱?
- Wox自制主题 - Material
- Gartner发布2022年云平台服务技术成熟度曲线,iPaaS、低代码将达到成熟期
- Endianness一点通
- PHP 7.2禁止类名为Object的巨坑
- AudioRecord报错startRecording() called on an uninitialized AudioRecord.总结
- 互联网的商业模式中有三个层次
热门文章
- Go之time的时区
- 生活常识之社保篇(面对生活,居安思危,危则有备,有备无患)//2021-1-26
- 儒竞科技创业板IPO过会:拟募资9.8亿 海尔是重要客户
- ASP.NET Core 2.0 : 七.一张图看透启动背后的秘密
- 继续教育学时视频学习脚本
- 第三天:用dplyr处理数据框—— 管道符,summarise()和group_by()函数
- 西门子200smart与v90伺服驱动器Profinet通讯。 sina-pos的运用
- 服务器怎么抵御DDOS攻击?
- 51单片机 PWM LED灯亮度调节+Proteus仿真
- 家用服务器配置清单,供君参考