用AWS KMS固定连接字符串
#aws #教程 #mysql #planetscale

概述

大多数开发人员完全意识到,将敏感信息存储在代码中是不好的做法。如果未经授权的用户可以访问您的代码库,那么该个人现在可以使用试图保护的任何信息。例如,如果您要在代码库中存储行星尺度连接字符串,那么任何访问代码的人现在都可以直接访问您的数据库并造成各种破坏。

避免这种情况的一种简单方法是使用环境变量,以便可以将敏感信息存储在运行代码的系统中,而不是代码本身。如果您在AWS中构建后端服务,实际上,有一种方法可以通过使用AWS KMS service中的托管密钥对这些环境变量进行进一步保护。本文解释了什么是kms以及如何在lambda函数中使用它。

要与本教程一起,您将需要以下内容:

请注意,我们将在AWS中创建资源,这花费了真实的钱。资源可能会在AWS用户的免费层覆盖,因此请检查您的AWS计划。

由于这是一个以安全为重点的话题,也建议您了解AWS Shared Responsibility Model

什么是kms

AWS Key Management Service(kms)是AWS提供的服务,可让您从一个位置管理加密密钥。它使您可以轻松地生成对称或不对称的键,或上传您生成的键,可用于加密和解密数据。

在考虑AWS安全模型时,它不仅可以决定使用这些钥匙获得哪些信息,还可以决定AWS中哪些服务可以访问以前使用AWS Identity和Access Management对其进行加密的键进行密码(IAM)(IAM)(IAM) )政策。它的设计方式也使AWS员工无法访问这些钥匙,因此即使在AWS中工作的个人也无法解密您的敏感信息。

教程概述

建议使用像kms这样的系统加密环境变量(例如行星尺度连接字符串),以确保最大的安全性。让我们看一下如何使用lambda函数来执行此操作。这是我们在本文其余部分中所看的内容的概述:

  • 您将通过创建一个读取来自行星尺度数据库的一些数据的lambda函数。
  • 然后,您将在kms中创建一个托管密钥。
  • 接下来,将加密存储连接字符串的环境变量,这将导致函数错误。
  • 最后,您将更新代码以解密连接字符串,并再次从数据库中测试读取数据。

该项目使用的数据库将包含一个名为recipes的表,该表具有以下结构:

+-------------------------+--------------+------+-----+---------+----------------+
| Field                   | Type         | Null | Key | Default | Extra          |
+-------------------------+--------------+------+-----+---------+----------------+
| id                      | int          | NO   | PRI | NULL    | auto_increment |
| name                    | varchar(100) | YES  |     | NULL    |                |
| est_time_to_make_in_min | int          | YES  |     | NULL    |                |
| description             | varchar(100) | YES  |     | NULL    |                |
+-------------------------+--------------+------+-----+---------+----------------+

如果您希望遵循相同的结构,则可以使用以下SQL命令在数据库中创建表并播种一些数据。这些可以使用Planetscale CLI或在仪表板中使用“控制台”选项卡运行。

CREATE TABLE recipes (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100),
  est_time_to_make_in_min INT,
  description VARCHAR(100)
);

INSERT INTO recipes (name, est_time_to_make_in_min, description) VALUES
    ('Goatsnake Pizza', 60, 'Lots of deliciousness'),
    ('Clutch Pizza', 45, 'This is a pretty awesome pizza too'),
    ('Pepperoni Pizza', 30, 'Spicy stuff!');

创建lambda功能

首先在计算机上创建一个新文件夹以保存GO项目。打开该目录中的终端并运行以下命令以初始化项目,用该项目的任意名称代替$PROJECT_NAME

go mod init $PROJECT_NAME

创建一个名为main.go的文件并添加以下代码。该代码已评论,因此您将了解正在发生的事情:

package main

import (
    "database/sql"
    "encoding/json"
    "log"
    "os"
    "github.com/aws/aws-lambda-go/lambda"
    _ "github.com/go-sql-driver/mysql"
)

// The Recipe model will hold the data for a record pulled from the database.
type Recipe struct {
    Id            int
    Name          string
    EstTimeToMake int
    Description   string
}

