Send reports safely via email(mailbox).

Hi everyone. In this article, I will guide you on how to securely send reports via email using AWS. Before we dive in, I’ll provide more details about the AWS services involved and outline the problem we’re solving. The problem Let’s explore why attaching a report directly to an email is generally unsafe. Email Interception & Lack of Encryption. Standard email protocols (SMTP, POP3, IMAP) do not encrypt emails end-to-end. If an attacker sniffs network traffic (e.g., on public Wi-Fi or an unsecured network), they can capture emails, including attachments. Even TLS (used by Gmail, Outlook, etc.) encrypts emails only in transit; once delivered, they are stored unencrypted on mail servers. Email Account Compromise If a hacker gains access to your email account (phishing, credential stuffing, malware, etc.), they can: Download all email attachments you’ve sent or received. Forward emails to other accounts without you knowing. Expose the report permanently—it stays in inboxes (yours and the recipient’s) indefinitely. Forwarding & Unintended Access A recipient might accidentally forward the email. Emails can be archived and stored indefinitely, making them a long-term risk. If their email account is hacked, your sensitive report is exposed to attackers. Malware & Attachment Exploits Hackers often embed malicious payloads in email attachments. If your report is attached as a Word or Excel file, an attacker could inject macro-based malware and resend it. Some email providers scan attachments and store copies, potentially exposing sensitive content. Please be aware that this is not a perfect solution. The main drawback is that the report link is shared via email. If a malicious actor becomes aware of this approach, it could be relatively easy to trick users by phishing them with a fake link and a spoofed authorization page. The most secure and recommended approach would be to notify users that a new report is ready, and make it accessible only through your application’s interface and a secure, custom mailbox. AWS Services To achieve the goal of this article, I will use the following AWS services: AWS Cognito AWS Identity and Access Management (IAM) AWS S3 AWS Lambda AWS Api Gateway Before we begin I would like to mention that each of these services is self-sufficient and covering all of their features in a single article is nearly impossible. That's why I will go into some more depth about some of them. By the end of this article, I hope these concepts will be deeply engraved in your mind, like wisdom, empowering you to use them productively. Let's break down every service with a feature that we will use and also the steps of our flow. AWS Cognito There are two services that we will use, and the first one is Identity Pools. An identity pool exchanges an external identity type for a set of temporary AWS credentials, allowing access to AWS resources. The external identity could be from Google, Facebook, Twitter, SAML 2.0, or even User Pools identities. From an Identity Pools perspective, User Pools are just another form of identity. As you might have guessed, we will use User Pools for authentication. Additionally, I’d like to clarify that User Pools are used for sign-in and sign-up (with a customizable web UI), MFA, and other security features. Identity and Access Management We will use this service to create an IAM role, which will be attached to the Identity Pool. When the JWT token is passed to the configured Identity Pool, Cognito assumes the IAM role and returns temporary AWS credentials. S3 Amazon S3 (Simple Storage Service) is a scalable, durable, and secure object storage service that allows you to store and retrieve any amount of data from anywhere. It’s used for storing files, images, videos, backups, logs, reports, and more. We will use Amazon S3 to store the report in a private bucket.. Lambda AWS Lambda is a serverless computing service that lets you run code without managing servers. You just upload your function, and AWS takes care of the rest—scaling, infrastructure, and execution. We will use it to exchange a JWT token, generated by the user pool service, for temporary AWS credentials with an IAM role that grants access to the private S3 bucket. API Gateway AWS API Gateway is a fully managed service that helps you create, publish, and manage APIs at scale. We will use it to create a simple GET endpoint and attach a Lambda function. This endpoint will serve as a callback URL for the User Pool. The solution Before we implement our solution, let's have a look visually at how our flow architecture looks at this point. S3 First, we need to create a private S3 bucket. To create a bucket, you only need to provide a globally unique bucket name. All other settings, including Block Public Access settings for this bucket, can be kept as default. And by default, all public access is blocked. La

Apr 7, 2025 - 11:59
 0
Send reports safely via email(mailbox).

Hi everyone.
In this article, I will guide you on how to securely send reports via email using AWS. Before we dive in, I’ll provide more details about the AWS services involved and outline the problem we’re solving.

The problem

Let’s explore why attaching a report directly to an email is generally unsafe.

Email Interception & Lack of Encryption.

  • Standard email protocols (SMTP, POP3, IMAP) do not encrypt emails end-to-end.
  • If an attacker sniffs network traffic (e.g., on public Wi-Fi or an unsecured network), they can capture emails, including attachments.
  • Even TLS (used by Gmail, Outlook, etc.) encrypts emails only in transit; once delivered, they are stored unencrypted on mail servers.

Email Account Compromise

If a hacker gains access to your email account (phishing, credential stuffing, malware, etc.), they can:

  • Download all email attachments you’ve sent or received.
  • Forward emails to other accounts without you knowing.
  • Expose the report permanently—it stays in inboxes (yours and the recipient’s) indefinitely.

Forwarding & Unintended Access

  • A recipient might accidentally forward the email.
  • Emails can be archived and stored indefinitely, making them a long-term risk.
  • If their email account is hacked, your sensitive report is exposed to attackers.

Malware & Attachment Exploits

  • Hackers often embed malicious payloads in email attachments.
  • If your report is attached as a Word or Excel file, an attacker could inject macro-based malware and resend it.
  • Some email providers scan attachments and store copies, potentially exposing sensitive content.

Please be aware that this is not a perfect solution. The main drawback is that the report link is shared via email. If a malicious actor becomes aware of this approach, it could be relatively easy to trick users by phishing them with a fake link and a spoofed authorization page.

