<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nicolás</title>
    <description>The latest articles on DEV Community by Nicolás (@nicolasv).</description>
    <link>https://dev.to/nicolasv</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3565127%2F19717cd4-f5f5-4939-bbc9-6ae307f662d1.png</url>
      <title>DEV Community: Nicolás</title>
      <link>https://dev.to/nicolasv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nicolasv"/>
    <language>en</language>
    <item>
      <title>Do you know the difference between Terraform Provider and Module?</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Mon, 20 Jul 2026 15:00:00 +0000</pubDate>
      <link>https://dev.to/nicolasv/do-you-know-the-difference-between-terraform-provider-and-module-2chh</link>
      <guid>https://dev.to/nicolasv/do-you-know-the-difference-between-terraform-provider-and-module-2chh</guid>
      <description>&lt;p&gt;Terraform is one of the most popular Infrastructure as Code (IaC) tools in the world. It allows us to provision infrastructure resources programmatically and declaratively, meaning we define what we want, and Terraform figures out how to build it.&lt;/p&gt;

&lt;p&gt;We do not need to worry about the specific API calls required to provision resources in AWS, GCP, Azure, or other platforms because Terraform abstracts that complexity. However, to use it effectively, we must understand two core components: Providers and Modules. &lt;/p&gt;

&lt;h2&gt;
  
  
  Providers - The translator layer
&lt;/h2&gt;

&lt;p&gt;Imagine you want to deploy an EC2 instance in AWS. To do this, Terraform needs to know how to authenticate with your AWS account and how to translate your configuration code into the specific API calls that AWS understands.&lt;/p&gt;

&lt;p&gt;Providers act as these translators. They are plugins that tell Terraform how to interact with external platforms like AWS, GCP, Azure, GitHub, etc. They encapsulate all the platform-specific logic, allowing you to focus purely on defining your resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Terraform Registry
&lt;/h3&gt;

