亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Golang基于Vault實(shí)現(xiàn)敏感數(shù)據(jù)加解密

 更新時(shí)間:2023年07月05日 14:45:13   作者:Shawn27  
數(shù)據(jù)加密是主要的數(shù)據(jù)安全防護(hù)技術(shù)之一,敏感數(shù)據(jù)應(yīng)該加密存儲在數(shù)據(jù)庫中,降低泄露風(fēng)險(xiǎn),本文將介紹一下利用Vault實(shí)現(xiàn)敏感數(shù)據(jù)加解密的方法,需要的可以參考一下

本文是《基于Vault的敏感信息保護(hù)》的姊妹篇,文中涉及的配置管理實(shí)現(xiàn)方案可以參考《淺談Golang配置管理》這篇文章。

背景

某些應(yīng)用程序會處理一些敏感的數(shù)據(jù),比如用戶的證件號碼、手機(jī)號等個(gè)人隱私數(shù)據(jù)。如果將這些敏感數(shù)據(jù)以明文形式存儲在數(shù)據(jù)庫中,一旦發(fā)生黑客入侵事件,這些數(shù)據(jù)很容易被竊取、泄露,從而引發(fā)用戶信任風(fēng)險(xiǎn)和輿情危機(jī),導(dǎo)致平臺用戶流失,甚至需要承擔(dān)法律責(zé)任。

數(shù)據(jù)加密是主要的數(shù)據(jù)安全防護(hù)技術(shù)之一,敏感數(shù)據(jù)應(yīng)該加密存儲在數(shù)據(jù)庫中,降低泄露風(fēng)險(xiǎn)。

數(shù)據(jù)加解密方案

本文采用的是 HashiCorp 公司的 Vault 工具。Vault 通過自帶的 Transit 引擎提供加解密即服務(wù)(Encryption as a Service),如下圖所示,加解密過程為:

加密過程:

App 將需要加密的明文發(fā)給 Vault

Vault 將加密后的密文返給 App

App 將含有密文的數(shù)據(jù)存儲到數(shù)據(jù)庫中

解密過程:

App 從數(shù)據(jù)庫中讀取數(shù)據(jù)(含密文字段)

App 將需要解密的密文發(fā)給 Vault

Vault 將解密后的明文返給 App

具體實(shí)現(xiàn)過程

1. 準(zhǔn)備工作

使用 Vault 提供加解密服務(wù)前,需要先啟用 Transit 引擎,創(chuàng)建專用的加解密密鑰,并賦予對應(yīng)的 AppRole 加解密相關(guān)權(quán)限。

# 啟用 Transit 引擎
$ vault secrets enable transit
# 創(chuàng)建專用的加解密密鑰
$ vault write -f transit/keys/mykey
# 為 AppRole 綁定的權(quán)限策略 myapp-policy 添加加解密權(quán)限
$ vault policy write myapp-policy -<<EOF
#已有的權(quán)限,見《基于Vault的敏感信息保護(hù)》這篇文章
#新增加密權(quán)限:
path "transit/encrypt/mykey" {
???capabilities = [ "update" ]
}
#新增解密權(quán)限:
path "transit/decrypt/mykey" {
???capabilities = [ "update" ]
}
EOF
# 重新生成 AppRole 的 SecretID
$ vault write -f -field=secret_id auth/approle/role/myapp/secret-id >~/.secretid

2. 初始化Vault客戶端

不同于《基于Vault的敏感信息保護(hù)》這篇文章,本文采用應(yīng)用程序與 Vault 直接集成的方案,使用的是 Vault 官方提供的 Go 語言庫。

在應(yīng)用程序與 Vault 交互前,需要初始化 Vault 客戶端:登錄 Vault 獲取 Token,并在 Token 過期前進(jìn)行續(xù)租,當(dāng)無法續(xù)租時(shí)重新登錄獲取新的 Token。示例代碼如下:

func VaultInit() {
    // 創(chuàng)建 Vault Client
    config := vault.DefaultConfig()
    config.Address = vaultAddress
    var err error
    VaultClient, err = vault.NewClient(config)
    if err != nil {
        log.Fatalf("Failed to create vault client, err: %v", err)
    }
    // 循環(huán):登錄認(rèn)證,并續(xù)租Token
    go func() {
        for {
            vaultLoginResp, err := login(VaultClient)
            if err != nil {
                log.Printf("Unable to authenticate to Vault: %v", err)
                time.Sleep(time.Second * 10)
                continue
            }
            tokenErr := renew(VaultClient, vaultLoginResp)
            if tokenErr != nil {
                log.Printf("Unable to start managing token lifecycle: %v", tokenErr)
                time.Sleep(time.Second * 10)
            }
        }
    }()
}

本文采用的 Vault 相關(guān)配置如下:

vault:
  address: http://x.x.x.x:8200
  transit:
    key: mykey
  auth:
    roleid-file-path: /app/role/roleid
    secretid-file-path: /app/role/secretid

3. 登錄認(rèn)證

本文選擇 AppRole 認(rèn)證方法,登錄 Vault 的示例代碼如下:

func login(client *vault.Client) (*vault.Secret, error) {
    // 讀取 RoleID
    bytes, err := ioutil.ReadFile(vaultRoleIdFilePath)
    if err != nil {
        return nil, fmt.Errorf("Error reading role ID file: %w", err)
    }
    roleID := strings.TrimSpace(string(bytes))
    if len(roleID) == 0 {
        return nil, errors.New("Error: role ID file exists but read empty value")
    }
    // 指定 SecretID
    secretID := &auth.SecretID{FromFile: vaultSecretIdFilePath}
    // 初始化 AppRole 認(rèn)證方法,指定身份憑據(jù)
    appRoleAuth, err := auth.NewAppRoleAuth(roleID, secretID)
    if err != nil {
        return nil, fmt.Errorf("unable to initialize AppRole auth method: %w", err)
    }
    // 通過 AppRole 認(rèn)證方法登錄到 Vault
    authInfo, err := client.Auth().Login(context.Background(), appRoleAuth)
    if err != nil {
        return nil, fmt.Errorf("unable to login to AppRole auth method: %w", err)
    }
    if authInfo == nil {
        return nil, fmt.Errorf("no auth info was returned after login")
    }
    log.Printf("Successfully (re)logined, lease duration: %ds", authInfo.Auth.LeaseDuration)
    return authInfo, nil
}

4. Token續(xù)租

renew函數(shù)監(jiān)聽Token的生命周期,在TTL到期前進(jìn)行續(xù)租操作,直到無法繼續(xù)續(xù)租、續(xù)租失敗為止,此時(shí)需要重新登錄,獲取新的 Token。renew函數(shù)的示例代碼如下:

func renew(client *vault.Client, token *vault.Secret) error {
    // 為 Token 創(chuàng)建一個(gè)監(jiān)聽器
    watcher, err := client.NewLifetimeWatcher(&vault.LifetimeWatcherInput{
        Secret: token,
        //Increment: 3600,
    })
    if err != nil {
        return fmt.Errorf("unable to initialize new lifetime watcher for renewing auth token: %w", err)
    }
    // 啟動(dòng)后臺續(xù)租協(xié)程
    go watcher.Start()
    defer watcher.Stop()
    for {
        select {
        // 續(xù)租失敗,或者無法繼續(xù)續(xù)租
        case err := <-watcher.DoneCh():
            //續(xù)租失敗
            if err != nil {
                log.Printf("Failed to renew token: %v. Re-attempting login.", err)
                return nil
            }
            // 無法繼續(xù)續(xù)租
            log.Printf("Token can no longer be renewed. Re-attempting login.")
            return nil
        // 成功完成續(xù)租
        case renewal := <-watcher.RenewCh():
            log.Printf("Successfully renewed, lease duration: %ds", renewal.Secret.Auth.LeaseDuration)
        }
    }
}

5. 加密