// Sets up the connection to the PlanetScale database.
func GetDatabase() (*sql.DB, error) {
    db, err := sql.Open("mysql", os.Getenv("DSN"))
    return db, err
}

// The function that will be called when the Lambda function is invoked.
func handler() {
    // Get a database connection
    db, err := GetDatabase()
    if err != nil {
        panic(err)
    }

    // Run the query to fetch all recipes
    query := "SELECT * FROM recipes;"
    res, err := db.Query(query)
    if err != nil {
        panic(err)
    }

    // Loop through the results, placing them in an array of the Recipe struct
    var recipes []Recipe
    for res.Next() {
        var r Recipe
        res.Scan(&r.Id, &r.Name, &r.EstTimeToMake, &r.Description)
        recipes = append(recipes, r)
    }

    // Convert the results to a JSON string and log out that string.
    jbytes, err := json.Marshal(recipes)
    if err != nil {
        panic(err)
    }
    log.Printf("Returned recipes: %v", string(jbytes))
}

// The main entry point for the Lambda service.
func main() {
    lambda.Start(handler)
}

现在返回终端并运行以下命令以安装任何缺失的依赖项:

go mod tidy

接下来,您将需要构建功能并将二进制汇编,以便将其上传到lambda服务。您需要为GOARCHGOOS设置环境变量,因此编译器为Lambda环境创建了适当的二进制文件。运行以下命令之一(取决于操作系统)以在dist文件夹中创建二进制文件:

// Mac/Linux
GOARCH=amd64 GOOS=linux go build -o dist/main .

// Windows
$Env:GOARCH="amd64"; $Env:GOOS="linux"; go build -o dist/main .

将功能上传到AWS之前的最后一步是将创建的二进制文件添加到ZIP文件中,因为这是Lambda服务的期望。在Mac上,您可以右键单击main文件并压缩它以创建该文件。

How to compress a file on a Mac.

设置并测试AWS中的lambda功能

下一步是在AWS中配置lambda函数,并在上一节中上传zpipted文件夹。在AWS中,使用全局搜索查找lambdaâ,然后从结果列表中选择它。

Lambda in the AWS search.

单击“创建函数” 启动向导以创建lambda函数。

Create a function in Lambda.

给该功能一个名称,然后更改“运行时” go 1.x 。留下其余默认值,然后单击“创建函数” 在表单的底部。

The Create function wizard.

创建功能后,您将需要上传上一节中创建的zip文件。从“代码” 源部分中,选择“从“ > ”上传。

Where to upload a zip file in Lambda.

在下一个模式中,单击“上传” 按钮,然后从计算机中选择zip文件。单击“保存” 一旦选择了它。

The Upload modal.

接下来,您需要将默认处理程序从hello更改为main,这是为lambda构建的二进制文件的名称。在运行时设置下,单击“编辑”

The Edit button in Runtime settings.

处理程序更改为主字段,然后单击“保存”

The Edit runtime settings view.

接下来,选择“配置” tab> “环境变量” > “ edit”

Where to edit environment variables.

创建一个名为“ DSN”的条目,并在您的PlanetsCale数据库的连接字符串中粘贴。您可以通过单击“连接”,单击“与“下拉”连接,然后选择“ go”来找到它。一旦您拥有它,然后将其粘贴并单击“保存”

The Edit environment variables view.

最后,让我们测试功能,看看是否从数据库中获取数据。选择“测试” 选项卡,然后单击“测试” 按钮。

Where to test the Lambda function.

视图应更新并显示一个称为执行结果的警报框。如果您正确遵循所有前一步,则该盒子应该为绿色。展开它,您应该从日志输出下的数据库中查看记录。

The first set of successful results.

现在,让我们看看如何用kms键加密连接字符串。在从Lambda继续前进之前,您需要担任此Lambda的执行角色。您可以在“配置” 选项卡中找到“权限” 。在下一步中需要注意它。

The location of the execution role.

在KMS中创建客户托管密钥

从AWS控制台开始,然后使用全局搜索查找密钥管理服务。从可用服务列表中选择它。

The Key Management Service entry in the search results.

如果您看不到立即创建键的按钮,请先从左导航中选择“客户托管密钥” 。单击“创建键”

