At the core of AWS Security we have IAM. In reality IAM is a mandatory service for anyone intending to use AWS.
For your compute services, you should secure your Key Pairs - as they are a very important piece of the security. You should also use only key pairs rather than regular passwords in your Linux instances.
Security groups are also something to use. You should use the principle of least privilege to configure the rules. A good way to grant permissions only to the right resources, is by configuring the inbound rules to accept only requests coming from resources in another security group.
For example you might have a web application with a web server running on an EC2 instance, and an RDS instance. In the security group of the web server, you should only open the ports that the application needs, in this case port 80. However, in the security group of your RDS instance, you can choose to accept only connections coming from resources in the web-server security group, by simply entering the security group ID where you would normally enter the IP range of your web instances.
It is your responsibility to manage access to your resources. So, when, for instance, you’re setting up S3 access, you should work with tools like object ACLs and bucket policies, always keeping in mind the principle of least privilege.
For networking, you really need to use ACLs which, because they work at the subnet level, add an additional layer of security above and beyond security groups that work in the instance level. By using network ACLs associated on top of security groups, you can block unwanted access that you might forgot to block in the security groups. You could, for example, block all the traffic coming from a suspicious IP address to your public subnet, because unlike security groups, you can define Deny rules in your network ACLs.
To provide even more protection for your resources, you should segment your VPC into public and private subnets. The resources in the private subnet cannot be accessed directly from the internet.
Sometimes you will have resources in a private subnet that you need to access once in a while. To solve this problem you can deploy something called a bastion host.
A Bastion host is an instance that you place in a public subnet and give permissions to access your resources in the private subnets. As a best practice you should allow access to it only for connections coming from a secure IP address. Also you should only start the bastion host instance when it’s actually needed, and stop it when you’re done.
Although AWS is very secure, you can choose to add another layer of security to your data by encrypting it. Services like S3 and RDS come with some encryption features out of the box.