本文以 GORM 庫為例來說明。GORM 的 Hook 機(jī)制允許在數(shù)據(jù)庫 CRUD 操作前后執(zhí)行預(yù)定義的 Hook 方法。對于加密而言,可以為模型類定義 BeforeSave 方法,并在其中完成敏感數(shù)據(jù)的加密操作。

func (t *Teacher) BeforeSave(*gorm.DB) error {
    return t.Encrypt()
}

Teacher 模型包含證件號碼IDcard和手機(jī)號Phone兩個(gè)敏感數(shù)據(jù):

// 此處僅展示 GORM 相關(guān)標(biāo)簽,省略其它標(biāo)簽
type Teacher struct {
    gorm.Model
    Name    string
    // ... 其余字段省略
    //密文
    IDcard string `gorm:"unique"`
    Phone  string
    //明文
    PlainIDcard string `gorm:"-"`
    PlainPhone  string `gorm:"-"`
}

加密方法Encrypt借助 Vault 對 IDcardPhone 進(jìn)行加密操作,示例代碼如下:

func (t *Teacher) Encrypt() error {
    path := fmt.Sprintf("/transit/encrypt/%s", config.VaultTransitKey)
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()
    // 批量加密
    resp, err := Vault.Logical().WriteWithContext(ctx, path, map[string]interface{}{
        "batch_input": []map[string]interface{}{
            {
                "plaintext": base64.StdEncoding.EncodeToString([]byte(t.PlainIDcard)),
            },
            {
                "plaintext": base64.StdEncoding.EncodeToString([]byte(t.PlainPhone)),
            },
        },
    })
    if err != nil {
        log.Printf("teacher.Encrypt failed to encrypt data")
        return err
    }
    // 拿到密文
    t.IDcard = resp.Data["batch_results"].([]interface{})[0].(map[string]interface{})["ciphertext"].(string)
    t.Phone = resp.Data["batch_results"].([]interface{})[1].(map[string]interface{})["ciphertext"].(string)
    log.Printf("teacher.Encrypt called")
    return nil
}

6. 解密

解密的實(shí)現(xiàn)與加密類似,我們可以定義解密方法Decrypt,當(dāng)需要進(jìn)行解密時(shí)調(diào)用該方法:

  • 如果沒有使用緩存層,可以在 AfterFind 方法中調(diào)用Decrypt,在查詢數(shù)據(jù)庫后完成解密操作
  • 如果使用了 Redis 等緩存服務(wù),則需要在更新緩存或命中緩存之后調(diào)用 Decrypt

Decrypt方法的示例代碼如下。

func (t *Teacher) Decrypt() error {
    path := fmt.Sprintf("/transit/decrypt/%s", config.VaultTransitKey)
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()
    // 批量解密
    resp, err := Vault.Logical().WriteWithContext(ctx, path, map[string]interface{}{
        "batch_input": []map[string]interface{}{
            {
                "ciphertext": t.IDcard,
            },
            {
                "ciphertext": t.Phone,
            },
        },
    })
    if err != nil {
        log.Printf("teacher.Decrypt failed to decrypt data")
        return err
    }
    // 拿到 base64 文本
    IDcard_base64 := resp.Data["batch_results"].([]interface{})[0].(map[string]interface{})["plaintext"].(string)
    Phone_base64 := resp.Data["batch_results"].([]interface{})[1].(map[string]interface{})["plaintext"].(string)
    // 解碼拿到明文
    IDcard, err1 := base64.StdEncoding.DecodeString(IDcard_base64)
    Phone, err2 := base64.StdEncoding.DecodeString(Phone_base64)
    if err1 != nil || err2 != nil {
        log.Printf("teacher.Decrypt failed to base64 decode")
        return errors.New("base64 decode error")
    }
    t.PlainIDcard = string(IDcard)
    t.PlainPhone = string(Phone)
    log.Printf("teacher.Decrypt called")
    return nil
}

總結(jié)

