【问题标题】:How to protect AWS API gateway URL from unauthorized access?如何保护 AWS API 网关 URL 免受未经授权的访问?
【发布时间】:2018-05-20 04:22:14
【问题描述】:

我想在 AWS S3 中托管我的静态网站。但它包含用于发送电子邮件的“联系我们”页面。当用户点击提交按钮时,它将请求发送到 AWS API 网关,API 网关触发 lambda 函数; lambda 函数将邮件发送给管理员。

但它有一些问题。当我们在服务器上托管网站时,我们可以使用验证码来防止提交请求自动化和欺诈活动。在这种情况下,任何人都可以滥用(通过在 URL 中传递查询字符串来发送更多请求)我知道它的 API 网关 URL。

所以我的问题是如何知道仅从我的网站请求提交请求以及有什么方法可以代替验证码

【问题讨论】:

  • 您可以通过 Googe Captcha 实现相同的目的,因为对于每个请求,它都需要使用从 API 验证的 Captcha 创建一个会话。为什么要使用验证码以外的东西?
  • 我们不会使用任何服务器,那么验证码将如何验证
  • 使用 API 网关和 Lambda 验证验证码。另一种选择是使用 CloudFront 和 AWS WAF 进行基于速率的阻止。
  • 我们可以在托管在 AWS S3 存储桶中的网站中使用谷歌验证码
  • 只有S3你不能这样做,你可以在静态页面中显示它但需要从后端验证。您可以使用 API Gateway 和 Lambda 作为后端来验证验证码。

标签: amazon-web-services amazon-s3 aws-lambda aws-api-gateway


【解决方案1】:

使用 API Gateway 和 Lambda 验证 reCAPTCHA

