在Lambda中使用NodeJS解密AWS KMS

10

首先,我想说这个问题似乎被问了很多次,但是在问题的答案中没有一个能解决我所遇到的问题。

我正在使用NodeJS编写lambda函数。它的所有内容都很好,除了尝试解密加密的环境变量。

为了让它正常工作,我已经将Lambda的其他部分注释掉了,但仍然没有结果。这是我现在正在使用的代码:

const aws = require('aws-sdk')
exports.handler = async (event, context, callback) => {
    const kms = new aws.KMS()

    let params = {
      //CiphertextBlob: Buffer.from(process.env.SECRET_KEY, 'base64')
      CiphertextBlob: process.env.SECRET_KEY
    }

    console.log('before decrypt')
    console.log('SECRET_KEY', process.env.SECRET_KEY)

    kms.decrypt(params, function (err, data) {
      console.log('decrypt')
      if (err) console.log(err, err.stack) // an error occurred
      else     console.log(data)           // successful response
    })

    console.log('after decrypt')
}

Lambda运行成功,没有遇到错误。这是代码的输出结果:
START RequestId: c3a83ca7-0f7a-11e9-84f1-a5f7503df368 Version: $LATEST
2019-01-03T17:12:36.726Z    c3a83ca7-0f7a-11e9-84f1-a5f7503df368    before decrypt
2019-01-03T17:12:36.763Z    c3a83ca7-0f7a-11e9-84f1-a5f7503df368    SECRET_KEY Encoded key string that I'm not putting in here
2019-01-03T17:12:36.765Z    c3a83ca7-0f7a-11e9-84f1-a5f7503df368    after decrypt
END RequestId: c3a83ca7-0f7a-11e9-84f1-a5f7503df368
REPORT RequestId: c3a83ca7-0f7a-11e9-84f1-a5f7503df368  Duration: 699.51 ms Billed Duration: 700 ms     Memory Size: 128 MB Max Memory Used: 40 MB  

正如你所看到的,解密回调函数中的所有控制台日志都没有显示出来,我不知道为什么。

使用密钥缓冲区版本(第6行)而不是明文版本的密钥(第7行)对输出没有任何影响。

请有人帮助我找出我错过了什么?


我猜你可以尝试在kms.decrypt调用内执行callback(),现在看起来你的代码是同步调用的。 - Kieper
@Kieper Node 不是我主要编码的语言。你的意思是这样吗?kms.decrypt(params, function (err, data) { console.log('decrypt') if (err) { console.log(err, err.stack) callback.done('error', error) } else { console.log(data) callback.success() } }) - Lisa
那似乎没有任何影响。 - Lisa
我认为更像是这里:https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html#nodejs-prog-model-handler-callback - Kieper
我不太确定我是否做对了,但似乎没有任何影响。当我执行以下操作时,输出日志没有任何更改: kms.decrypt(params, function (err, data) { console.log('decrypt') if (err) { console.log(err, err.stack) callback(null, 'error') } else { console.log(data) callback(null, 'success') } }) - Lisa
2个回答

17

这是我的同事帮我解决的方案。

const aws = require('aws-sdk')
const kms = new aws.KMS()
exports.handler = async (event, context, callback) => {
  let params = {
    CiphertextBlob: Buffer.from(process.env.SECRET_KEY, 'base64')
  }

  let secret = null
  try {
    const decrypted = await kms.decrypt(params).promise()
    secret = decrypted.Plaintext.toString('utf-8')
  }
  catch (exception) {
    console.error(exception)
  }
}

0
Lisa,了解你的原始代码来自何处以及为什么失败非常重要。所以这段代码来自于这里 - https://docs.aws.amazon.com/kms/latest/developerguide/programming-encryption.html - 这是官方的 AWS 文档。在 Decrypting a data key 部分的 Node.js 标签页中,你可以找到一个与你的代码非常相似的示例 - 不幸的是,截至今天,该示例仍然存在你抱怨的问题。 现在的问题是该示例使用了回调方式(现在我引用 AWS 文档的话):
kmsClient.decrypt({ CiphertextBlob, KeyId }, (err, data) => {
  if (err) console.log(err, err.stack); // an error occurred
  else {
    const { Plaintext } = data;
    ...
  }
});

顺便说一下,文档中的参数"{ CiphertextBlob, KeyId }"也是错误的。应该是{ CiphertextBlob: theBlob, KeyId: theKey }。而且KeyId参数并不是真正必需的,事实上你在批准的答案中没有包含它 - 参见https://docs.aws.amazon.com/kms/latest/developerguide/programming-encryption.html - "当使用对称加密KMS密钥解密时,KeyId参数是不需要的。AWS KMS可以从密文块中的元数据获取用于加密数据的KMS密钥。"
现在,如果您将原始代码按原样放入处理程序中,解密命令将异步运行,并且将调用回调函数,但处理程序方法不会等待生成的线程!相反,处理程序将立即完成,您的Lambda函数将在您有机会查看回调输出之前终止。
批准答案中的解决方案 -
const decrypted = await kms.decrypt(params).promise()

之所以能够正常工作,是因为处理程序在继续执行之前等待解密命令完成。

然而,确实有一种方法可以使用回调风格,并且仍然使处理程序等待解密调用的完成。这是一个比批准答案更复杂的解决方案,但我将在这里包含它以便理解,并且也许有人会想要使用这种风格:

const AWS = require('aws-sdk');
const kms = new AWS.KMS();

module.exports.handler = async (event, context, callback) => {
    const queryStringParameters = event.queryStringParameters;
    let cipherText= queryStringParameters.mySecretParameter;
    let ciphertextBlob = Buffer.from(cipherText, 'base64');
    const params = {
        CiphertextBlob: ciphertextBlob
    };
    const decryptionPromise = new Promise((resolve, reject) => {
        kms.decrypt(params, (err, data) => {
            if (err) {
                reject(err);
            } else {
                const { Plaintext } = data;
                resolve(Plaintext.toString('utf-8'));
            }
        });
    });
    try {
        const decryptedPlaintext = await decryptionPromise;

        // Do something with the decrypted plaintext
        console.log("Decrypted plaintext:", decryptedPlaintext);

        return {
            statusCode: 200,
            body: "Decryption successful"
        };
    } catch (error) {
        console.error("Error decrypting:", error);
        return {
            statusCode: 500,
            body: "Decryption failed"
        };
    }
};

"const decryptedPlaintext = await decryptionPromise;" 这行代码确保处理程序等待解密调用完成,因此您的 Lambda 函数不会过早终止。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接