數(shù)據(jù)加密是主要的數(shù)據(jù)安全防護(hù)技術(shù)之一,敏感數(shù)據(jù)應(yīng)該加密存儲在數(shù)據(jù)庫中,降低泄露風(fēng)險(xiǎn)。本文介紹了 Golang 基于 Vault 實(shí)現(xiàn)敏感數(shù)據(jù)加解密的方案和具體實(shí)現(xiàn)過程。

到此這篇關(guān)于Golang基于Vault實(shí)現(xiàn)敏感數(shù)據(jù)加解密的文章就介紹到這了,更多相關(guān)Golang Vault敏感數(shù)據(jù)加解密內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!  

相關(guān)文章

  • 詳解Go語言變量作用域

    詳解Go語言變量作用域

    這篇文章主要介紹了Go 語言變量作用域的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用go語言,感興趣的朋友可以了解下
    2021-03-03
  • 詳解Go?語言如何通過測試保證質(zhì)量

    詳解Go?語言如何通過測試保證質(zhì)量

    這篇文章主要為大家介紹了Go?語言如何通過測試保證質(zhì)量詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Go語言中如何進(jìn)行包管理

    Go語言中如何進(jìn)行包管理

    在Go語言中,包(package)是函數(shù)和數(shù)據(jù)的集合,用于組織代碼,實(shí)現(xiàn)模塊化開發(fā),本文將結(jié)合實(shí)際案例,詳細(xì)講解Go語言包管理的用法,有需要的可以參考下
    2024-10-10
  • go-zero創(chuàng)建RESTful API 服務(wù)的方法

    go-zero創(chuàng)建RESTful API 服務(wù)的方法

    文章介紹了如何使用go-zero框架和goctl工具快速創(chuàng)建RESTfulAPI服務(wù),通過定義.api文件并使用goctl命令,可以自動(dòng)生成項(xiàng)目結(jié)構(gòu)、路由、請求和響應(yīng)模型以及處理邏輯,感興趣的朋友一起看看吧
    2024-11-11
  • Golang實(shí)現(xiàn)Directional Channel(定向通道)

    Golang實(shí)現(xiàn)Directional Channel(定向通道)

    這篇文章主要介紹了Golang實(shí)現(xiàn)Directional Channel(定向通道),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Go 日志封裝實(shí)戰(zhàn)示例詳解

    Go 日志封裝實(shí)戰(zhàn)示例詳解

    這篇文章主要為大家介紹了Go 日志封裝實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 如何使用Go語言獲取當(dāng)天、昨天、明天、某天0點(diǎn)時(shí)間戳以及格式化時(shí)間

    如何使用Go語言獲取當(dāng)天、昨天、明天、某天0點(diǎn)時(shí)間戳以及格式化時(shí)間

    這篇文章主要給大家介紹了關(guān)于如何使用Go語言獲取當(dāng)天、昨天、明天、某天0點(diǎn)時(shí)間戳以及格式化時(shí)間的相關(guān)資料,格式化時(shí)間戳是將時(shí)間戳轉(zhuǎn)換為特定的日期和時(shí)間格式,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • Gin框架之參數(shù)綁定的實(shí)現(xiàn)

    Gin框架之參數(shù)綁定的實(shí)現(xiàn)

    為了能夠更方便的獲取請求相關(guān)參數(shù),提高開發(fā)效率,本文主要介紹了Gin框架之參數(shù)綁定的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Golang中的Slice與數(shù)組及區(qū)別詳解

    Golang中的Slice與數(shù)組及區(qū)別詳解

    數(shù)組是一種具有固定長度的基本數(shù)據(jù)結(jié)構(gòu),在golang中與C語言一樣數(shù)組一旦創(chuàng)建了它的長度就不允許改變,數(shù)組的空余位置用0填補(bǔ),不允許數(shù)組越界。今天小編通過實(shí)例代碼操作給大家詳細(xì)介紹lang中的Slice與數(shù)組的相關(guān)知識,一起看看吧
    2020-02-02
  • Go http client 連接池不復(fù)用的問題

    Go http client 連接池不復(fù)用的問題

    這篇文章主要介紹了Go http client 連接池不復(fù)用的問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01

最新評論