对于 S3 静态站点上的无服务器 SPA 站点上的联系表单,我将 AngularJS 与“angular-recaptcha”服务 (https://github.com/VividCortex/angular-recaptcha) 结合使用,尽管您可以使用任何方式获取和发布 recaptcha 值。

在后端,您可以使用调用 Lambda 函数的 API Gateway 来验证 recaptcha 值并执行某些操作。使用 NodeJS 和 Recaptcha2 (https://www.npmjs.com/package/recaptcha2) 来验证令牌。

var reCAPTCHA = require('recaptcha2')

module.exports.sendemail = (event, context, callback) => {

    // parse the data that was sent from API Gateway
    var eventData = JSON.parse(event.body);

    // Prepare the recaptcha connection to Google
    var recaptcha = new reCAPTCHA({
      siteKey: process.env.RECAPTCHA_KEY,
      secretKey: process.env.RECAPTCHA_SECRET
    })

    // Validate the recaptcha value
    recaptcha.validate(eventData.recaptcha)
        .then(function(){
            // validated ok
            console.log("ReCaptcha Valid")


            ... DO STUFF HERE ...


        })
        .catch(function(errorCodes){

            // invalid recaptcha
            console.log("ReCaptcha Not Valid")

            // translate error codes to human readable text
            console.log(recaptcha.translateErrors(errorCodes));

            // send a fail message with cors headers back to the UI
            var response = {
                statusCode: 500,
                headers: {
                    "Access-Control-Allow-Origin" : "*",
                    "Access-Control-Allow-Credentials" : true
                },
                body: JSON.stringify({"message":"Error: Invalid Recaptcha"})
            }
            callback(null, response);
        });
};

使用 Handlebars 和 SES 的模板电子邮件示例

作为奖励,这里有一些我重复使用的代码,用于使用 html/文本模板发送电子邮件:

  • 把手用于向模板 SES 注入值以发送电子邮件。
  • 确保您已使用您的域名设置 SES,并允许 Lambda 访问“ses:SendEmail”和“ses:SendEmailRaw”。
  • 在部署函数时添加环境变量,这样可以使代码可重用,并保持源代码的秘密。
  • 我强烈建议使用无服务器框架来部署您的无服务器应用程序。 https://serverless.com

对我来说是一种享受:)

'use strict';

var AWS = require('aws-sdk');
var ses = new AWS.SES();
var reCAPTCHA = require('recaptcha2')
var fs = require('fs');
var Handlebars = require('handlebars');

module.exports.sendemail = (event, context, callback) => {

    // parse the data that was sent from API Gateway
    var eventData = JSON.parse(event.body);

    // Prepare the recaptcha connection to Google
    var recaptcha = new reCAPTCHA({
      siteKey: process.env.RECAPTCHA_KEY,
      secretKey: process.env.RECAPTCHA_SECRET
    })

    // Validate the recaptcha value
    recaptcha.validate(eventData.recaptcha)
        .then(function(){
            // validated ok
            console.log("reCAPTCHA Valid")

            // Read the HTML template from the package root
            fs.readFile('./contact/email_template.html', function (err, emailHtmlTemplate) {
                if (err) {
                    console.log("Unable to load HTML Template");
                    throw err;
                }
                // Read the TEXT template from the package root
                fs.readFile('./contact/email_template.txt', function (err, emailTextTemplate) {
                    if (err) {
                        console.log("Unable to load TEXT Template");
                        throw err;
                    }

                    // Gather data to be injected to the templates
                    var emailData = {
                        "websiteaddress": process.env.WEBSITEADDRESS,
                        "websitename": process.env.WEBSITENAME,
                        "content": null,
                        "email": process.env.EMAIL_TO,
                        "event": eventData
                    };

                    // Use Handlebars to compile the template and inject values into the title (used in subject and body of email)
                    var templateTitle = Handlebars.compile(process.env.EMAIL_TITLE);
                    var titleText = templateTitle(emailData);
                    console.log(titleText);

                    // Add title to the values object
                    emailData.title = titleText;

                    // Use Handlebars to compile email plaintext body
                    var templateText = Handlebars.compile(emailTextTemplate.toString());
                    var bodyText = templateText(emailData);
                    console.log(bodyText);

                    // Use Handlebars to compile email html body
                    var templateHtml = Handlebars.compile(emailHtmlTemplate.toString());
                    var bodyHtml = templateHtml(emailData);
                    console.log(bodyHtml);

                    // Prepare the SES payload
                    var params = {
                        Destination: {
                            ToAddresses: [
                                process.env.EMAIL_TO
                            ]
                        },
                        Message: {
                            Body: {
                                Text: {
                                    Data: bodyText,
                                    Charset: 'UTF-8'
                                },
                                Html: {
                                    Data: bodyHtml
                                },
                            },
                            Subject: {
                                Data: titleText,
                                Charset: 'UTF-8'
                            }
                        },
                        Source: process.env.EMAIL_FROM
                    }
                    console.log(JSON.stringify(params,null,4));

                    // Send SES Email
                    ses.sendEmail(params, function(err,data){
                        if(err) {
                            console.log(err,err.stack); // error

                            // Handle SES send errors
                            var response = {
                                statusCode: 500,
                                headers: {
                                    "Access-Control-Allow-Origin" : "*",
                                    "Access-Control-Allow-Credentials" : true
                                },
                                body: JSON.stringify({"message":"Error: Unable to Send Message"})
                            }
                            callback(null, response);
                        }
                        else {
                            console.log(data); // success

                            // SES send was successful
                            var response = {
                                statusCode: 200,
                                headers: {
                                    "Access-Control-Allow-Origin" : "*",
                                    "Access-Control-Allow-Credentials" : true
                                },
                                body: JSON.stringify({"message":"Message Sent"})
                            }
                            callback(null, response);
                        }
                  });

              }); //end of load text template
          }); //end of load html template


      })
      .catch(function(errorCodes){

          // invalid recaptcha
          console.log("reCAPTCHA Not Valid")

          // translate error codes to human readable text
          console.log(recaptcha.translateErrors(errorCodes));

          // send a fail message with cors headers back to the UI
          var response = {
              statusCode: 500,
              headers: {
                  "Access-Control-Allow-Origin" : "*",
                  "Access-Control-Allow-Credentials" : true
              },
              body: JSON.stringify({"message":"Error: Invalid Recaptcha"})
          }
          callback(null, response);
     });
 };

编辑:修正语法错误

【讨论】:

  • 谢谢,但我毫无疑问。我正在使用 api gateway url 通过查询字符串传递表单数据,并使用后端 python lambda 函数发送邮件。如果我在我的表单中添加谷歌验证码。如何通过用户在谷歌验证码中输入的api网关url中的查询字符串传递数据
  • 在 php 中我们这样做是为了存储在一个变量中 $data = $_POST['g-recaptcha-response'] 如何在 javascript 中做同样的事情
  • var eventData = JSON.parse(event.body); 获取输入变量,然后eventData.recaptcha 是recaptcha 值。与eventData.contactFormMessageFromUser 或 'eventData.contactFormEmailAddressFromUser` 相同
  • 对于您的示例,您将使用 eventData.['g-recaptcha-response'] 获取 reCAPTCHA 的发布值。 (不能以相同的方式访问带有连字符的变量名的格式更改,应该避免)。
【解决方案2】:

Google 的 reCAPTCHA 看起来很容易集成 -

https://developers.google.com/recaptcha/intro 闯入 -

  1. 使用验证码 (https://developers.google.com/recaptcha/docs/display) 验证用户
  2. 验证响应 (https://developers.google.com/recaptcha/docs/verify)

这可以通过 API 网关端点和 lambda 轻松实现(您的秘密将在 lambda [也许作为 env 变量] 中,站点密钥将在 s3 静态站点中)

无论如何,为什么不设计自己的验证机制(如果你真的想自己做:))?在让他们提交(或提交时..)之前询问用户的电话号码。然后调用获取此号码的 API 网关端点并通过 SNS 发送 otp。一旦用户验证了这个 otp(另一个 api 网关调用),然后只发送管理员电子邮件(做进一步的处理)。您可以将此 otp 存储为带有经过验证的布尔标志的一些 rds。您不必担心这些 API 网关端点,因为它们实际上仅用于身份验证,并且可以通过云端自动扩展。

【讨论】:

  • 谢谢,但我毫无疑问。我正在使用 api gateway url 通过查询字符串传递表单数据,并使用后端 python lambda 函数发送邮件。如果我在我的表单中添加谷歌验证码。如何通过用户在谷歌验证码中输入的api网关url中的查询字符串传递数据
  • 您不返回用户输入的查询字符串。 Google API 将为您验证它并为您提供用户响应令牌。这是由 reCAPTCHA 提供的,用于验证您网站上的用户。将其发送给 lambda 并由他们验证此响应是否正确 - developers.google.com/recaptcha/docs/verify
  • 谢谢你,终于我用aws lambda完成了我的验证谷歌验证码的任务,它非常有用。谢谢你一次获得
【解决方案3】:

您可以在 http 上使用 Referer 标头。它由浏览器设置。

【讨论】:

  • referrer 标头对我的方案有何用处
  • 在提交被击中后,只需在 HttpRequest 中查看标头 Referrer。我觉得 Referrer 会给您您的网站页面
猜你喜欢
  • 1970-01-01
  • 2019-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-11
  • 2017-02-08
相关资源
最近更新 更多