If you are designing, creating, testing your web/mobile application with security in mind, this Checklist of counter-measures can be a good starting point
- Use HTTPS everywhere.
- Store password hashes using
Bcrypt
(no salt necessary -Bcrypt
does it for you). - Destroy the session identifier after
logout
. - Destroy all active sessions on reset password (or offer to).
- Must have the
state
parameter in OAuth2. - No open redirects after successful login or in any other intermediate redirects.
- When parsing Signup/Login input, sanitize for javascript://, data://, CRLF characters.
- Set secure, httpOnly cookies.
- In Mobile
OTP
based mobile verification, do not send the OTP back in the response whengenerate OTP
orResend OTP
API is called. - Limit attempts to
Login
,Verify OTP
,Resend OTP
andgenerate OTP
APIs for a particular user. Have an exponential backoff set or/and something like a captcha based challenge. - Check for randomness of reset password token in the emailed link or SMS.
- Set an expiration on the reset password token for a reasonable period.
- Expire the reset token after it has been successfully used.
- Any resource access like,
my cart
,my history
should check the logged in user's ownership of the resource using session id. - Serially iterable resource id should be avoided. Use
/me/orders
instead of/user/37153/orders
. This acts as a sanity check in case you forgot to check for authorization token. -
Edit email/phone number
feature should be accompanied by a verification email to the owner of the account. - Any upload feature should sanitize the filename provided by the user. Also, for generally reasons apart from security, upload to something like S3 (and post-process using lambda) and not your own server capable of executing code.
-
Profile photo upload
feature should sanitize all theEXIF
tags also if not required. - For user ids and other ids, use RFC compliant
UUID
instead of integers. You can find an implementation for this for your language on Github. - JWT are awesome. Use them if required for your single page app/APIs.
- Use encryption for data identifying users and sensitive data like access tokens, email addresses or billing details.
- If your database supports low cost encryption at rest (like AWS Aurora), then enable that to secure data on disk. Make sure all backups are stored encrypted as well.
- Use minimal privilege for the database access user account. Don’t use the database root account.
- Store and distribute secrets using a key store designed for the purpose. Don’t hard code in your applications.
- Fully prevent SQL injection by only using SQL prepared statements. For example: if using NPM, don’t use npm-mysql, use npm-mysql2 which supports prepared statements.
-
salt
from payment gateways should not be hardcoded. -
secret
/auth token
from 3rd party SDK's should not be hardcoded. - API calls intended to be done
server to server
should not be done from the app. - In Android, all the granted permissions should be carefully evaluated.
- On iOS, store sensitive information (authentication tokens, API keys, etc.) in the system keychain. Do not store this kind of information in the user defaults.
- Certificate pinning is highly recommended.
-
Add
CSP header to mitigate XSS and data injection attacks. This is important. -
Add
CSRF header to prevent cross site request forgery. Also add SameSite attributes on cookies. -
Add
HSTS header to prevent SSL stripping attack. -
Add
your domain to the HSTS Preload List -
Add
X-Frame-Options to protect against Clickjacking. -
Add
X-XSS-Protection header to mitigate XSS attacks. - Update DNS records to add SPF record to mitigate spam and phishing attacks.
- Add subresource integrity checks if loading your JavaScript libraries from a third party CDN. For extra security, add the require-sri-for CSP-directive so you don't load resources that don't have an SRI sat.
- Use random CSRF tokens and expose business logic APIs as HTTP POST requests. Do not expose CSRF tokens over HTTP for example in an initial request upgrade phase.
- Do not use critical data or tokens in GET request parameters. Exposure of server logs or a machine/stack processing them would expose user data in turn.
-
Sanitize
all user inputs or any input parameters exposed to user to prevent XSS. - Always use parameterized queries to prevent SQL Injection.
- Sanitize user input if using it directly for functionalities like CSV import.
-
Sanitize
user input for special cases like robots.txt as profile names in case you are using a url pattern like coolcorp.io/username. - Do not hand code or build JSON by string concatenation ever, no matter how small the object is. Use your language defined libraries or framework.
- Sanitize inputs that take some sort of URLs to prevent SSRF.
- Sanitize Outputs before displaying to users.
- Make sure that DOS attacks on your APIs won’t cripple your site. At a minimum, have rate limiters on your slower API paths like login and token generation routines. Consider CAPTCHA on front-end APIs to protect back-end services against DOS.
- Enforce sanity limits on the size and structure of user submitted data and requests.
- Use Distributed Denial of Service (DDOS) mitigation via a global caching proxy service like CloudFlare. This can be turned on if you suffer a DDOS attack and otherwise function as your DNS lookup.
- If you are small and inexperienced, evaluate using AWS elasticbeanstalk or a PaaS to run your code.
- Use a decent provisioning script to create VMs in the cloud.
- Check for machines with unwanted publicly
open ports
. - Check for no/default passwords for
databases
especially MongoDB & Redis. - Use SSH to access your machines; do not setup a password, use SSH key-based authentication instead.
- Install updates timely to act upon zero day vulnerabilities like Heartbleed, Shellshock.
- Modify server config to use TLS 1.2 for HTTPS and disable all other schemes. (The tradeoff is good.)
- Do not leave the DEBUG mode on. In some frameworks, DEBUG mode can give access full-fledged REPL or shells or expose critical data in error messages stacktraces.
- Be prepared for bad actors & DDOS - use a hosting service that has DDOS mitigation.
- Set up monitoring for your systems, and log stuff (use New Relic or something like that).
- If developing for enterprise customers, adhere to compliance requirements. If AWS S3, consider using the feature to encrypt data. If using AWS EC2, consider using the feature to use encrypted volumes (even boot volumes can be encrypted now).
- Ensure all services have minimum ports open. While security through obscurity is no protection, using non-standard ports will make it a little bit harder for attackers.
- Host backend database and services on private VPCs that are not visible on any public network. Be very careful when configuring AWS security groups and peering VPCs which can inadvertently make services visible to the public.
- Isolate logical services in separate VPCs and peer VPCs to provide inter-service communication.
- Ensure all services only accept data from a minimal set of IP addresses.
- Restrict outgoing IP and port traffic to minimize APTs and “botification”.
- Always use AWS IAM roles and not root credentials.
- Use minimal access privilege for all ops and developer staff.
- Regularly rotate passwords and access keys according to a schedule.
CI & CD Audit your design and implementation with unit/integration tests coverage. Use a code review process and disregard self-approval. Ensure that all components of your services are statically scanned by AV software before pushing to production, including vendor libraries and other dependencies. Design a rollback solution for deployments.
- Set up an email (e.g. [email protected]) and a page for security researchers to report vulnerabilities.
- Depending on what you are making, limit access to your user databases.
- Be polite to bug reporters.
- Have your code review done by a fellow developer from a secure coding perspective. (More eyes)
- In case of a hack or data breach, check previous logs for data access, ask people to change passwords. You might require an audit by external agencies depending on where you are incorporated.
- Ensure you can do upgrades without downtime. Ensure you can quickly update software in a fully automated manner.
- Create all infrastructure using a tool such as Terraform, and not via the cloud console. Infrastructure should be defined as “code” and be able to be recreated at the push of a button. Have zero tolerance for any resource created in the cloud by hand — Terraform can then audit your configuration.
- Use centralized logging for all services. You should never need SSH to access or retrieve logs.
- Don’t SSH into services except for one-off diagnosis. Using SSH regularly, typically means you have not automated an important task.
- Don’t keep port 22 open on any AWS service groups on a permanent basis. Instead consider allowing only authorized IPs to SSH on the box.
- Create immutable hosts instead of long-lived servers that you patch and upgrade. (See Immutable Infrastructure Can Be More Secure).
- Use an Intrusion Detection System like SenseDeep or service to minimize APTs.
References