Where to create a customer managed key.

如前所述,AWS使您可以创建对称和不对称的键。这两种选项都可以用于加密和解密数据,但是如果您需要下载用于签署AWS之外的其他工件的公共密钥,则不对称密钥很有用。由于我们仅在AWS中工作,因此选择“对称” 选择,然后单击“ Next”

The Configure key view.

别名下的下一个视图中

The Add labels view.

现在,您需要配置密钥管理员,该管理员可以是IAM用户,组或角色。密钥管理员是允许从AWS控制台或API对密钥更改的用户。对于本教程,选择您自己的IAM用户帐户。向下滚动,然后单击“ Next”

The Define key administrative permissions view.

下一个视图将使您选择允许在kms中访问密钥的IAM用户,组或角色。从上一节键入Lambda函数的执行角色名称,然后从列表中选择它。单击“ Next” 一旦选择了它。

The Define key usage permissions view.

最后,滚动到底部,然后单击“完成”

The final view of the Create key wizard.

加密lambda中的连接字符串

回到您的lambda功能,选择“配置” tab> “环境变量” >> “ edit”

Editing the existing environment variables in Lambda.

现在扩展加密配置部分。检查“启用助手在运输中进行加密” 框,您会注意到加密按钮现在存在于DSN环境变量旁边。

Enabling encryption for the value.

单击“加密” 时,将出现一种模式,您可以在上一节中选择创建的kms键。如果您扩展了解密秘密片段,您还可以向您显示您可以使用的代码,以将其拉入加密值并将其解密以在代码中使用。我们将将其添加到lambda函数中。选择您的kms键,然后单击“加密”

The Encryption in transit modal.

DSN环境变量的值应更新为加密值。单击“保存”

The Edit environment variables view after the value has been encrypted.

现在,如果您尝试再次测试代码,则应该失败,因为代码不知道该如何处理加密的连接字符串。注意如何专门围绕MySQL驱动程序弄清楚如何连接到Planetscale数据库的错误。

The failed test after encrypting the connection string.

要解决此问题,请在计算机上再次打开main.go,然后更新文件的前半部分(通过GetDatabase()),以如下图。将更新导入,将添加init()函数,并将更新GetDatabase()函数以反映符合解密连接字符串的DSN变量。

package main

import (
    "database/sql"
    "encoding/json"
    "log"
    "os"
    "encoding/base64"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/kms"
    _ "github.com/go-sql-driver/mysql"
)

// Set up variables to be used with the encrypted connection string.
var functionName string = os.Getenv("AWS_LAMBDA_FUNCTION_NAME")
var encrypted string = os.Getenv("DSN")
var DSN string

// The init function will run first, decrypting DNS into the above variable.
func init() {
    kmsClient := kms.New(session.New())
    decodedBytes, err := base64.StdEncoding.DecodeString(encrypted)
    if err != nil {
        panic(err)
    }

    input := &kms.DecryptInput{
        CiphertextBlob: decodedBytes,
        EncryptionContext: aws.StringMap(map[string]string{
            "LambdaFunctionName": functionName,
        }),
    }

    response, err := kmsClient.Decrypt(input)
    if err != nil {
        panic(err)
    }
    DSN = string(response.Plaintext[:])
}

// The Recipe model will hold the data for a record pulled from the database.
type Recipe struct {
    Id            int
    Name          string
    EstTimeToMake int
    Description   string
}

// Sets up the connection to the PlanetScale database.
func GetDatabase() (*sql.DB, error) {
    db, err := sql.Open("mysql", DSN) // ← Update the second parameter here
    return db, err
}

// remainder of the code...

现在遵循上一节的过程以构建项目,将其缩进并将其上传到AWS中。这样做后,请再次在AWS中测试该功能,并应成功返回数据。

The successful test results after updating the code.

结论

如果您遵循的话,您应该对如何使用KMS在AWS上构建的应用程序中加密敏感信息有很好的了解。这是存储连接字符串的一种更安全的方法,因此即使您的AWS帐户受到损害,未经授权的用户也无法访问您的行星标度数据库。在此处使用的示例时,无论语言如何,任何应用程序都适用于任何应用。