&lt;p&gt;Terraform relies on an extensive plugin system that you can explore in the Terraform Registry (&lt;a href="https://registry.terraform.io/" rel="noopener noreferrer"&gt;https://registry.terraform.io/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5vo2k5wyi4d7j6l20fho.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5vo2k5wyi4d7j6l20fho.PNG" alt=" " width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Providers
&lt;/h3&gt;

&lt;p&gt;Using a provider is super simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provider "aws" {
  region = "us-west-2"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a best practice, providers are usually defined in a dedicated &lt;code&gt;providers.tf&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Once the provider is set up, Terraform knows exactly how to read an AWS resource block and interact with the AWS API to deploy it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Modules - Reusability is served
&lt;/h2&gt;

&lt;p&gt;In traditional programming, we follow the DRY principle (Don't Repeat Yourself) to avoid repeating code over and over. So, we use functions or classes to group collections of related logic, variables, and operations into reusable blocks. By passing different parameters to these functions, we can execute the same behavior in various contexts without duplicating the underlying code.&lt;/p&gt;

&lt;p&gt;Terraform modules apply this principle to infrastructure. Just as a function prevents us from rewriting the same script, a module prevents us from rewriting the same resource blocks. &lt;/p&gt;

&lt;p&gt;They allow us to group a collection of related resources, think of an EC2 instance, its security groups, its volumes...., into a single, standardized package that can be deployed repeatedly just by changing the input variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using modules
&lt;/h3&gt;

&lt;p&gt;Imagine that we need to deploy S3 buckets for different departments within the company. Instead of typing over and over the multiple Terraform resources to define the buckets, we could create a module that simply accepts a name and environment as parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# modules/secure_bucket/main.tf
variable "bucket_name" {
  type = string
}

variable "environment" {
  type = string
}

resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name

  tags = {
    Environment = var.environment
    ManagedBy   = "Terraform Module"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then, we would call it as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# main.tf
module "financial_data_bucket" {
  source = "./modules/secure_bucket"

  bucket_name = "company-financial-data-2026"
  environment = "Production"
}

module "app_assets_bucket" {
  source = "./modules/secure_bucket"

  bucket_name = "company-app-assets-2026"
  environment = "Development"
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As a takeaway, let's remember that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Providers: provide the connectivity components to external platforms by translating your Terraform code to the right API calls.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modules: containers of multiple resources used together. Allows reusability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to prevent member accounts from leaving your AWS organization</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Sun, 19 Jul 2026 15:00:00 +0000</pubDate>
      <link>https://dev.to/nicolasv/how-to-prevent-member-accounts-from-leaving-your-aws-organization-1hhl</link>
      <guid>https://dev.to/nicolasv/how-to-prevent-member-accounts-from-leaving-your-aws-organization-1hhl</guid>
      <description>&lt;p&gt;When you start in AWS, you probably start small.&lt;/p&gt;

&lt;p&gt;You open an account and start trying out services, building PoCs, and understanding how everything works before committing to designing environments to host your production workloads.&lt;/p&gt;

&lt;p&gt;Other teams hear that you have AWS access and start requesting access to the same account to also build PoCs for their specific use cases. But soon the account starts to get messy: resources from different teams are scattered everywhere, and teams see their PoCs impacted by others deleting resources. So, you start looking for a better solution.&lt;/p&gt;

&lt;p&gt;You then create a second standalone account, and then a third, and a fourth. You grant access to each team so that everyone has environments completely isolated from each other.&lt;/p&gt;

&lt;p&gt;However, you soon realize that dealing with separate billing, security, and governance across multiple accounts is a pain, so you start looking for a better alternative again.&lt;/p&gt;

&lt;p&gt;You start exploring AWS Organizations. You create one org and invite each of the standalone accounts so you can now benefit from consolidated billing, access to org-wide discounts, enhanced governance, and improved security.&lt;/p&gt;

&lt;p&gt;But just as easily as the AWS accounts joined, they can also leave your organization, which represents a significant security problem.&lt;/p&gt;

&lt;p&gt;Think about the consequences of having an AWS account with access to sensitive information suddenly becoming independent. You lose all your governance and security controls that were implemented at the organization level.&lt;/p&gt;

&lt;p&gt;So, you start thinking about implementing security controls to prevent this.&lt;/p&gt;

&lt;p&gt;When it comes to security, the best approach is having a layered defense. Just in case one fails, another is active to protect the fort.&lt;/p&gt;

&lt;p&gt;The first control that we can implement is tied to the Principle of Least Privilege. Closing an account or leaving an organization are highly sensitive operations, and nobody should have enough permissions to perform them. It could be argued that you could have a handful of privileged users in case of an emergency, and that's fine as long as you accepted the risk. Just make sure that those privileged users are highly restricted and monitored. &lt;/p&gt;

&lt;p&gt;The specific permissions you should restrict are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;organizations:LeaveOrganization&lt;/li&gt;
&lt;li&gt;account:CloseAccount&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, simply don't add them to any IAM policy. &lt;/p&gt;

&lt;p&gt;The second control would be at the org level via a Service Control Policy (SCP). These are organizational policies that let us control the maximum permissions principals can have across the organization. They act as guardrails and are enforced in every single AWS account in your organization, assuming you have attached them at the root level.&lt;/p&gt;

&lt;p&gt;The following SCP blocks principals in your organization from closing accounts and from attempting to remove member accounts from the organization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BlockOrgSensitiveActions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"organizations:LeaveOrganization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"account:CloseAccount"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The good thing is that if you created an AWS Organization via the AWS console after July 10, 2026, an SCP is created for you and attached at the root level to prevent this very same concern. If you did it before that date, or used the API, you will still need to manually configure the SCP.&lt;/p&gt;

</description>
      <category>security</category>
      <category>aws</category>
      <category>governance</category>
    </item>
    <item>
      <title>Do you pin your base images in Dockerfile?</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Sat, 18 Jul 2026 12:48:39 +0000</pubDate>
      <link>https://dev.to/nicolasv/do-you-pin-your-base-images-in-dockerfile-90a</link>
      <guid>https://dev.to/nicolasv/do-you-pin-your-base-images-in-dockerfile-90a</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Using tags like latest or numeric versions (3.23.5) in your Dockerfile is dangerous because they are mutable and can change without warning.&lt;/p&gt;

&lt;p&gt;These changes can break your application in production or expose you to supply chain attacks if the base image is compromised.&lt;/p&gt;

&lt;p&gt;It is crucial to always pin your base image to a specific SHA (e.g., FROM alpine@sha256:...) since it is the only way to guarantee immutability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;If you work in tech, you've probably heard of Docker and the famous containers. If not, the main thing you need to know is that it's a technology that allows you to package applications into containers, thus solving the legendary "it works on my machine, but it won't even open on yours" problem.&lt;/p&gt;

&lt;p&gt;These containers run our application from an image, which includes our code and everything it needs to work.&lt;/p&gt;

&lt;p&gt;To build this image, we use a file called Dockerfile. &lt;/p&gt;

&lt;h2&gt;
  
  
  So what is the Dockerfile?
&lt;/h2&gt;

&lt;p&gt;The Dockerfile is just a plain text file. It doesn't even use a specific extension. You simply need to name the file Dockerfile for Docker to recognize it.&lt;/p&gt;

&lt;p&gt;In this file, we define the instructions we want Docker to follow to create an image. Something along the lines of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use this base image&lt;/li&gt;
&lt;li&gt;Run commands XYZ&lt;/li&gt;
&lt;li&gt;Remove these services&lt;/li&gt;
&lt;li&gt;and so on and so on&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once we're done, creating the image is super simple. We just need to open the CLI, navigate to the location where the Dockerfile is, and run the command &lt;code&gt;docker build . -t &amp;lt;Name&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Base Image
&lt;/h2&gt;

&lt;p&gt;To build any image in Docker, we start with a base image. This image will be like a canvas on which we will install our application and add our own dependencies.&lt;/p&gt;

&lt;p&gt;It is important to keep in mind that it's not a blank canvas: the image already comes with pre-installed software that may or may not be useful to us. If it isn't, the best practice is to uninstall it to reduce the attack surface, but that's a topic for another post.&lt;/p&gt;

&lt;p&gt;For example, if we run the command with the following code, Docker will create a local image starting from the latest available version of &lt;code&gt;alpine&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:latest&lt;/span&gt;

&lt;span class="c"&gt;# Print to console to verify execution&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["echo", "Hi!"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Problem with Selecting Versions
&lt;/h2&gt;

&lt;p&gt;There are three main ways to select a base image version: using &lt;code&gt;latest&lt;/code&gt;, a specific version, or the hash identifier (SHA). &lt;/p&gt;

&lt;p&gt;You've probably already come across many tutorials or blogs that use base images like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:latest&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:3.23.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason why is no mystery: it's simple, readable, and it's easy to understand which version is being used.&lt;/p&gt;

&lt;p&gt;If we use &lt;code&gt;latest&lt;/code&gt;, we ensure that at build time, the most recent version is used, and if we use the second option, we make it explicitly clear that we want to use version &lt;code&gt;3.23.5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, selecting the base image version in these ways can open the door to two major security risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supply chain attacks&lt;/li&gt;
&lt;li&gt;Introduction of vulnerabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(If you've used GitHub Actions before, this problem will be familiar to you).&lt;/p&gt;

&lt;p&gt;The problem with using &lt;code&gt;latest&lt;/code&gt; is that, as its name suggests, it can change from one day to the next. This can cause our application to stop working due to the introduction of breaking changes. &lt;/p&gt;

&lt;p&gt;Even worse, if an attacker manages to compromise the accounts of the maintainers, they could publish a malicious version that would be downloaded by pipelines using &lt;code&gt;latest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We might think that specifying a version would free us from this problem, but that's not the case. A version like &lt;code&gt;3.23.5&lt;/code&gt; is actually a tag, just like &lt;code&gt;latest&lt;/code&gt;, that points to specific code. &lt;/p&gt;

&lt;p&gt;These tags are mutable, which means maintainers could make changes such as updating the base image to patch vulnerabilities and have the &lt;code&gt;3.23.5&lt;/code&gt; tag point to this new code.&lt;/p&gt;

&lt;p&gt;The solution to these problems is to pin the base image to a specific SHA.&lt;/p&gt;

&lt;h2&gt;
  
  
  Always Pin the Image
&lt;/h2&gt;

&lt;p&gt;The only way to achieve immutability is through this method. This guarantees that we always use exactly the same version, since any minor change in the image's code will produce a different SHA. &lt;/p&gt;

&lt;p&gt;There is no excuse not to use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine@sha256:fd791d74b68913cbb027c6546007b3f0d3bc45125f797758156952bc2d6daf40 # 3.22.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is a good practice to include a comment on the same line indicating the version the SHA refers to.&lt;/p&gt;

</description>
      <category>security</category>
      <category>docker</category>
      <category>containers</category>
      <category>devops</category>
    </item>
    <item>
      <title>Terraform - How to prevent sensitive data from ending up in the .tfstate</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Sat, 11 Jul 2026 18:19:50 +0000</pubDate>
      <link>https://dev.to/nicolasv/terraform-how-to-prevent-sensitive-data-from-ending-up-in-the-tfstate-3hdo</link>
      <guid>https://dev.to/nicolasv/terraform-how-to-prevent-sensitive-data-from-ending-up-in-the-tfstate-3hdo</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;By default, Terraform stores all secrets in plaintext within its state file (.tfstate). The traditional &lt;code&gt;sensitive = true&lt;/code&gt; argument only hides the data in the CLI, but does not protect it in the state. The definitive solution to securely inject secrets at runtime without leaving a trace in the state is to combine &lt;code&gt;ephemeral = true&lt;/code&gt; variables with the write-only arguments of each provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Terraform is one of the most popular IaC (Infrastructure as Code) tools among DevOps teams. And it's no surprise; it's portable, has a wide catalog of providers, and is relatively easy to use.&lt;/p&gt;

&lt;p&gt;With Terraform, we can deploy infrastructure, keep track of its configuration, and finally, delete it when it is no longer needed, all with just a few commands.&lt;/p&gt;

&lt;p&gt;To properly work, Terraform needs to know the resources it has deployed as well as their configuration. To achieve this, it relies on a state file with the &lt;code&gt;.tfstate&lt;/code&gt; extension that acts as a resource inventory.&lt;/p&gt;

&lt;p&gt;This file is automatically managed by Terraform and reflects the state of our infrastructure. By default, the state is saved in plaintext, so anyone with access to it can read it without problems.&lt;/p&gt;

&lt;p&gt;This represents a security problem for two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It reveals the design of the environment. It allows knowing the infrastructure, the resources, how they connect to each other, as well as their configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It implies potential access to secrets.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We know that Terraform needs to store the value of these secrets to check if they change, but is there a safer way to manage them and prevent them from ending up exposed in the state?&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does the Documentation Say?
&lt;/h2&gt;

&lt;p&gt;As good engineers, our first instinct is to check the official documentation (or interrogate our trusted AI), which is quite clear about this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you add secret values directly to your configuration, Terraform stores those secrets in its state and plan files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fortunately, the documentation also points us to two key arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;sensitive&lt;/code&gt;: Tells Terraform to hide the value of a variable or output in the CLI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ephemeral&lt;/code&gt;: Allows handling values at runtime without persisting them in the state.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It should be noted that both arguments are not mutually exclusive, so you can use them together to ensure that a secret neither appears in the terminal nor leaves a trace in the state.&lt;/p&gt;

&lt;p&gt;But, if we don't store the secret's value, how does Terraform know when to update it? When using &lt;code&gt;ephemeral&lt;/code&gt;, &lt;code&gt;write-only&lt;/code&gt; arguments defined by each provider are also typically used. These arguments allow passing secrets, which are only available during runtime, to the resources without them becoming part of the state.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;write-only&lt;/code&gt; arguments usually end with the suffix &lt;code&gt;_wo&lt;/code&gt; and have their corresponding version argument &lt;code&gt;_wo_version&lt;/code&gt;. The latter are used by Terraform to know when to update the secrets, so we just have to increment the value of that version for Terraform to detect the change and apply the new secret.&lt;/p&gt;

&lt;p&gt;True to the motto of "Trust, but verify", let's move from theory to practice to check it for ourselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 1: Hardcoded Secret
&lt;/h2&gt;

&lt;p&gt;The first scenario is to hardcode secrets directly into the Terraform configuration. As we have already mentioned, this causes them not only to be stored in the state, but also allows anyone with access to the configuration itself to read the secret effortlessly.&lt;/p&gt;

&lt;p&gt;As an example, let's create a parameter within Parameter Store in AWS. These parameters can contain anything, since what they store is a string, and those things can include a secret, like an API key to access some platform.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt; &lt;span class="s2"&gt;"payment_api_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API Key for processing payments securely"&lt;/span&gt;
  &lt;span class="c1"&gt;# "SecureString" ensures AWS encrypts this value at rest&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SecureString"&lt;/span&gt;
  &lt;span class="c1"&gt;# The value is passed in securely via the variable&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MiSuperSecreto876#"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
    &lt;span class="nx"&gt;Application&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"billing"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code creates a parameter named &lt;code&gt;/production/payment-gateway/api-key&lt;/code&gt; of type &lt;code&gt;SecureString&lt;/code&gt; (encrypted with KMS), applies two tags (&lt;code&gt;Environment=production&lt;/code&gt; and &lt;code&gt;Application=billing&lt;/code&gt;) to it, and defines the value of the secret: &lt;code&gt;MiSuperSecreto876#&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we run a &lt;code&gt;terraform plan&lt;/code&gt;, we see how the secret appears masked and in its place we find &lt;code&gt;value = (sensitive value).&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F432vo7xzdwhjch9sywv5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F432vo7xzdwhjch9sywv5.png" alt=" " width="800" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, didn't we mention earlier that, in order to hide a secret in the CLI, we had to explicitly use the &lt;code&gt;sensitive = true&lt;/code&gt; argument?&lt;/p&gt;

&lt;p&gt;That's correct. However, there is a default behavior that has our backs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Terraform also treats any expressions that reference a sensitive variable or output as inherently sensitive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's take a look at the state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"tags_all"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SecureString"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MiSuperSecreto876#"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected, Terraform has stored the secret in the state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 2: The &lt;code&gt;sensitive&lt;/code&gt; argument to the rescue (or almost)
&lt;/h2&gt;

&lt;p&gt;For the following scenario, we're going to apply some best practices. Instead of leaving the secret exposed in the code for anyone to see, we will move it to an external variable and use the &lt;code&gt;sensitive=true&lt;/code&gt; argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"payment_api_key_value"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The secret value for the payment API key"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt; &lt;span class="s2"&gt;"payment_api_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API Key for processing payments securely"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SecureString"&lt;/span&gt;
  &lt;span class="c1"&gt;# 2. Reference the sensitive variable&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payment_api_key_value&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
    &lt;span class="nx"&gt;Application&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"billing"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon running a &lt;code&gt;terraform plan&lt;/code&gt;, the terminal output will be identical to the first scenario: the secret will not be shown in the CLI and Terraform will mask it completely.&lt;/p&gt;

&lt;p&gt;However, the state still contains the secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"tags_all"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SecureString"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MiSuperSecreto876#"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is proven then that sensitive only hides the value in the CLI, but Terraform still saves it in the &lt;code&gt;.tfstate&lt;/code&gt; so it can track changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 3: Secrets in an external platform
&lt;/h2&gt;

&lt;p&gt;One of the most highly recommended practices when using secrets in Terraform is storing them in an external manager like AWS Secrets Manager and consuming them at runtime.&lt;/p&gt;

&lt;p&gt;For this scenario, I've used HashiCorp Vault locally with the same Terraform code as the previous scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"payment_api_key_value"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The secret value for the payment API key"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt; &lt;span class="s2"&gt;"payment_api_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API Key for processing payments securely"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SecureString"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payment_api_key_value&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
    &lt;span class="nx"&gt;Application&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"billing"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we will execute it using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;terraform apply -var="payment_api_key_value=$&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;vault kv get &lt;span class="nt"&gt;-address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8200"&lt;/span&gt; &lt;span class="nt"&gt;-field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password secret/myapp/database&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which produces a plan similar to the previous ones with the secret masked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0azt7z8zreeq7h8b7pkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0azt7z8zreeq7h8b7pkg.png" alt=" " width="800" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, the secret is still visible in the state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"API Key for processing payments securely"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags_all"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SecureString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PasswordSuperSecretaDeVault#"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scenario 4: Using &lt;code&gt;ephemeral&lt;/code&gt; for the win
&lt;/h2&gt;

&lt;p&gt;In the last scenario, we are going to test how the ephemeral argument works. As we mentioned earlier, this argument prevents Terraform from storing the secret in the state.&lt;/p&gt;

&lt;p&gt;To start, let's add &lt;code&gt;ephemeral=true&lt;/code&gt; to our variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"payment_api_key_value"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The secret value for the payment API key"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;ephemeral&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterward, we add the write-only arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt; &lt;span class="s2"&gt;"payment_api_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API Key for processing payments securely"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SecureString"&lt;/span&gt;
  &lt;span class="nx"&gt;value_wo&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payment_api_key_value&lt;/span&gt;
  &lt;span class="nx"&gt;value_wo_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
    &lt;span class="nx"&gt;Application&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"billing"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running terraform plan, we see how the secret is hidden thanks to sensitive (just like before).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0g3c0g1yn3y607523gvj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0g3c0g1yn3y607523gvj.png" alt=" " width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we apply the changes with &lt;code&gt;terraform apply&lt;/code&gt;, we see that there is no trace of the secret in the state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"API Key for processing payments securely"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"has_value_wo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/production/payment-gateway/api-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags_all"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SecureString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value_wo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value_wo_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Marking a variable as sensitive is a necessary measure to hide secrets in the CLI and logs, but it is insufficient on its own, as the secrets remain exposed to anyone with access to the state.&lt;/p&gt;

&lt;p&gt;Consuming secrets at runtime from platforms like AWS Secrets Manager or HashiCorp Vault mitigates the risk of exposing them in the source code, but Terraform will still save the final value in the .tfstate if ephemeral is not used.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ephemeral&lt;/code&gt; and &lt;code&gt;write-only&lt;/code&gt; arguments prevent secrets from ending up in the state.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>terraform</category>
      <category>security</category>
    </item>
    <item>
      <title>Basic protections for your S3 buckets</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Thu, 08 Jan 2026 20:12:10 +0000</pubDate>
      <link>https://dev.to/nicolasv/basic-protections-for-your-s3-buckets-388n</link>
      <guid>https://dev.to/nicolasv/basic-protections-for-your-s3-buckets-388n</guid>
      <description>&lt;p&gt;S3 is one of the most popular storage services in AWS due to its simplicity. You can easily store large amounts of data for multiple purposes at a relatively low cost.&lt;/p&gt;

&lt;p&gt;Regardless of your use case, I imagine you want your precious data to be stored securely, not only because of the trouble you could face with regulators, but because it's simply the right thing to do.&lt;/p&gt;

&lt;p&gt;Today, I am going to talk about some basic safeguards that you can implement in S3 to help secure your data with no extra cost. &lt;/p&gt;

&lt;p&gt;Let's start with a classic one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encryption
&lt;/h3&gt;

&lt;p&gt;Encryption is the process of taking readable information, like your secret master plan that will make you rich, and converting it into unreablable format so that only authorized parties can access it. &lt;/p&gt;

&lt;p&gt;I won't bore you with technical details but the key point is that encryption is important.&lt;/p&gt;

&lt;p&gt;Most encryption you will see fall into two categories: at rest, and in transit. You may also hear about encryption in use, but that one is out of scope for this article.&lt;/p&gt;

&lt;h4&gt;
  
  
  Encryption at rest
&lt;/h4&gt;

&lt;p&gt;In simple terms, encryption at rest deals with data that sits idle, waiting in your storage to be used. You can think of encryption at rest as  having a safe where you store a top secret notebook, only authorized parties, the ones that know the code, can open the safe and access your data.&lt;/p&gt;

&lt;p&gt;There are two main encryption methods in S3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server-side -&amp;gt; AWS encrypts the data for you before saving it on disks&lt;/li&gt;
&lt;li&gt;Client-side -&amp;gt; You encrypt the data before uploading it to S3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The good thing is that buckets have encryption at rest configured by default. That way, all the objects you upload will be using Server-side encryption with AMazon S3 managed keys (SSE-S3). ALl this will be transparent to you and free of charge. &lt;/p&gt;

&lt;p&gt;For most use-cases, SSE-S3 will be more than enough, but if you want more control, you can opt to encrypt your data using a KMS key you control.&lt;/p&gt;

&lt;h4&gt;
  
  
  Encryption in transit
&lt;/h4&gt;

&lt;p&gt;The other encryption category we mentioned is encryption in transit. This one is in charge of protecting the data as it moves around, like when it moves from AWS to your devices as you download it.&lt;/p&gt;

&lt;p&gt;Encryption in transit is important to guarantee the secret of communications. If there was no encryption, anyone that intercept your network traffic could read into your emails and know what your last purchase at Amazon was. &lt;/p&gt;

&lt;p&gt;This type of encryption is achieved using asymmetric encryption, which relies on a pair of keys, a public key and a private key, to establish a secure channel between two parties for sharing information.&lt;/p&gt;

&lt;p&gt;Think of a mailbox that anyone can drop letters into, but only the owner can open.&lt;/p&gt;

&lt;p&gt;The mailbox slot is like the public key: anyone can use it to send a message securely. The key that opens the mailbox is the private key: only the owner has it. Even if someone watches you drop a letter into the mailbox, they cannot read it because they do not have the key.&lt;/p&gt;

&lt;p&gt;Asymmetric encryption works the same way. If someone were to intercept your emails, the contents would appear as gibberish because that person does not have access to the private key used to protect the communication channel.&lt;/p&gt;

&lt;p&gt;S3 buckets allow you to access your data using either HTTP (unencrypted) or HTTPS (encrypyed) endpoints. It is highly recommended to only allow access via the latter. To enforce this, you can apply the following bucket policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version":"2012-10-17",              
    "Statement": [{
        "Sid": "RestrictToTLSRequestsOnly",
        "Action": "s3:*",
        "Effect": "Deny",
        "Resource": [
            "arn:aws:s3:::&amp;lt;yourBucket&amp;gt;",
            "arn:aws:s3:::&amp;lt;yourBucket&amp;gt;/*"
        ],
        "Condition": {
            "Bool": {
                "aws:SecureTransport": "false"
            }
        },
        "Principal": "*"
    }]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Public/Private buckets
&lt;/h3&gt;

&lt;p&gt;In AWS you have two types of buckets, public and private. As you may have already guessed by their names, public buckets can be accessed by anyone in the world whereas private ones are only accessible to authorized individuals.&lt;/p&gt;

&lt;p&gt;Luckily for all, new buckets are private by default so you would need to go on the extra mile to make it public.&lt;/p&gt;

&lt;p&gt;But hey, mistakes can happen.&lt;/p&gt;

&lt;p&gt;To ensure you have a layered defense that prevents someone from simply turning a private bucket into a public one, consider enabling the &lt;em&gt;Block Public Access&lt;/em&gt; feature.&lt;/p&gt;

&lt;p&gt;Now, this feature can be enabled at three levels: bucket, account, and organization: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Block Public Access (Bucket) -&amp;gt; This is the level you are most likely to have enabled by default when a bucket is private.&lt;/li&gt;
&lt;li&gt;Block Public Access (Account) -&amp;gt; This level blocks public access to all buckets across an account. &lt;/li&gt;
&lt;li&gt;Block Public Access (Organization) -&amp;gt; If you work at a company that have a large AWS organization, you can turn this feature on at the org level so no buckets within your org can be made public.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Least privilege
&lt;/h3&gt;

&lt;p&gt;You may have already come across the principle of least privilege, but if not, let me introduce it. Least privilege is a security concept that states you should grant an entity (such as a person, application, or service account) only the minimum set of permissions required to perform a specific task, and no more.&lt;/p&gt;

&lt;p&gt;Applying this principle helps reduce the risk of accidental misuse or malicious activity. Therefore, to ensure your data is protected in S3, you must carefully scope IAM permissions so that users and services can access only the buckets and actions they truly need. &lt;/p&gt;

&lt;p&gt;For example, if you have a bucket that stores marketing information, only the Marketing team should have access to it.&lt;/p&gt;

&lt;p&gt;Keep in mind that least privilege is not a one-off exercise. It requires ongoing maintenance and regular audits as responsibilities change, teams are reorganized, or access requirements evolve over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning &amp;amp; MFA delete
&lt;/h3&gt;

&lt;p&gt;If you want to further protect your data against malicious or accidental deleition, you can enable versioning and MFA delete on buckets.&lt;/p&gt;

&lt;p&gt;When versioning is enabled on a bucket, S3 keeps multiple versions of an object instead of permanently replacing or removing it. This allows you to recover previous versions of a file if it is deleted or modified by mistake. FYI, this feature cannot be disabled once it's enabled on a bucket and it comes with extra cost due to the multiple versions stored!&lt;/p&gt;

&lt;p&gt;To go one extra step further, you can enable MFA Delete. MFA Delete requires users to provide multi-factor authentication (MFA) in addition to their credentials when performing sensitive operations such as permanently deleting an object version or suspending versioning on a bucket. This adds an extra layer of security by ensuring that even if credentials are compromised, critical destructive actions cannot be performed without physical access to the MFA device.&lt;/p&gt;

&lt;p&gt;Together, versioning and MFA Delete provide an effective safeguard against data loss, whether caused by human error or malicious activity.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>security</category>
    </item>
    <item>
      <title>Top 10 AWS Security Mistakes Beginners Must Avoid</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Sun, 04 Jan 2026 10:24:41 +0000</pubDate>
      <link>https://dev.to/nicolasv/avoid-these-top-10-aws-security-mistakes-beginners-make-46b1</link>
      <guid>https://dev.to/nicolasv/avoid-these-top-10-aws-security-mistakes-beginners-make-46b1</guid>
      <description>&lt;p&gt;AWS provides a highly secure cloud platform, but that does not mean your environment is secure by default. As we explored in the &lt;a href="https://dev.to/ciberconscientes/aws-shared-responsibility-model-explained-54a6"&gt;AWS Responsibility Model&lt;/a&gt; article, security is a shared responsibility so you must do your part to ensure your resources are safe. &lt;/p&gt;

&lt;p&gt;Most AWS security incidents do not involve advanced attackers or zero-day exploits. They happen because of basic mistakes, often made by people who are new to the cloud and still applying on-prem thinking to a very different environment.&lt;/p&gt;

&lt;p&gt;This article walks through the top 10 AWS security mistakes beginners make, explains why they are risky, and shows how to avoid them.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Using the Root Account for Daily Work
&lt;/h3&gt;

&lt;p&gt;Every AWS account has a root user with unlimited privileges. This is effectively "God Mode" and can do almost anything on your account. Beginners often use it because it’s easy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It’s a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Root user has full control over the account&lt;/li&gt;
&lt;li&gt;If compromised, your entire account is at risk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure the root user uses a strong password&lt;/li&gt;
&lt;li&gt;Don't create access keys for the root user&lt;/li&gt;
&lt;li&gt;Enable MFA on the root user&lt;/li&gt;
&lt;li&gt;Create alternative access (IAM user or IAM role) for daily use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
It's recommended not using the root account unless absolutely necessary. &lt;/p&gt;

&lt;p&gt;If you are experimenting with AWS, an IAM user will likely suffice for the time being. However, keep in mind that IAM users have long-term credentials such as passwords and access keys, which you should rotate regularly.&lt;/p&gt;

&lt;p&gt;The best practice, however, is to create an use an IAM role for daily operations as roles provide temporary credentials by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Not Enabling Multi-Factor Authentication (MFA)
&lt;/h3&gt;

&lt;p&gt;Passwords alone are not enough, especially in the cloud that can be accessed from anywhere in the world. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It’s a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If credentials are leaked through phishing or reuse, attackers can log in immediately. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adding MFA adds a second layer of defense that protects your account. Attackers would need to also get hold of the second authenticator factor to successfully log in.&lt;/p&gt;

&lt;p&gt;Recommendations: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable MFA for the root account&lt;/li&gt;
&lt;li&gt;Require MFA for privileged IAM users&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Having public S3 buckets
&lt;/h3&gt;

&lt;p&gt;S3 is one of the core services in AWS. It easily lets you store objects like files, images, videos, etc. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It’s a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Public S3 buckets can expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer data&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;li&gt;Logs&lt;/li&gt;
&lt;li&gt;Intellectual property&lt;/li&gt;
&lt;li&gt;Source code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many major data breaches started this way. If data should not be public, explicitly prevent it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All new buckets are private by default, but a single misconfiguration can unintentionally expose their contents to the internet.&lt;/p&gt;

&lt;p&gt;Recommendations: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review bucket policies carefully&lt;/li&gt;
&lt;li&gt;Implement alerts to notify you when a bucket goes public (AWS Config, CloudTrail, EventBridge services can help)&lt;/li&gt;
&lt;li&gt;Enable S3 Block Public Access at the account level or organization-level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
Having public S3 buckets is not inherently a security issue. There are valid use cases for public buckets. Just ensure the objects stored in them are truly intended to be public.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Overly permissive Security Groups
&lt;/h3&gt;

&lt;p&gt;Security Groups are AWS’s primary network firewall, but beginners often configure them too loosely, or use the default ones because they "just work" at the cost of being highly permissive. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It’s a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Overly permissive security groups are problematic because they significantly increase the attack surface of your environment. &lt;/p&gt;

&lt;p&gt;When services like SSH, RDP, or databases are exposed to the internet, they become easy targets for automated scans, brute-force attacks, and exploitation attempts. &lt;/p&gt;

&lt;p&gt;Even if the underlying application is secure, unnecessary exposure increases the likelihood of compromise.&lt;/p&gt;

&lt;p&gt;For instance, a security group with a rule like:&lt;/p&gt;

&lt;p&gt;SSH (22) from 0.0.0.0/0&lt;/p&gt;

&lt;p&gt;allows SSH traffic from the entire internet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review inbound and outbound rules regularly &lt;/li&gt;
&lt;li&gt;Restrict access to known IP ranges and expected ports/traffic&lt;/li&gt;
&lt;li&gt;Use AWS System Manager Session Manager instead of SSH where possible&lt;/li&gt;
&lt;li&gt;Monitor changes via AWS Config or a custom solution (i.e. EventBridge + Lambda)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Insufficient Password Policies
&lt;/h3&gt;

&lt;p&gt;Weak or poorly enforced password policies are a common security gap in AWS environments, especially in accounts that were set up quickly or without security in mind. &lt;/p&gt;

&lt;p&gt;By default, AWS allows basic password requirements unless you explicitly configure stronger rules, and many beginners never revisit these settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It's a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Passwords are often the first, and sometimes only, line of defense protecting AWS accounts. &lt;/p&gt;

&lt;p&gt;If password policies are too lenient, they become easy targets for brute-force attacks, credential stuffing, or simple guessing. &lt;/p&gt;

&lt;p&gt;This risk is amplified when users reuse passwords across multiple services or fall for phishing attempts.&lt;/p&gt;

&lt;p&gt;An attacker who successfully compromises a single IAM user with a weak password may gain access to sensitive resources, escalate privileges, or move laterally within the account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enforce strong IAM password policies (length, complexity, rotation)&lt;/li&gt;
&lt;li&gt;Require multi-factor authentication (MFA) for all users, especially privileged ones&lt;/li&gt;
&lt;li&gt;Disable unused IAM users and credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Strong password policies are a basic control, but they remain one of the most effective ways to reduce account compromise, especially when combined with MFA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Prefer IAM roles over long-lived user passwords whenever possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Insecure Use of Access Keys
&lt;/h3&gt;

&lt;p&gt;AWS access keys provide programmatic access to your account and services. While they are powerful, they are also easy to misuse. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It's a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Access keys are long-lived credentials. If they are exposed through source code repositories, configuration files, logs, or shared scripts, an attacker can use them immediately, often without triggering obvious alarms. &lt;/p&gt;

&lt;p&gt;Unlike passwords, access keys do not expire by default and are frequently forgotten after being created.&lt;/p&gt;

&lt;p&gt;Once compromised, access keys can be used from anywhere in the world to interact with AWS APIs, potentially allowing attackers to read data, launch resources, or incur significant costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use IAM roles instead of access keys whenever possible&lt;/li&gt;
&lt;li&gt;Store secrets in AWS Secrets Manager or Parameter Store (Secure String)&lt;/li&gt;
&lt;li&gt;Rotate access keys regularly and delete unused ones&lt;/li&gt;
&lt;li&gt;Monitor access key usage with CloudTrail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Access keys should be treated like highly sensitive secrets. In most modern AWS environments, they should be the exception, not the default.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Overly Permissive IAM Policies
&lt;/h3&gt;

&lt;p&gt;Beginners often use policies like AdministratorAccess or &lt;code&gt;*:*&lt;/code&gt; just to make things work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It’s a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Violates the principle of least privilege&lt;/li&gt;
&lt;li&gt;Increases blast radius if credentials are compromised&lt;/li&gt;
&lt;li&gt;Makes auditing difficult&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with minimal permissions&lt;/li&gt;
&lt;li&gt;Grant only what is required&lt;/li&gt;
&lt;li&gt;Use AWS-managed policies as learning references, not defaults&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Convenience today becomes risk tomorrow.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Logging and Monitoring Gaps
&lt;/h3&gt;

&lt;p&gt;If you don’t log activity, you won’t know when something goes wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It’s a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without logs, you cannot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detect suspicious behavior&lt;/li&gt;
&lt;li&gt;Investigate incidents&lt;/li&gt;
&lt;li&gt;Prove compliance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable AWS CloudTrail&lt;/li&gt;
&lt;li&gt;In large organizations with multiple AWS accounts, configure an organization trail in CloudTrail&lt;/li&gt;
&lt;li&gt;Protect CloudTrail so that only the security team can change settings&lt;/li&gt;
&lt;li&gt;Send logs to a protected S3 bucket&lt;/li&gt;
&lt;li&gt;Monitor alerts using CloudWatch, Security Hub or a third-party solution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visibility is a core security requirement, not an optional feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Insecure use and storage of Secrets and Access Keys
&lt;/h3&gt;

&lt;p&gt;Hard-coding passwords and API keys is a common beginner shortcut. Similarly, secrets that never change are secrets that eventually get compromised. &lt;/p&gt;

&lt;p&gt;One of the most common security oversights in AWS environments is creating credentials: passwords, access keys, or secrets, and then leaving them unchanged for months or even years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It’s a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Credentials can leak via source control&lt;/li&gt;
&lt;li&gt;They are hard to rotate&lt;/li&gt;
&lt;li&gt;They are often shared unintentionally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use AWS Secrets Manager or Parameter Store for secrets&lt;/li&gt;
&lt;li&gt;Use IAM roles instead of access keys and IAM users&lt;/li&gt;
&lt;li&gt;Rotate credentials frequently. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  10. No encryption of stored data
&lt;/h3&gt;

&lt;p&gt;Failing to encrypt data at rest is a common security gap in AWS environments, particularly in early-stage or test accounts where security controls are often overlooked. &lt;/p&gt;

&lt;p&gt;While AWS supports encryption for most storage services, it is not always enabled automatically or consistently across all resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It's a Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unencrypted data is vulnerable if storage is accessed by unauthorized users, compromised credentials, or misconfigured permissions. &lt;/p&gt;

&lt;p&gt;Without encryption, anyone who gains access to the underlying data can read it directly, including sensitive customer information, credentials, backups, or intellectual property.&lt;/p&gt;

&lt;p&gt;Encryption adds a critical layer of protection by ensuring that even if data is accessed improperly, it cannot be read without the appropriate encryption keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Avoid It&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For S3 buckets, encryption at rest is enabled by default but consider using your own encryption key&lt;/li&gt;
&lt;li&gt;Enforce encryption in transit when interacting with S3 buckets&lt;/li&gt;
&lt;li&gt;Require encryption for EBS volumes and snapshots&lt;/li&gt;
&lt;li&gt;Use AWS Key Management Service (KMS) to manage encryption keys&lt;/li&gt;
&lt;li&gt;Enforce encryption using AWS Config rules or service control policies (SCPs)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Security Groups vs Network ACLs: The Two Firewalls You Didn’t Know You Were Using in AWS</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Sat, 03 Jan 2026 11:55:33 +0000</pubDate>
      <link>https://dev.to/nicolasv/security-groups-vs-network-acls-the-two-firewalls-you-didnt-know-you-were-using-in-aws-4bjd</link>
      <guid>https://dev.to/nicolasv/security-groups-vs-network-acls-the-two-firewalls-you-didnt-know-you-were-using-in-aws-4bjd</guid>
      <description>&lt;p&gt;Firewalls are essential for deciding what traffic is allowed in or out of a network. In traditional, on-prem networks, controlling traffic is usually straighforward, not because the configuration is simple, but because the structure is (hopefully) well undestood. &lt;/p&gt;

&lt;p&gt;You know how the network is laid out, what connects to what, and what should be talking with what. In other words, you (hopefully) know the internal details.&lt;/p&gt;

&lt;p&gt;Security teams are used to thinking in terms of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single firewall (or a small number of them)&lt;/li&gt;
&lt;li&gt;Clearly defined network edges&lt;/li&gt;
&lt;li&gt;Rules applied at obvious choke points&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When moving to the cloud, this mental model no longer fully applies, although the concept of firewall still exist, it appears under a different name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firewalls in AWS
&lt;/h2&gt;

&lt;p&gt;AWS does not have a single perimeter firewall protecting your environment. Instead, traffic control is distributed across multiple layers and services. Security decisions are no longer made in just one place, but at different points within the network, sometimes at the subnet level, sometimes at the resource level.&lt;/p&gt;

&lt;p&gt;This shift in approach is often one of the biggest challenges for people new to AWS. The tools are different, the terminology is new, and familiar concepts do not always behave the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Networking basics
&lt;/h2&gt;

&lt;p&gt;Before discussing how we can control traffic in AWS, it is important to understand a few core networking constructs that everything else builds upon.&lt;/p&gt;

&lt;h3&gt;
  
  
  VPC (Virtual Private Cloud)
&lt;/h3&gt;

&lt;p&gt;A VPC is a logically isolated network within AWS. You can think of it as your own private data center network in the cloud.&lt;/p&gt;

&lt;p&gt;Within a VPC, you define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The IP address range (CIDR block)&lt;/li&gt;
&lt;li&gt;Subnets&lt;/li&gt;
&lt;li&gt;Routing rules&lt;/li&gt;
&lt;li&gt;Network security controls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, resources inside a VPC cannot be accessed from the internet unless you explicitly allow it.&lt;/p&gt;

&lt;p&gt;This gives you full control over how traffic flows into, out of, and within your environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subnet
&lt;/h3&gt;

&lt;p&gt;A subnet is a subdivision of a VPC’s IP address range. Each subnet exists within a single AWS Availability Zone and represents a smaller network segment.&lt;/p&gt;

&lt;p&gt;Subnets are commonly used to separate workloads based on purpose or exposure, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public subnets (internet-facing resources)&lt;/li&gt;
&lt;li&gt;Private subnets (internal services, databases)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Controlling network traffic
&lt;/h2&gt;

&lt;p&gt;Two of the most important network security controls in AWS are &lt;strong&gt;Security Groups&lt;/strong&gt; and &lt;strong&gt;Network ACLs&lt;/strong&gt;. Both act like firewalls, but they operate at different layers and follow different rules.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security Groups: protect individual resources (like EC2 instances)&lt;/li&gt;
&lt;li&gt;Network ACLs: protect entire subnets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at the following figure to understand it better:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhzxz1tpjtsh282vqpm5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhzxz1tpjtsh282vqpm5.png" alt="High-level diagram of controls applied to control traffic in AWS" width="685" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Traffic flows from the internet into AWS, but before it can reach your application, it must first stop at the subnet level, where a Network ACL verifies whether the traffic is allowed. If it is, the traffic continues and stops at the doorway of your application. There, a gatekeeper performs a second verification check to ensure the traffic is allowed to reach the application.&lt;/p&gt;

&lt;p&gt;As you can see, both controls work in combination to protect your workloads, as traffic can be dropped at either layer if the checks are unsuccessful.&lt;/p&gt;

&lt;p&gt;Let’s examine them in more detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network ACL
&lt;/h3&gt;

&lt;p&gt;A Network ACL (NACL) acts like a security gate at the entrance of a building. It controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What traffic can enter a subnet&lt;/li&gt;
&lt;li&gt;What traffic can leave a subnet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u6pysm788nydkjbfccs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u6pysm788nydkjbfccs.png" alt="Diagram showing NACL" width="351" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NACLs are applied at the subnet level and are free of charge. Every subnet must be associated with a NACL. If none is specified, the subnet is associated with the default NACL, which allows all inbound and outbound traffic. A subnet can only be associated with one NACL.&lt;/p&gt;

&lt;p&gt;NACLs are composed of rules that control traffic. Each rule can either allow or deny traffic, and rules are evaluated in order based on a number you assign to each rule. Evaluation starts with the lowest number and continues until the traffic matches a rule.&lt;/p&gt;

&lt;p&gt;A key concept of NACLs is that they are stateless, meaning inbound and outbound connections are treated separately. This means that both inbound and outbound rules must explicitly allow traffic for a connection to be successful.&lt;/p&gt;

&lt;p&gt;You must think about both directions: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inbound traffic must be explicitly allowed&lt;/li&gt;
&lt;li&gt;Outbound traffic must also be explicitly allowed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at an example.&lt;/p&gt;

&lt;p&gt;Imagine that you have a subnet where you host a public web server meant to be accessed from anywhere in the world by anyone.&lt;/p&gt;

&lt;p&gt;You need both HTTP (unencrypted, not recommended) and HTTPS traffic to reach your application. &lt;/p&gt;

&lt;p&gt;In this situation, your Inbound rules might look like this:&lt;/p&gt;

&lt;p&gt;100  ALLOW  HTTP  (80)   0.0.0.0/0&lt;br&gt;
110  ALLOW  HTTPS (443)  0.0.0.0/0&lt;br&gt;
     DENY   ALL&lt;/p&gt;

&lt;p&gt;Because NACLs, are stateless, we need to have rules allowing the outbound connectivity:&lt;/p&gt;

&lt;p&gt;100  ALLOW  ALL  0.0.0.0/0&lt;br&gt;
     DENY   ALL&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Groups
&lt;/h3&gt;

&lt;p&gt;A security group is associated with a resource and controls the traffic that reaches it and leaves it. In other words, it controls inbound and outbound traffic to and from the resource.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqae5vwoezzwnb6jkzn6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqae5vwoezzwnb6jkzn6s.png" alt="Diagram showing Security Groups" width="351" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similar to NACLs, security groups are composed of rules that control both inbound and outbound traffic. The components of these rules are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the source (i.e. IP address, IP range)&lt;/li&gt;
&lt;li&gt;port range (i.e. 22, 80, 25-30)&lt;/li&gt;
&lt;li&gt;protocol (TCP, UDP, ICMP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike NACLs, which support both ALLOW and DENY rules, security groups only support ALLOW rules. Another difference is that you can attach multiple security groups to a single resource.&lt;/p&gt;

&lt;p&gt;However, the most important difference between NACLs and security groups is that security groups are stateful. This means they keep track of allowed connections. If inbound traffic is allowed, the corresponding return traffic is automatically allowed.&lt;/p&gt;

&lt;p&gt;You only need to think about the initial request.&lt;/p&gt;

&lt;p&gt;You do not need to explicitly allow response traffic.&lt;/p&gt;

&lt;p&gt;Continuing with the web server example, the traffic has passed the NACL and is now attempting to reach your application.&lt;/p&gt;

&lt;p&gt;You create a security group with the following rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow inbound HTTP (port 80) from anywhere (0.0.0.0/0)&lt;/li&gt;
&lt;li&gt;Allow inbound HTTPS (port 443) from anywhere (0.0.0.0/0)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a user sends a request on port 80 or 443, the Security Group allows it, and the response is automatically allowed back.&lt;/p&gt;

&lt;p&gt;You do not need to add a rule for return traffic.&lt;/p&gt;

&lt;p&gt;This is what “stateful” means.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojf8a4vvhox0ixxcv2j7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojf8a4vvhox0ixxcv2j7.png" alt="NACL vs SG Comparison table" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use them
&lt;/h2&gt;

&lt;p&gt;Both Network ACLs and Security Groups act as firewalls, and are typically used together rather than as alternatives. You can think of them as different security layers protecting your resources.&lt;/p&gt;

&lt;p&gt;Ideally, you should configure both of them appropriately to ensure that only expected traffic reaches your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Security Groups
&lt;/h3&gt;

&lt;p&gt;Security Groups should be your primary mechanism for controlling traffic in AWS and you want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-grained control at the resource level (EC2, ALB, RDS, etc.)&lt;/li&gt;
&lt;li&gt;Simple, intuitive rules that follow the application logic&lt;/li&gt;
&lt;li&gt;Automatic handling of return traffic (stateful behavior)&lt;/li&gt;
&lt;li&gt;Micro-segmentation between tiers (web, application, database)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, most application level access decisions, such as which services can talk to each other, are enforced using Security Groups.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Network ACLs
&lt;/h3&gt;

&lt;p&gt;Use Network ACLs when you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A broad security boundary at the subnet level&lt;/li&gt;
&lt;li&gt;An additional layer of protection to block or restrict traffic&lt;/li&gt;
&lt;li&gt;Explicit deny rules (which Security Groups do not support)&lt;/li&gt;
&lt;li&gt;Guardrails that apply to all resources within a subnet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NACLs are often used as a secondary control to enforce high-level policies, such as blocking known malicious IP ranges or preventing unintended exposure.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>firewall</category>
      <category>network</category>
    </item>
    <item>
      <title>AWS Shared Responsibility Model Explained</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Tue, 30 Dec 2025 10:42:08 +0000</pubDate>
      <link>https://dev.to/nicolasv/aws-shared-responsibility-model-explained-54a6</link>
      <guid>https://dev.to/nicolasv/aws-shared-responsibility-model-explained-54a6</guid>
      <description>&lt;p&gt;One of the most dangerous assumptions when starting to work with cloud environments is believing that workloads are secure by default. After all, if you are using the cloud, the provider should take care of security, right?&lt;/p&gt;

&lt;p&gt;Not exactly.&lt;/p&gt;

&lt;p&gt;Cloud providers are responsible for security, but only to a certain extent.&lt;/p&gt;

&lt;p&gt;Imagine checking into a hotel and booking a room. The hotel is responsible for maintaining the building, hiring security staff, and monitoring cameras to secure entrances and hallways. It provides locks on the doors, installs smoke detectors and sprinklers, ensures that electricity and water work, and even offers a safe in each room for guests’ valuables.&lt;/p&gt;

&lt;p&gt;In other words, the hotel is responsible for keeping the hotel itself secure.&lt;/p&gt;

&lt;p&gt;You, the guest, are still responsible for locking your room when you leave. You decide who is allowed to enter, and you choose whether or not to use the safe to protect your valuables.&lt;/p&gt;

&lt;p&gt;The hotel gives you a lock, but you must actually use it.&lt;/p&gt;

&lt;p&gt;The same concept applies in the cloud. Cloud providers ensure that the underlying cloud infrastructure is secure, but you are responsible for securing your workloads and protecting your environment.&lt;/p&gt;

&lt;p&gt;This division of responsibility, in the context of AWS, is known as the Shared Responsibility Model, and it is exactly what we are going to explore in this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the AWS Shared Responsibility Model?
&lt;/h3&gt;

&lt;p&gt;The AWS Shared Responsibility Model defines how security responsibilities are divided between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS (the cloud provider) is responsible for &lt;strong&gt;security of the cloud&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You (the customer) are responsible for &lt;strong&gt;security in the cloud&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This concept is best illustrated by [AWS] itself:(&lt;a href="https://aws.amazon.com/es/compliance/shared-responsibility-model/" rel="noopener noreferrer"&gt;https://aws.amazon.com/es/compliance/shared-responsibility-model/&lt;/a&gt;)&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw8fblsdnlt1jznuf7qru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw8fblsdnlt1jznuf7qru.png" alt="AWS Shared Responsibility Model" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Responsibility
&lt;/h3&gt;

&lt;p&gt;AWS is responsible for protecting the infrastructure that runs all AWS services. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physical data centers (buildings, locks, guards, cameras)&lt;/li&gt;
&lt;li&gt;Hardware (servers, storage devices, networking equipment)&lt;/li&gt;
&lt;li&gt;Global infrastructure (regions and availability zones)&lt;/li&gt;
&lt;li&gt;Core services infrastructure (compute, storage, networking foundations)&lt;/li&gt;
&lt;li&gt;Hypervisors and underlying virtualization layers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if a physical server in an AWS data center fails or is physically tampered with, AWS is responsible for detecting and resolving that issue. Customers never manage or even see this hardware, and as such, they don't need to worry about: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data center access control&lt;/li&gt;
&lt;li&gt;Physical server patching&lt;/li&gt;
&lt;li&gt;Power, cooling, or hardware lifecycle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS handles all of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer responsiblity
&lt;/h3&gt;

&lt;p&gt;As a customer, you are responsible for how you configure and use AWS services, which includes: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM users, roles, and permissions&lt;/li&gt;
&lt;li&gt;Operating systems (for EC2)&lt;/li&gt;
&lt;li&gt;Network configurations (VPCs, security groups, NACLs)&lt;/li&gt;
&lt;li&gt;Data encryption and key management&lt;/li&gt;
&lt;li&gt;Application security&lt;/li&gt;
&lt;li&gt;Logging and monitoring&lt;/li&gt;
&lt;li&gt;Compliance with your own regulatory requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at an example to better understand this. &lt;/p&gt;

&lt;p&gt;Imagine your marketing team stores images of an unreleased product in a private S3 bucket. The images are protected from the outside world because the bucket has strict access controls. However, a mistake occurs and the S3 bucket is made public temporarily, but long enough for someone to access and download the images.&lt;/p&gt;

&lt;p&gt;Even though the bucket is hosted on AWS infrastructure, this incident is your responsibility, because you control and manage the security configuration of that bucket. &lt;/p&gt;

&lt;h3&gt;
  
  
  Real Examples
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Amazon EC2 (Virtual Servers)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With EC2, you have significant control and responsibility.&lt;/p&gt;

&lt;p&gt;AWS handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physical servers&lt;/li&gt;
&lt;li&gt;Hypervisor&lt;/li&gt;
&lt;li&gt;Data center security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OS patching (Linux/Windows updates)&lt;/li&gt;
&lt;li&gt;Firewall rules (security groups)&lt;/li&gt;
&lt;li&gt;SSH/RDP access&lt;/li&gt;
&lt;li&gt;Installed software vulnerabilities&lt;/li&gt;
&lt;li&gt;Application-level security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common mistake:&lt;br&gt;
Assuming AWS patches your EC2 operating system automatically. AWS does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon S3 (Object Storage)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;S3 is fully managed, but configuration is your responsibility.&lt;/p&gt;

&lt;p&gt;AWS handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage infrastructure&lt;/li&gt;
&lt;li&gt;Durability and availability&lt;/li&gt;
&lt;li&gt;Physical protection of disks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bucket access policies&lt;/li&gt;
&lt;li&gt;Public vs private access&lt;/li&gt;
&lt;li&gt;Encryption settings&lt;/li&gt;
&lt;li&gt;Who can read or write data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common mistake:&lt;br&gt;
Leaving buckets publicly accessible because “AWS would block that if it was unsafe.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Lambda (Serverless)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As services become more managed, your responsibilities decrease, but never disappear.&lt;/p&gt;

&lt;p&gt;AWS handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Servers and OS&lt;/li&gt;
&lt;li&gt;Runtime environment&lt;/li&gt;
&lt;li&gt;Scaling and availability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM permissions for the function&lt;/li&gt;
&lt;li&gt;Secure handling of secrets&lt;/li&gt;
&lt;li&gt;Application logic&lt;/li&gt;
&lt;li&gt;Data access controls&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why This Model Matters for Security
&lt;/h3&gt;

&lt;p&gt;There is a common misconception that security incidents in AWS are caused primarily by sophisticated attacks, but that could not be farther from the truth.&lt;/p&gt;

&lt;p&gt;In reality, most incidents in AWS stem from customer-side errors, such as misconfigurations, weak identity controls, and insufficient monitoring. This is consistently highlighted in industry research, including &lt;a href="https://www.tenable.com/press-releases/tenable-research-finds-pervasive-cloud-misconfigurations-exposing-critical-data-and-secrets" rel="noopener noreferrer"&gt;Tenable 2025 Cloud Security Risk Report&lt;/a&gt; and &lt;a href="https://cloudsecurityalliance.org/artifacts/top-threats-to-cloud-computing-2024" rel="noopener noreferrer"&gt;Top Threats to Cloud Computing 2024&lt;/a&gt; report published by the Cloud Security Alliance (CSA)&lt;/p&gt;

&lt;p&gt;Because of this, it is critical to understand what falls under your responsibility so you can apply the appropriate security controls and guardrails to prevent issues such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public data exposure&lt;/li&gt;
&lt;li&gt;Overly permissive IAM roles&lt;/li&gt;
&lt;li&gt;Data tampering&lt;/li&gt;
&lt;li&gt;Unpatched systems&lt;/li&gt;
&lt;li&gt;Missing logs and alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;AWS provides a highly secure cloud platform, but security is a shared effort. AWS secures the foundation; you secure everything built on top of it.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cloud</category>
      <category>aws</category>
    </item>
    <item>
      <title>WhatsApp Ghost Pairing: A Silent Abuse of Linked Devices</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Tue, 23 Dec 2025 16:00:00 +0000</pubDate>
      <link>https://dev.to/nicolasv/whatsapp-ghost-pairing-a-silent-abuse-of-linked-devices-4jb7</link>
      <guid>https://dev.to/nicolasv/whatsapp-ghost-pairing-a-silent-abuse-of-linked-devices-4jb7</guid>
      <description>&lt;p&gt;In the previous &lt;a href="https://dev.to/ciberconscientes/how-to-tell-if-your-whatsapp-account-is-hacked-and-how-to-protect-yourself-2d2l"&gt;blog&lt;/a&gt;, we discussed how to detect if your WhatsApp account has been compromised and the safeguards you can adopt to protect your account against common cyber threats.&lt;/p&gt;

&lt;p&gt;Today, I’m going to show you a specific attack that aims to monitor and spy on your conversations without your awareness. This technique is called &lt;em&gt;Ghost Pairing&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Ghost Pairing?
&lt;/h2&gt;

&lt;p&gt;Ghost Pairing is an attack vector in which an attacker secretly links their own device to your WhatsApp account using the official Linked Devices feature. Their intention is not to fully take over your account but to let you continue using it normally while they:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read all messages you send and receive&lt;/li&gt;
&lt;li&gt;Access your photos, videos, and documents&lt;/li&gt;
&lt;li&gt;Impersonate you by sending messages to your contacts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds scary, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  How Ghost Pairing Works
&lt;/h2&gt;

&lt;p&gt;WhatsApp allows one account to be linked to up to &lt;a href="https://faq.whatsapp.com/378279804439436/?helpref=uf_share" rel="noopener noreferrer"&gt;four devices&lt;/a&gt;. Ghost pairing abuses this design. The attack does not target WhatsApp’s encryption or internal security. Instead, it targets the user, exploiting human behavior to grant unauthorized access.&lt;/p&gt;

&lt;p&gt;There are two common scenarios in which this attack can occur:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Physical Access&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you leave your phone unlocked and unattended, an attacker can take advantage of that moment to link your WhatsApp account to their own device.&lt;/p&gt;

&lt;p&gt;No verification SMS is sent&lt;/p&gt;

&lt;p&gt;You are not logged out of your account&lt;/p&gt;

&lt;p&gt;The attacker can dismiss the push notification WhatsApp sends for new linked devices&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remote access&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;In this scenario, the attacker gains access without physically handling your phone. This usually involves social engineering techniques designed to trick you into revealing information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The attacker convinces you to share the WhatsApp verification code you received via SMS. (Reminder: never share this code with anyone.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The attacker impersonates one of your contacts and attempts to trick you into providing the verification code or visiting a malicious link&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You visit a seemingly innocent website that claims you must verify your phone to view content, but in the background it collects your verification code&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why It’s Dangerous
&lt;/h2&gt;

&lt;p&gt;Ghost pairing is especially risky because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No obvious takeover – your account still works&lt;/li&gt;
&lt;li&gt;Real-time spying – messages sync instantly&lt;/li&gt;
&lt;li&gt;Persistent access – stays active until manually removed&lt;/li&gt;
&lt;li&gt;Perfect for scams – attackers can impersonate you&lt;/li&gt;
&lt;li&gt;No advanced skills needed - attackers simply exploits an official WhatsApp feature&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Signs of Ghost Pairing
&lt;/h2&gt;

&lt;p&gt;Be alert for these warning signs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A linked device you don’t recognize under Settings → Linked Devices&lt;/li&gt;
&lt;li&gt;Messages marked as “read” when you didn’t open them&lt;/li&gt;
&lt;li&gt;Contacts receiving messages you don’t remember sending&lt;/li&gt;
&lt;li&gt;Unusual activity times in Last Seen&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Protect Yourself
&lt;/h2&gt;

&lt;p&gt;The good news is ghost pairing is easy to prevent if you follow basic security practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lock your phone (PIN, biometrics, auto-lock) and don't leave it unattanded&lt;/li&gt;
&lt;li&gt;Regularly check Linked Devices and remove any unfamiliar ones&lt;/li&gt;
&lt;li&gt;Enable notifications for new linked devices&lt;/li&gt;
&lt;li&gt;Enable two-step verification (2FA) for your WhatsApp account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you found this article helpful, share it with your friends and family to help them stay informed and protected. Remember: security is everyone’s responsibility.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>privacy</category>
    </item>
    <item>
      <title>How to Tell If Your WhatsApp Account Is Hacked — and How to Protect Yourself</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Mon, 22 Dec 2025 13:50:08 +0000</pubDate>
      <link>https://dev.to/nicolasv/how-to-tell-if-your-whatsapp-account-is-hacked-and-how-to-protect-yourself-2d2l</link>
      <guid>https://dev.to/nicolasv/how-to-tell-if-your-whatsapp-account-is-hacked-and-how-to-protect-yourself-2d2l</guid>
      <description>&lt;p&gt;WhatsApp is the most widely used instant messaging platform on the planet, with over 3.5 billion active accounts in &lt;a href="https://arxiv.org/abs/2511.20252" rel="noopener noreferrer"&gt;2025&lt;/a&gt;. For many people, it has become the de facto channel for personal communication, and small businesses and even larger corporations have also adopted it as an easy way to communicate with customers.&lt;/p&gt;

&lt;p&gt;WhatsApp’s global reach and its importance in people’s daily lives make it a high-value target for cybercriminals. For this reason, it’s essential to understand how to protect yourself and recognize the warning signs that your account may be at risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recognizing the Signs Your WhatsApp Account May Be Compromised
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Unexpected Activity&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Messages are marked as read without your involvement.&lt;/li&gt;
&lt;li&gt;Messages or media you didn’t send appear in your chats.&lt;/li&gt;
&lt;li&gt;New contacts or group chats appear that you don’t recognize or didn’t add.&lt;/li&gt;
&lt;li&gt;Your &lt;em&gt;Last Seen&lt;/em&gt; status shows activity at times when you weren’t using the app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Unknown Linked Devices&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Linked Devices is a feature that allows you to securely access your WhatsApp account from multiple devices (such as WhatsApp Web, desktop apps, or another phone). These linked devices have full access to your chats and media, meaning they can read your messages, view your photos, and send messages to your contacts.&lt;/p&gt;

&lt;p&gt;For this reason, it’s important to periodically review your list of linked devices and immediately remove any that you don’t recognize.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Changes to Profile Information&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your profile photo, status, or display name changes without your consent, it’s a strong indication that someone else may have access to your account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You Receive Unexpected Verification Codes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you receive an SMS containing a WhatsApp verification code that you did not request, it likely means someone is attempting to register your phone number on another device. &lt;/p&gt;

&lt;p&gt;DO NOT SHARE THIS CODE WITH ANYONE. Sharing it would give them full access to your account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You Can’t Log In or Have Been Logged Out&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If WhatsApp reports that your number is “no longer registered” on your device, or if you are suddenly logged out and unable to sign back in using your phone number, your account may have been compromised.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do If You Suspect Your Account Is Comrpomised
&lt;/h2&gt;

&lt;p&gt;Once you identify suspicious activity, take the following steps immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Regain Control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open WhatsApp and sign in using your phone number.&lt;/p&gt;

&lt;p&gt;Enter the six-digit verification code sent via SMS. This will automatically log out any existing sessions on devices controlled by an attacker.&lt;/p&gt;

&lt;p&gt;If you’re unable to receive the six-digit code because the attacker has enabled or changed two-step verification, WhatsApp typically enforces a waiting period (for example, seven days) before you can register the number again without the two-step verification PIN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Log Out Unknown Devices&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you still have access to your account, go to Settings → Linked Devices.&lt;/p&gt;

&lt;p&gt;Review the list and log out of any devices you don’t recognize.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Alert Your Contacts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Notify friends, family, and colleagues that your account may have been compromised so they do not fall for scams sent from your profile. &lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Tips to Protect Your WhatsApp Account
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Enable Two-Step Verification (2FA)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even though two-step verification is an optional security feature, it's highly recommended enabling it as it significantly strengthens your account by adding a six-digit PIN required to register your number on a new device. &lt;/p&gt;

&lt;p&gt;It is the single most effective defensive measure you can enable.&lt;/p&gt;

&lt;p&gt;To enable it, go to:&lt;/p&gt;

&lt;p&gt;Settings → Account → Two-step verification → Enable.&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enter a six-digit PIN&lt;/li&gt;
&lt;li&gt;Add an email address to help you recover your PIN if you forget it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Never Share Your Verification Codes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WhatsApp will never ask you for your SMS verification code or your two-step verification PIN. Sharing either of these, even with someone claiming to be WhatsApp support, almost always results in account takeover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor Linked Devices Regularly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Periodically review the list of devices linked to your WhatsApp account and log out of any sessions you don’t recognize.&lt;/p&gt;

&lt;p&gt;Go to Settings → Linked Devices to see the full list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Strong Device Security&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Protect your phone with a secure PIN, biometric lock, or strong password. This reduces the risk of someone accessing your WhatsApp account if your device is lost or stolen.&lt;/p&gt;

&lt;p&gt;Also, make sure your phone is locked when you leave it unattended. Anyone with access to an unlocked phone could link their own device to your WhatsApp account and monitor your conversations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be Wary of Links, Files, and Phishing Attempts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Attackers often send malicious links or files designed to install malware or trick you into revealing sensitive information. Avoid clicking unknown links, opening suspicious attachments, or installing apps from unofficial sources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update WhatsApp and Your Operating System&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Security updates frequently patch vulnerabilities that attackers exploit. Enable automatic updates for WhatsApp and your device’s operating system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable Push Notifications on your phone&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;WhatsApp sends a notification whenever a new device is linked to your account. Keeping notifications enabled allows you to quickly detect and remove unauthorized devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid Unofficial Apps and Tools&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Linked Devices is a feature officially provided by WhatsApp at no cost. Using unofficial or third-party apps to link your account can expose your messages and credentials, putting your account at serious risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;WhatsApp is widely regarded as a secure platform thanks to its end-to-end encryption. However, account takeovers remain common because attackers typically exploit human behavior through social engineering rather than breaking cryptographic protections.&lt;/p&gt;

&lt;p&gt;By recognizing the warning signs of compromise and adopting strong account security practices, especially enabling two-step verification and carefully handling verification codes and links, you can significantly reduce your risk and better protect your communications.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>When a seemingly innoffensive conversation with AI turns malicious</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Tue, 16 Dec 2025 21:15:47 +0000</pubDate>
      <link>https://dev.to/nicolasv/when-a-seemingly-innoffensive-conversation-with-ai-turns-malicious-2j6p</link>
      <guid>https://dev.to/nicolasv/when-a-seemingly-innoffensive-conversation-with-ai-turns-malicious-2j6p</guid>
      <description>&lt;p&gt;AI has changed the way we interact with technology in many ways, from simple tasks such as generating text and helping with homework to more technical ones like creating web pages and assisting developers with code reviews. &lt;/p&gt;

&lt;p&gt;One of the most significant changes has been the way we search for information. It is now common to default to an LLM such as ChatGPT, Gemini, Grok, or Claude to ask about almost anything we can think of: planning trips, searching for historical facts, explaining complex documentation, or helping us fix problems by providing step-by-step guides.&lt;/p&gt;

&lt;p&gt;It is in this last area where cybercriminals have found a way to exploit the trust users place in these platforms, creating an attack vector that tricks users into downloading and installing malware designed to steal credentials, as reported earlier this month by the cybersecurity company &lt;a href="https://www.huntress.com/blog/amos-stealer-chatgpt-grok-ai-trust" rel="noopener noreferrer"&gt;Huntress&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What category does this attack fit into?
&lt;/h3&gt;

&lt;p&gt;Social engineering through the “impersonation” of a legitimate LLM conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does it work?
&lt;/h3&gt;

&lt;p&gt;I will leave out the technical details for the sake of simplicity and refer you to the original article. In a nutshell, the attack abuses the shareable conversation feature available in&lt;br&gt;
&lt;a href="https://grok.com/faq/share-links" rel="noopener noreferrer"&gt;Grok&lt;/a&gt; and &lt;a href="https://help.openai.com/en/articles/7925741-chatgpt-shared-links-faq" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This feature allows users to generate a unique URL that can be easily shared on social media, instant messaging platforms, or public forums, and anyone with the link can access the conversation.&lt;/p&gt;

&lt;p&gt;These conversations can be indexed by search engines and may rank highly in search results, causing them to appear at the top of the page and point to a seemingly legitimate conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does it work?
&lt;/h3&gt;

&lt;p&gt;The conversations resemble step-by-step guides on how to perform common operations on macOS devices, such as clearing storage.&lt;/p&gt;

&lt;p&gt;Users access the link through a browser and see a real conversation with an LLM—real in the sense that it is the actual UI and platform used when interacting with Grok or ChatGPT. &lt;/p&gt;

&lt;p&gt;This creates an element of trust, as users are familiar with these platforms. However, without realizing it, the steps include malicious terminal commands that open the user’s computer to attackers.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to protect yourself
&lt;/h3&gt;

&lt;p&gt;AI is not flawless. We all know that LLMs can hallucinate and provide made-up information in an attempt to be helpful. In that sense, this scenario is not entirely different. If an LLM were to ask for your Social Security number, your bank password, or guide you through making a bank transfer to an account number you don't know, you would not do it, right?&lt;/p&gt;

&lt;p&gt;Here, instead of asking for sensitive information directly, the model instructs users to run commands in their device’s terminal without clearly explaining what those commands do - and those commands can be highly destructive. &lt;/p&gt;

&lt;h3&gt;
  
  
  The recommended precautions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Never provide sensitive information.&lt;/li&gt;
&lt;li&gt;Never execute terminal commands unless you fully understand what they do, even if they appear to come from a trusted source.&lt;/li&gt;
&lt;li&gt;Follow good password hygiene to keep your accounts secure, such as never reusing passwords and using a password manager to generate strong, unique credentials.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Beware the Forgotten Tab: Understanding and Preventing Tabsnabbing</title>
      <dc:creator>Nicolás</dc:creator>
      <pubDate>Sun, 09 Nov 2025 09:18:56 +0000</pubDate>
      <link>https://dev.to/nicolasv/beware-the-forgotten-tab-understanding-and-preventing-tabsnabbing-21jf</link>
      <guid>https://dev.to/nicolasv/beware-the-forgotten-tab-understanding-and-preventing-tabsnabbing-21jf</guid>
      <description>&lt;p&gt;If you’re anything like me, your curiosity tends to get the better of you. You start reading a blog post, click on a related article (or two… or ten), and before you know it, your browser looks like a chaotic battlefield of open tabs.&lt;/p&gt;

&lt;p&gt;This kind of tab-hopping often comes from our multitasking instincts — reading a bit here, jumping there, chasing the next interesting thing that catches our eye. Before long, you’ve gone so far down the rabbit hole that you’re learning how nut prices in Asia affect global trade, even though all you wanted to know in the first place was whether it’s going to be sunny tomorrow.&lt;/p&gt;

&lt;p&gt;Now, having too many tabs open isn’t really a problem — at least, not if your computer can handle it. But what if I told you that, somewhere in that ocean of forgotten tabs, something darker might be waiting? Something that could turn against you the moment you let your guard down?&lt;/p&gt;

&lt;p&gt;Welcome, my friend, to Tabsnabbing — a sneaky phishing technique that exploits your trust in your own open tabs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Tabsnabbing?
&lt;/h2&gt;

&lt;p&gt;Tabsnabbing is a type of phishing attack that leverages inactive browser tabs. When a user navigates away from a page but leaves its tab open, a malicious site can detect that state and replace its content with a realistic-looking login page (for example Gmail, GitHub, or an online bank). If the user returns and enters their credentials, the attacker captures them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabsnabbing Types
&lt;/h2&gt;

&lt;p&gt;There exist two types of tabsnabbing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Passive (the most common)&lt;br&gt;
This is the one that changes the original content of the website, when the tab is inactive, for a login page. When you return to it, it gives the impression that the session expired, so the user is more likely to enter their credentials. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reverse &lt;br&gt;
This one occurs when you click on a link that opens a new browser tab. Unknowingly, this action triggers a process that automatically change the content of previous page. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why It Works So Well
&lt;/h2&gt;

&lt;p&gt;Tabsnabbing preys on two simple things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Trust &lt;br&gt;
You think the tab is still the site you opened earlier (e.g., “That’s my Gmail tab, right?”).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Distraction &lt;br&gt;
We multitask. We leave tabs open for hours. Sometimes days.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The combination means that when you return to a tab and see a login page, your brain fills in the blanks: “I must’ve been logged out.” And that’s when the attacker wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Risks We Face
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Identity Theft &lt;br&gt;
Attackers can pretend they are you if they manage to your credentials for email account, your social accounts (TikTok, Instagram, Facebook...)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Financial Loss&lt;br&gt;
Capturing your bank credentials can lead to attackers transfering money out your account, using your card details to purchase goods&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Access to sensitive information &lt;br&gt;
Company's proprietary data if your work credentials are captured, your medical records...&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Protect Yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Close tabs you are not using&lt;/li&gt;
&lt;li&gt;Verify the URL before entering credentials. If in doubt, close the tab and navigate to the login page.&lt;/li&gt;
&lt;li&gt;Don't reuse passwords&lt;/li&gt;
&lt;li&gt;Enable MFA whenever possible&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Tabsnabbing is one of those web security gotchas that’s easy to underestimate. It doesn’t rely on zero-days or fancy exploits — just our everyday browsing habits.&lt;/p&gt;

&lt;p&gt;It’s a good reminder that security is as much about psychology as it is about code.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>browser</category>
    </item>
  </channel>
</rss>
