Clever Developers, Costly Mistakes

Published: 10 Sep 2024

In the world of software development, the allure of writing clever code is strong. Developers, especially those who are highly skilled, often take pride in crafting intricate solutions that showcase their expertise. They might use advanced techniques like metaprogramming or implement complex rules engines to solve problems in novel ways. While this cleverness can be impressive, it comes with significant risks. When a clever developer makes a mistake, the impact is often severe—think remote code execution (RCE) rather than a simple bypass. Moreover, even if the clever developer never makes a mistake, their complex code can create significant challenges for future maintainers, leading to serious vulnerabilities. This is why, in many cases, the smartest choice is to write "dumb" code—code that is simple, easy to understand, and easy to maintain.

The Risks of Clever Code

Clever developers, by their very nature, tend to make fewer mistakes. Their deep understanding of programming languages, frameworks, and algorithms allows them to write highly optimized and efficient code. However, when they do make a mistake, it often has far-reaching consequences. This is because clever code tends to be complex, utilizing advanced techniques that are powerful but also more prone to subtle errors. For example, metaprogramming—a technique where code writes code—can be incredibly efficient, but if something goes wrong, it can lead to catastrophic vulnerabilities like RCE.

The problem is compounded when these clever developers move on to new projects or different jobs. At some point, someone else will need to maintain, modify, or improve the code they wrote. This maintainer might not have the same level of expertise or understanding of the intricate logic embedded in the code. As a result, they might miss something—a small detail that would have been obvious to the original developer but is obscure to anyone else. When this happens, the consequences can be severe. A piece of code that seemed flawless when it was written can become a ticking time bomb, waiting for the next maintainer to inadvertently introduce a vulnerability.

A Classic Example: CVE-2008-4096 in phpMyAdmin

A well-known example of this dynamic is CVE-2008-4096, a vulnerability in phpMyAdmin, a popular tool for managing MySQL databases. In this case, the vulnerability arose from the use of create_function, a feature in PHP that allows the creation of anonymous functions based on a string. This string was dynamically generated from user-controlled input, making it fully injectable and extremely dangerous. The developers had opted for a sophisticated approach, likely thinking it was more elegant or efficient. However, this complexity introduced significant risk.

Here’s a snippet of the code in question, where $sort_by is fully user-controlled:

if ($GLOBALS['cfg']['NaturalOrder']) {
  $sorter = 'strnatcasecmp';
} else {
  $sorter = 'strcasecmp';
}

/* produces f.e.: */
/* return -1 * strnatcasecmp($a["SCHEMA_TABLES"], $b["SCHEMA_TABLES"]) */
$sort_function = '
  return ' . ($sort_order == 'ASC' ? 1 : -1) . ' * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);
';

usort($databases, create_function('$a, $b', $sort_function));

In this example, the dynamically generated code based on user input becomes a potential vector for code injection attacks. A simpler approach-using a few if/else statements in a function-could have achieved the same result without the added risk. However, the more sophisticated method was chosen, perhaps because it was seen as cooler or more powerful. Unfortunately, this decision opened the door to a serious security flaw. The vulnerability allowed attackers to inject arbitrary code, leading to a potentially devastating compromise of the system.

Language Matters: Simplicity at the Language Level

There is also a case to be made for choosing programming languages that prioritize simplicity and readability. Part of the original philosophy behind Golang, for instance, is to create a language that is easy for most programmers to understand. In contrast, languages like Perl, Ruby, or more esoteric ones like Scala, often encourage or allow for more complex and idiosyncratic code. While these languages can be powerful, they also tend to introduce "surprises"-unexpected behaviors that can be difficult for others to understand and maintain.

In this context, the security and productivity of the entire development team-and indeed the entire organization-should take precedence over the cleverness or productivity of a few "rockstar" developers. If everyone on the team, including operations and security, can easily understand and work with the code, the overall security and productivity of the project will be far higher. This is why using languages and writing code that minimizes surprises is so important: the security of the code as a whole, written, reviewed, and maintained by many, is far more valuable than the security of code written by a few individuals.

Conclusion

In the end, the goal of writing code is not just to solve a problem but to solve it in a way that is sustainable over the long term. While clever code can be impressive, it often introduces unnecessary risks. When those risks materialize, the impact can be devastating. On the other hand, "dumb" code—simple, maintainable, and easy to understand—minimizes those risks and ensures that the software remains secure and reliable, even as it evolves over time. Additionally, choosing programming languages that emphasize simplicity and readability can further enhance the security and maintainability of the codebase. The next time you're tempted to use a complex solution or a language that encourages clever tricks, consider whether a simpler approach might be the smarter choice. After all, in software development, sometimes the dumbest code is the smartest.

Photo of Louis Nyffenegger
Written by Louis Nyffenegger
Founder and CEO @PentesterLab