I think application security is very much a friend of the cloud security because at all conditions they move in lockstep to keep things safer in the production world. I believe that even minor flaws in the web applications or misconfigurations in the cloud can have big impact especially when bad guys find ways to combine or chain them together. Despite this not being a solid piece of work or an interesting story, I couldn’t resist sharing it.
It’s not even arguable sometimes the way an application is architected securely is what saves cloud assets from being hacked, even when there are misconfigurations in the cloud. Other times, it’s the cloud configurations that end up saving the entire application logic from being compromised. But architecting a secure application and blending in the security angle is a bit like making a famous cup of tea: it’s sweet in taste, but most people hesitate to brew it themselves because the ingredients like secure development practices, thorough security testing, well crafted security policies, and industry cloud security frameworks can seem bit intimidating.
Speaking of the cloud, which is what we are really interested in, I came across two different AWS S3 POST Policies within the same application, each tied to a different feature. It didn’t take me long to spot something interesting. You might also want to spend a little time looking through both policies carefully. Sometimes, it just takes swiping left and right across the divider to spot what’s off.


The Policy I is more restrictive and efficient. It includes conditions like $key
to define the exact upload path, content-length
to set the maximum file size, user redirection, and other important controls. At first glance, everything looks good. However, unless you also look at Policy II, it doesn’t really matter how restrictive the first policy is, because it completely misses the application’s security logic.
If you ask me why, it all comes down to a single condition in Policy II, $key
is set to just uploads/
.
What does it actually mean? In the first policy, the $key
match condition was something like uploads/user/{numbers}/{uuid}
. This means a user is only allowed to upload files to their specific, designated path. But Policy II ignores that structure and simply allows anyone to upload anything to any path under uploads/
. That one change undermines all the restrictions from the first policy and basically opens up the bucket to potential misuse.
This misconfiguration would allow anyone to overwrite image files or anything else inside the S3 bucket. To put it more bluntly, a malicious user could replace images or upload whatever they want across the entire application, potentially leading to data loss or serious misuse. The only thing that slightly mitigates this risk is s3:ListBucket
is not enabled, and the UUIDs used in the file paths are difficult to enumerate for specific users.
The image below shows the HTTP POST request being made. If you look at the parameter named “key,” you’ll see that its value can be changed from something specific like uploads/data/number/uuid/${filename}
to a more general path like uploads/${filename}
, or really anything after uploads/
, which could easily be abused.

How do we secure it?
To secure this, the value of the “key” parameter should be tightly restricted. Each user should only be able to upload files to a specific folder, such as uploads/userid/uuid/${filename}
, enforced through strict conditions in the S3 POST policy.
Generic patterns like uploads/
should be avoided, as they increase the risk of overwriting or unauthorized uploads. Public write permissions must never be granted to the bucket, and policies should be regularly reviewed to ensure that only intended users have upload access to the appropriate paths.
Periodic audits and testing of the configuration are important for identifying and closing any potential loopholes before they can be exploited. Security controls are only as strong as their weakest configuration. Always check for exceptions that may quietly open the door to attackers.