The most secure and recommended approach would be to notify users that a new report is ready, and make it accessible only through your application’s interface and a secure, custom mailbox.

AWS Services

To achieve the goal of this article, I will use the following AWS services:

  • AWS Cognito
  • AWS Identity and Access Management (IAM)
  • AWS S3
  • AWS Lambda
  • AWS Api Gateway

Before we begin I would like to mention that each of these services is self-sufficient and covering all of their features in a single article is nearly impossible. That's why I will go into some more depth about some of them. By the end of this article, I hope these concepts will be deeply engraved in your mind, like wisdom, empowering you to use them productively.
Let's break down every service with a feature that we will use and also the steps of our flow.

  1. AWS Cognito
    There are two services that we will use, and the first one is Identity Pools. An identity pool exchanges an external identity type for a set of temporary AWS credentials, allowing access to AWS resources. The external identity could be from Google, Facebook, Twitter, SAML 2.0, or even User Pools identities.
    From an Identity Pools perspective, User Pools are just another form of identity. As you might have guessed, we will use User Pools for authentication. Additionally, I’d like to clarify that User Pools are used for sign-in and sign-up (with a customizable web UI), MFA, and other security features.

  2. Identity and Access Management
    We will use this service to create an IAM role, which will be attached to the Identity Pool. When the JWT token is passed to the configured Identity Pool, Cognito assumes the IAM role and returns temporary AWS credentials.

  3. S3
    Amazon S3 (Simple Storage Service) is a scalable, durable, and secure object storage service that allows you to store and retrieve any amount of data from anywhere. It’s used for storing files, images, videos, backups, logs, reports, and more.
    We will use Amazon S3 to store the report in a private bucket..

  4. Lambda
    AWS Lambda is a serverless computing service that lets you run code without managing servers. You just upload your function, and AWS takes care of the rest—scaling, infrastructure, and execution.
    We will use it to exchange a JWT token, generated by the user pool service, for temporary AWS credentials with an IAM role that grants access to the private S3 bucket.

  5. API Gateway
    AWS API Gateway is a fully managed service that helps you create, publish, and manage APIs at scale.
    We will use it to create a simple GET endpoint and attach a Lambda function. This endpoint will serve as a callback URL for the User Pool.

The solution

Before we implement our solution, let's have a look visually at how our flow architecture looks at this point.

Solution diagram

S3

First, we need to create a private S3 bucket. To create a bucket, you only need to provide a globally unique bucket name. All other settings, including Block Public Access settings for this bucket, can be kept as default. And by default, all public access is blocked.

Lambda

Then, we need to create a Lambda function. I'll keep all the logic within a single Lambda, but you can split it into two functions or even three to follow the Single Responsibility Principle.
The Lambda function will be attached to an API Gateway endpoint. The event will contain fileName and a Cognito code. For simplicity, I’m omitting event validation and error handling in the Lambda code, but you should include these for production use.

For all intents and purposes, we haven’t finished with the Lambda function yet. We still need to set all environment variables, but from a code perspective, we won’t be making any more changes. Since we haven’t created the necessary resources yet, we’re unable to do so.

Now is the time to do that.

Api Gateway

We need to create an API Gateway with a GET endpoint and attach our Lambda function to it. You should be able to handle this on your own. However, there is one important detail I’d like to highlight—please don’t forget to enable the Lambda Proxy Integration option. Without it, the event argument will be empty.
Lambda proxy integration

User pools

We need to create a User Pool. As I mentioned earlier, we use the User Pool as an identity store for Identity Pools. If your integrators prefer not to create another user account (with a separate login and password), they can use an alternative identity provider such as Google or Facebook.
To create a User Pool, simply fill out the required form. Additionally, don’t forget to provide the API Gateway endpoint link that was created in the second step.
The form of creating a user pool

Identity pools

We need to create an Identity Pool to enable swapping the JWT token issued by the User Pool for temporary AWS credentials. I want to emphasize once again that access to AWS resources is only possible through AWS credentials.
To create an Identity Pool, you’ll need to go through a few steps:

  • In the first step, choose Authenticated access and select Amazon Cognito User Pool as the authentication provider.

The first step of the Identity Pool form.

  • The second step is about configuring the default IAM role. You need to choose Create a new role and provide a meaningful name for it.

After creating the Identity Pool, we will assign the required permissions to this role.

The second step of the Identity Pool form.

  • The third step is about configuring your Cognito identity pool to accept users that sign in with a Cognito user pool. Simply choose the user pool created earlier.

The third step of the Identity Pool form.

  • You can easily skip the fourth step and just click the Next button.
  • In the final step, after reviewing, you can click the Create Identity Pool button.

IAM

In the previous step, when we created the Identity Pool, we only provided a name for the role that will be assumed with temporary credentials. We need to update its policy to grant read access to our private bucket.

Please open the permissions policies for this role and paste the following two actions. Don’t forget to replace BUCKET_NAME with the actual bucket name.

Set environment variables

Finally, navigate to the Lambda service, select the Lambda function, and open the Configuration tab. Here, we need to set all the required environment variables. If you're using the code from my gist, you’ll need to define six variables.

Env Variables form for the lambda function

That’s it — we’re almost done. Due to the hardcoded file format in the Lambda function code, I would recommend uploading a txt file into the S3 bucket. Once you've done that, we’re ready to test.

To test, navigate to User Pools > App clients > Login pages. There, you can easily find the View login page link.

View login page button

Click on this button, and it will navigate you to a new page where you'll find the sign-in form. Before you proceed with the sign-in process, you need to modify the URL in the browser’s address bar. Add &state=FILE_NAME_INTO_S3 at the end of the URL. The state parameter allows us to pass the file name into the Lambda function. More details about this parameter you can find here