A while back one of the biggest leaks of personal information in history was made by hackers accessing the Commission on Elections database here in the Philippines. More than 50 million voter registration records, including information such as full names, date of birth, address, among others. A small percentage of the data leaked also included email addresses and even passport numbers. The hack exposed more than half of the country’s population to the possibility of social engineering and other exploits. It was certainly a national embarrassment, especially for those of us working in software development. But the truth of the matter is that securing publicly accessible sites against determined bad actors is difficult, very difficult. It’s something that needs to be taken into account on all levels of the system.
I don’t consider myself any kind of security specialist, but I think it’s something that software developers of all levels need to at least have a passing knowledge of, especially in this modern age where many tech companies store personal/private data for large numbers of users. I tend to classify the security practices and vulnerabilities under several levels:
- Physical / hardware layer - this means controlling the physical access of unauthorized personnel to hardware that contains private information. It would take serious levels of screw-ups for a system to be compromised in this manner, or perhaps a well-placed bribe. It is however, comparatively easy to detect any breaches in this layer. In any case, this is not really a concern for the software engineer.
- Server OS and local network level - this means that controlling the access to the actual OS running on the machine and the corresponding local network the server is on. Practically as bad (sometimes worse) than compromising the hardware layer, a compromise on this layer means the bad actor can access everything, possibly remotely, making it much more difficult to detect. Securing your system on this layer involves among other things making sure known vulnerabilities on your OS are patched or worked around and network access is restricted as needed. This is often the concern of people like network engineers or devops people who are setting up and securing your servers.
- Application container level - this doesn’t necessarily mean your application itself, but typically websites and web applications will be running inside a container software such as a webserver like Apache, Tomcat, ISS, etc. Securing this layer is often just a matter of making sure the latest security patches are applied and known vulnerabilities are closed and access is restricted as needed. If this layer is compromised, the bad actors will typically have access to any files deployed in the container (which may contain other information that will facilitate further leaks). I also include here securing vulnerabilities in the database software used as your backend.
- Application level - this refers to your security in the design of your application itself. Typically, problems in the first three layers above will be the responsibility of people setting up the servers and the server software, who are probably a different set of people than the development team. Concerns from this layer onwards are more applicable to the actual application developers. Application-level security can be further broken down into several levels:
- User authentication - this refers to your application’s implementation of user login and session control, and management of things like passwords, password reset requests, two-party authentication, etc. If the implementation is poor, your application may have vulnerabilities like bad actors being able to identify your users’ usernames by enumerating login requests or being able to brute force their passwords.
- Functional security - this refers to how your application grants rights to each user as to which functions in the system he is able to access. As an example, in a typical system, normal users should not be able to access functions restricted to administrator users.
- User-specific Data security - this ensures that only relevant data for each user can be accessed by each user. For example, if I’m logged in as User ID = 1, I shouldn’t be able to view the data for User ID = 2 simply by changing a parameter such as USER_ID=2 in the URL.
- Domain-specific security - this refers to additional security rules specific to the domain your application is involved in. For example, your security rules may demand that sensitive information can only be viewed by users with specific privileges and would need to be masked for other users. This may introduce some special logic in your program development.
- Logging - applications typically log a lot of information to log files and audit trails, to facilitate debugging of problems and often also for statutory purposes. Security restrictions may require making sure that sensitive data is not included in the logs and audit trails.
- Code level - this refers to security in coding practices in both the code used to write your application and also any third-party libraries or frameworks used by the application. This is highly experience-based and requires knowledge of the most common vulnerabilities as a result of coding practices. Organizations such as OWASP regularly publish lists of the most common vulnerabilities, many of them resulting from poor coding decisions such as SQL injection, cross-site scripting, cross-site request forgery, etc. Modern frameworks will typically provide standard ways for developers to minimize the chance of introducing the most well-known vulnerabilities.
- Database level - this refers to security in the design of the database itself. Examples of concerns on this level are whether sensitive information is encrypted or hashed or whether financial data such as credit card information is stored in the database and secured appropriately (for credit card information specifically, there are standards laid out for managing credit card data that you have to comply with in order to be certified to handle credit card transactions). I guess I would also include in this layer how database backups and purged data are handled - are they backed up and encrypted separated from the main database? - although the protection of such backups may be outside the scope of the application developer.
- Third party code - modern day applications use a lot of third party libraries and packages, not to mention code that developers are copying from StackOverflow. And rightly so - since using these dependencies means savings in effort and more productivity. But developers need to be aware of the cost every time we add a new dependency; not only are we dependent on that third party library working correctly, but also that it is generally secure. And if a security issue is found, will it be fixed in a timely manner? And this kind of thing can be difficult to track. There have been stories of websites being exposed to security breaches or even things like embedded crypto miners simply because they added a node.js dependency that had another dependency that had another dependency that was compromised. It’s the kind of thing more senior developers on the team should be aware of and on the lookout for.
Often times many clients will require that a third-party security audit will be done near the end of the project, and the development team will need to address any security concerns that come up during the audit. I’ve been on the receiving end of these a few times and I’ve found the companies doing them to be fairly thorough.
It’s better for your dev team to be generally aware of security best practices from the very beginning, as it may be more costly to correct security problems towards the end of a project. It’s the kind of valuable tribal knowledge among developers that you can carry easily across projects and companies and tech stacks.