13 Security Notice for Web Developers
Developing secure and reliable cloud-based web applications is not an easy task. Even – very difficult. If you think that this is not so, then either you are a representative of an unknown science of the higher form of life, or – wait for a hefty roasted cock.
If you are inspired by the idea of creating a minimum viable product and are confident that you can develop something useful and safe in a month, think twice before launching such a “product”, but rather – just a prototype.
After you look at the following checklist of tasks that you need to solve to ensure the security of your web project, you will for sure see for yourself that much of what’s in it is not included in your development. What to do? At the very least, be honest with potential users and tell them that your project is still under development, that you are offering them to familiarize yourself with a prototype in which a full-fledged security system has not yet been implemented.
- If possible, use encryption to store information that identifies the user, and to store sensitive data, such as access tokens, e-mail addresses, or payment requisites (this approach, in particular, will limit requests to the database to the exact match-matching level).
- If your DBMS supports economical encryption of stored data, enable it to protect the information on the disks. In addition, make sure that all backup copies of the databases are also encrypted.
- Use user accounts to access databases with minimal privileges. Do not use the superuser account and check the system for unused accounts and accounts with too weak passwords.
- Store and transfer account data, system access tokens and other sensitive information using a key store designed for such scenarios. Do not store such data by hard-coding them in the application code.
- Protect the system from attacks by SQL injection, using only prepared SQL queries. For example, if your project is designed for Node.js, do not use the MySQL SNMP library, refer to the mysql2 library that supports prepared queries.
- Ensure that the system components are checked for vulnerabilities before each release. It’s all about libraries, packages, and the work environment. Ideally, the checks should be automated within the CI-CD process.
- Ensure the safety of the computers of developers, taking this issue with the same attention with which you treat the safety of production servers. Development should be carried out on protected, isolated from potentially dangerous external environment machines.
- Make sure that all passwords are hashed using a suitable crypto algorithm like a crypt. Do not use the hash functions of hashing, properly initialize the cryptosystems using qualitative random data.
- Use proven components to organize logon, restore a forgotten password and reset the password. Do not reinvent the wheel. The fact is that with independent development it will be very difficult for you to ensure the correct operation of such mechanisms in all possible scenarios.
- Implement simple but non-threatening password requirements that encourage users to create long passwords that consist of random character sets.
- Apply, for all service providers, multifactor authentication.
Protection from DOS-attacks
- Make sure that DOS-attacks on your API will not affect the site’s performance. At a minimum, use the query frequency limit in the slowest API paths and in those parts of the project that are associated with authentication. For example, it can be about the modules of logging into the system and the subsystems for creating access tokens. Consider using CAPTCHA in those parts of the project that are accessible from the outside world to protect the server subsystems from DOS attacks.
- Set reasonable limits on the size and structure of the data that users can send to the system, as well as the requests that they perform.
- Consider using the system to mitigate the effects of distributed DOS attacks, for example, using a global caching proxy like CloudFlare. During the attack, this will help your project survive, and at the usual time will reduce the load on the server and speed up the loading of the site.
- Use TLS for the entire site, not just to protect the authorization system. Never use TLS only to protect the authorization form. In the transitional period, use the HTTP-header strict-transport-security to force the use of the HTTPS protocol.
- Cookies must have the httpOnly attribute, they must be protected, and path and domain parameters must be present among their attributes.
- Use a content protection policy (CSP, Content Security Policy), not permitting unsafe-inline and unsafe-eval. Such a policy is not easy to adjust, but it is worth the effort and time. Use the Subresource Integrity mechanism for CDN content.
- Use the HTTP headers X-Frame-Option and X-XSS-Protection in the responses of clients.
- Use the HSTS mechanism to provide access to the system only using TLS. Do not trust the client systems, ensure the use of HTTPS on the server side.
- Use CSRF tokens in all forms, use the new SameSite cookie attribute to protect against CSRF attacks. It is supported by modern versions of browsers.
- Verify that the public API does not have resources that can be detected by brute force.
- Users must be fully authenticated and appropriately authorized before they can use the API.
Checking and converting data
- Check, on the client side, the data entered by users. This will allow them to get a quick response to their actions. However, never trust this test. Always check and convert to the format you need, what the user entered, before displaying on the pages of the site.
- Check everything that comes from the user, using white lists on the server. Never insert unencoded data entered by the user into HTTP requests and replies. Do not under any circumstances use potentially dangerous data entered by the user in SQL queries or other server logic.
- Verify that all cloud services have the minimum number of ports open. While securing security through hiding information is not protection, using non-standard ports will make life harder for those who try to attack your system.
- Keep databases and services in virtual private clouds (VPCs, Virtual Private Cloud) that are not accessible from the outside. Be very careful when configuring AWS security groups and configuring peering between VPCs, since errors can cause internal services to be visible from the outside.
- Isolate logical services in different VPCs and configure peering between them to ensure inter-service interaction.
- Make sure that all services accept data only from the minimum set of IP addresses.
- Limit outgoing traffic to IP addresses and ports to minimize the possibility of targeted cyber attacks and bot attacks.
- Always use the AWS IAM roles, not the superuser account.
- Use the minimum access privileges for those who service the system, and for developers.
- Regularly, according to the schedule, perform the rotation of passwords and access keys.
- Make sure that you can perform project updates without downtime. Implement a fully automatic system that allows you to quickly update the software.
- Create the entire infrastructure using tools like Terraform, and not through the cloud services management console. Strive to implement the “infrastructure as code” approach. This will allow you, if necessary, to recreate everything literally with a single click on the key. Do not allow the creation of cloud resources manually. Perform configuration auditing.
- Use centralized logging for all services. The logging system must be built so that you never need to connect to the system via SSH only in order to view or download logs.
- Do not connect to services on SSH, except for single cases of their diagnosis. Regular use of SSH usually means that important tasks are not automated.
- Do not keep port 22 permanently open in any AWS service group. If you absolutely need SSH access, use only public key authentication, not logins and passwords.
- Give preference to immune hosts, rather than long-lived servers that you patch and update. Here is a useful material on this topic.
- Use an intrusion detection system to minimize damage from targeted cyber attacks.
- Disable unused services and servers. The most secure server is the one from which the power cord was pulled.
Testing the system
- Perform an audit of the architecture and implementation of the system.
- Conduct a system test for penetration – hack yourself. However, it is useful to attract outside experts to such tests.
- Train staff (especially beginners), talking about cyber-risks and about the methods of social engineering used to hack systems.
Plan of action in a non-emergency situation
- Create a threat model that describes what you are protecting from. It should be listed, with the assignment of priorities, possible threats, and participants in the attacks.
- Prepare a clear plan of action in an abnormal situation. One day you will need it.
The checklist of tasks considered in this material does not pretend to be complete. The thing is that every web project is unique and only its creators know exactly what it is threatening, and how to deal with these threats. However, different projects have many common features. Therefore, we believe, everyone will be able to adjust the list presented here for their project.
What you read is based on more than fourteen years of experience of the author of the material in the field of developing secure Web applications. This experience has not been easy for him, and we are confident that his findings will make life easier for those who are concerned about the safety of their web solutions.