第 10 章:云安全

云安全是云原生应用的基石,只有将安全性融入设计与开发流程,才能确保数据与服务的可靠性。本章系统介绍云安全的核心原则、常见模式及实用技术,帮助开发者构建安全的云原生应用。

引言

安全实现起来往往复杂且容易被忽视,许多团队习惯将安全相关代码留到最后,导致上线前手忙脚乱。对于云原生应用,安全必须前置,贯穿设计、开发和运维全过程。

本章将围绕以下内容展开:

  • Web 应用安全选项与实现
  • 微服务安全保护方法
  • 隐私与数据安全设计原则
  • 实践练习:安全服务开发

保护 Web 应用程序

Web 应用安全是云安全的基础。合理选择认证与授权机制,避免常见安全误区,是保障云端应用安全的关键。

应用程序安全性选项

常见 Web 应用安全技术包括:

  • HTTP 基本验证:用户名密码哈希传输,易受缓存和重放攻击。
  • 表单(Cookies)认证:凭据由页面生成,易受 CSRF 等攻击。
  • Windows 身份验证:适用于企业内网,不推荐云原生场景。
  • SAML(安全断言标记语言):行业标准,支持身份联邦与多语言 API。
  • OAuth/OpenID:开放授权标准,支持第三方登录,云原生推荐。
  • 习惯性方案:不建议依赖底层操作系统或主机安全机制。

警告
不要依赖底层操作系统的密钥库或用户账户安全机制,系统安全需应用层实现。

本章以 OAuth 为例,介绍安全 Web 应用的实现。

设置 Auth0 账户

OAuth 认证需依赖身份信息提供商(如 Auth0、Google、UAA 等),通过 JWT(JSON Web Token)实现安全认证。推荐使用 Auth0,支持私有数据库和第三方登录,注册流程简单,无需信用卡。

注册后可在仪表板管理用户和认证系统:

Auth0 仪表板
Auth0 仪表板

构建 OAuth 安全 Web 应用

以 Auth0 为例,构建安全 Web 应用的核心流程如下:

  1. 配置 Auth0 账户,获取 ClientID、Secret、Domain、CallbackURL。
  2. 在服务端初始化认证配置与会话管理。
  3. 配置路由与中间件,保护敏感资源。
  4. 实现认证回调与用户信息处理。

服务端核心代码示例:

// filepath: server/server.go
package server

import (
           "net/http"

           "github.com/astaxie/beego/session"
           "github.com/cloudfoundry-community/go-cfenv"
           "github.com/cloudnativego/cf-tools"
           "github.com/codegangsta/negroni"
           "github.com/gorilla/mux"
)
type authConfig struct {
           ClientID   string
           ClientSecret  string
           Domain   string
           CallbackURL    string
}

func NewServer(appEnv *cfenv.App) *negroni.Negroni {
   authClientID,_ := cftools.GetVCAPServiceProperty("authzero", "id", appEnv)
   authSecret,_:= cftools.GetVCAPServiceProperty("authzero", "secret", appEnv)
   authDomain,_:= cftools.GetVCAPServiceProperty("authzero", "domain", appEnv)
   authCallback,_:= cftools.GetVCAPServiceProperty("authzero", "callback",appEnv)

   config := &authConfig{
           ClientID:   authClientID,
           ClientSecret:  authSecret,
           Domain:   authDomain,
           CallbackURL:    authCallback,
   }

   sessionManager, _ := session.NewManager("memory",
    '{"cookieName":"gosessionid","gclifetime":3600}')
   go sessionManager.GC()

   n := negroni.Classic()
   mx := mux.NewRouter()

   initRoutes(mx, sessionManager, config)

   n.UseHandler(mx)
   return n
}

func initRoutes(mx *mux.Router, sessionManager *session.Manager,
   config *authConfig) {
   mx.HandleFunc("/", homeHandler(config))
   mx.HandleFunc("/callback", callbackHandler(sessionManager, config))
   mx.Handle("/user", negroni.New(
           negroni.HandlerFunc(isAuthenticated(sessionManager)),
           negroni.Wrap(http.HandlerFunc(userHandler(sessionManager))),
   ))
   mx.PathPrefix("/public/").Handler(http.StripPrefix("/public/",
     http.FileServer(http.Dir("public/"))))
}

中间件实现:

// filepath: server/middleware.go
func isAuthenticated(sessionManager *session.Manager) negroni.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        session, _ := sessionManager.SessionStart(w, r)
        defer session.SessionRelease(w)
        if session.Get("profile") == nil {
            http.Redirect(w, r, "/", http.StatusMovedPermanently)
        } else {
            next(w, r)
        }
    }
}

主页处理器示例:

// filepath: server/home_handler.go
var bodyTemplate = `
<script src="https://cdn.auth0.com/js/lock-8.2.min.js"></script>
<script type="text/javascript">
var lock = new Auth0Lock('{{.ClientID}}', '{{.Domain}}');
function signin() {
  lock.show({
    callbackURL: '{{.CallbackURL}}',
    responseType: 'code',
    authParams: { scope: 'openid profile' }
  });
}
</script>
<button onclick="window.signin();">Login</button>
`

func homeHandler(config *authConfig) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        t := template.Must(template.New("htmlz").Parse(bodyTemplate))
        t.Execute(w, config)
    }
}

用户信息处理:

// filepath: server/user_handler.go
func userHandler(sessionManager *session.Manager) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        session, _ := sessionManager.SessionStart(w, r)
        defer session.SessionRelease(w)

        // Getting the profile from the session
        profile := session.Get("profile")
        fmt.Fprintf(w, "USER DATA: %+v", profile)
    }
}

会话状态警告
示例采用内存会话,生产环境建议使用 Redis 或分布式缓存。

运行方式:

./runlocal local_config/env

配置环境变量:

X_AUTHZERO_ID=(your auth0 ID)
X_AUTHZERO_SECRET=(your auth0 secret)
X_AUTHZERO_DOMAIN=(your auth0 domain, e.g. foo.auth0.com)
X_AUTHZERO_CALLBACK=http://192.168.99.100/callback

登录后可在 /user 页面查看用户 profile 信息:

Auth0 登录页面
Auth0 登录页面

保护微服务

微服务安全需采用无 UI 的认证方式,推荐客户端凭据模式(API key/secret),避免重定向和复杂认证流程。

客户端凭据模式概述

客户端凭据模式适用于服务间认证,常见于 API key/secret 方案。需区分:

  • 请求发起者身份
  • 服务是否授权

正确使用 HTTP 状态码:

  • 401 Unauthorized:未提供凭据
  • 403 Forbidden:凭据正确但权限不足

使用客户端凭据保护微服务

通过中间件保护 API 路由,示例代码:

// filepath: server/server.go
func NewServer() *negroni.Negroni {
    formatter := render.New(render.Options{IndentJSON: true})
    n := negroni.Classic()
    router := mux.NewRouter()
    router.HandleFunc("/", homeHandler)
    apiRouter := mux.NewRouter()
    apiRouter.HandleFunc("/api/get", apiGetHandler(formatter)).Methods("GET")
    apiRouter.HandleFunc("/api/post", apiPostHandler(formatter)).Methods("POST")
    router.PathPrefix("/api").Handler(negroni.New(
        negroni.HandlerFunc(isAuthorized(formatter)),
        negroni.Wrap(apiRouter),
    ))
    n.UseHandler(router)
    return n
}

认证中间件:

// filepath: server/middleware.go
func isAuthorized(formatter *render.Render) negroni.HandlerFunc {
    apikey := os.Getenv(APIKey)
    return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        providedKey := r.Header.Get(APIKey)
        if providedKey == "" {
            formatter.JSON(w, http.StatusUnauthorized, struct{ Error string }{"Unauthorized."})
        } else if providedKey != apikey {
            formatter.JSON(w, http.StatusForbidden, struct{ Error string }{"Insufficient credentials."})
        } else {
            next(w, r)
        }
    }
}

关于 SSL 的注意事项

SSL 终端建议由基础设施层(如反向代理、云平台)统一管理,应用层无需直接处理 SSL 证书,避免代码耦合和安全隐患。

隐私和数据安全

数据安全是云安全的重要组成部分。设计安全系统时应遵循以下原则:

  • 不在用户数据库存储敏感信息,推荐使用第三方令牌。
  • 敏感数据存储与传输均需加密。
  • 不与加密数据同存解密密钥。
  • 日志中避免输出个人身份信息(PII)。
  • 机密信息处理后立即销毁,避免内存泄漏。
  • 实践 3R 法则:轮换(Rotate)、翻修(Repave)、修复(Repair)。

读者练习

建议读者实践以下安全服务开发任务:

  • 在安全服务中添加资源,如 /customer/{email}/orders,响应中包含 email 字段。
  • 在安全 Web 应用中新增页面,展示安全服务返回的订单列表。
    • 从会话状态提取用户 email,构建 REST 请求。
    • 使用用户提供服务配置,避免硬编码后台服务 URL。

通过练习,深入理解安全 Web 应用与安全服务的集成与保护方法。

总结

安全性应贯穿云原生应用的设计与开发全过程,绝不能事后补救。通过合理选择认证机制、保护微服务、加强数据安全和隐私保护,开发者可有效提升云端应用的安全性。建议结合实际项目,深入理解安全设计原则,为后续复杂系统开发打下坚实基础。

参考文献

文章导航

独立页面

这是书籍中的独立页面。

书籍首页

评论区