An exposition on the first of the Twelve Web (In)Security Truths
#1 – Software execution is less secure than software design, but running code has more users.
A running site is infinitely more useable than one that only exists in design. Talk all you want, but eventually someone wants you to deliver that design.
Sure, you could describe Twitter as a glorified event loop around an echo server. You might even replicate it in a weekend with a few dozen lines of Python and an EC2 instance. Now scale that napkin design to a few hundred million users preserving security and privacy controls. That’s a testament to implementing a complex design – or scaling a simple design if you boil it down to sending and viewing tweets.
It’s possible to have impressive security through careful design. A prominent example in cryptography is the “perfect secrecy”1 of the One-Time Pad (OTP). The first OTP appeared in 1882, designed in an era without the codified information theory or cryptanalysis of Claude Shannon and Alan Turing.2 Never the less, its design understood the threats to confidential communications when telegraphs and Morse code carried secrets instead of fiber optics and TCP/IP. Sadly, good designs are sometimes forgotten or their importance unrecognized. The OTP didn’t gain popular usage until its re-invention in 1917, along with a more rigorous proof of its security.
But security also suffers when designs have poor implementations or have complex requirements. The OTP fails miserably should a pad be reused or is insufficiently random. The pad must be as long as the input to be ciphered. So, if you’re able to securely distribute a pad (remember, the pad must be unknown to the attacker), then why not just distribute the original message? Once someone introduces a shortcut in the name of efficiency or cleverness the security breaks. (Remember the Debian OpenSSL debacle?) This is why it’s important to understand the reasons for a design rather than treat it as a logic table to be condensed like the singularity of a black hole. Otherwise, you might as well use two rounds of ROT13.
Web security has its design successes. Prepared statements are a prime example of a programming pattern that should have relegated SQL injection to ancient CVEs. Only devotees of Advanced Persistent Ignorance continue to glue SQL statements together with string concatenation. SQL injection is so well-known (at least in appsec) and studied that a venerable tool like sqlmap has been refining exploitation for over six(teen) years.
Yet the Internet loves to re-invent vulns. Whether or not SQL injection is in its death throes, NoSQL injection promises to reanimate its bloated corpse. Herbet West would be proud.
Sometimes software repeats the mistakes, intentionally or not, of other projects without understanding the underlying reasons for those mistakes. The Ruby on Rails Mass Assignment feature is reminiscent of PHP’s
register_globalsissues. Both are open source projects with large communities. It’s unfair to label the entire group as ignorant of security. But the question of priorities has to be considered. Do you have a default stance of high or low security? Do you have language features whose behavior changes based on configuration settings outside the developer’s control, or that always have predictable behavior?
Secure design is never easy. Apache’s reverse proxy/mod_rewrite bug went through a few iterations and several months of discussion before Apache developers arrived at an effective solution. You might argue that the problem lies with users who created poor rewrite rules that omitted a path component. But I prefer to see it as a design flaw because users had difficulty understanding its nuances and mistakes lead to security issues. Either way, the vuln proved how difficult it is to choose between trade-offs in security decisions.
React even preserved the ability to write insecure HTML, but it did so in a way that was obvious in its design as
dangerouslySetInnerHTML. This also made it easier to run linters for identifying areas of risk in the code. Alas, React didn’t even appear until a year after the original version of this article.
The Content Security Policy has been trying to bring a secure design against HTML injection with mechanisms that restrict how a page may load resources. CSP doesn’t prevent HTML injection – it mitigates its exploitability. So, developers must still invest in frameworks like React or other ways of preventing these XSS in the first place. CSP feels like a well-intentioned design, but it suffers from being placed at the point of exploitation (the browser) as opposed to the point where flaws are introduced (the app). Despite more than ten years of design and an iteration to CSP Level 3, this is the type of security design that places a lot of burden on developers to implement without a lot of compelling benefits to justify that burden.
Secure design should be how we send whole groups of vulns to the graveyard. Good security models understand the threats a design counters as well as those it does not. Spend too much time on design and the site will never be implemented. Spend too much time on piecemeal security and you risk blocking obscure exploits rather than fundamental threats.
As the ancient Fremen saying goes, “Truth suffers from too much analysis.”3 Design also suffers when its scrutiny is based on nonspecific or unreasonable threats. It’s important to question the reasons behind a design and the security claims it makes. Yes, HSTS relies on the frail security of DNS – it’s a trust on first use (TOFU) model where the browser assumes the header comes from an authentic source.
However, HSTS improved the reliability of maintaing HTTPS connections and minimizing the impact of malicious CAs, but it also introduced risk. A misconfigured HSTS header could create a self-induced DoS by preventing browsers from connecting. And that misconfiguration might come from a developer mistake or an attacker with the ability to set headers from a compromised server.
Design your way to a secure concept, code your way to a secure site. When vulns appear determine if they’re due to flaws in the design or mistakes in programming. A design that anticipates vulns, like parameterized queries, should be easy to implement and serve the developer’s needs. Vulns that surprise developers should lead to design changes that provide more flexibility for resolving the problem.
Inflexibility, whether in design or implementation, is dangerous to security. As the Bene Gesserit say, “Any road followed precisely to its end leads precisely nowhere.”4
• • •
As Steven Bellovin notes in his paper, an 1882 codebook contains an amusingly familiar phrase regarding identity questions, “Identity can be established if the party will answer that his or her mother’s maiden name is…“ It seems identity proofs haven’t changed in 140 years! ↩
Music has a universal appeal uninhibited by language. A metal head in Istanbul, Tokyo, or Oslo instinctively knows the deep power chords of Black Sabbath – it takes maybe two beats to recognize a classic like “N.I.B.” or “Paranoid.” The same guitars that screamed the tapping mastery of Van Halen or led to the spandex hair excess of 80s metal also served The Beatles and Pink Floyd. And before them was Chuck Berry, laying the ground work with the power chords of “Roll Over Beethoven”.
All that from six strings and five notes: E - A - D - G - B - E. Awesome.
Then there’s the written word on the web. Thousands of symbols in 8 bits, 16 bits, and 32 bits. In ASCII, or US-ASCII as RFC 2616 puts it, or rather ISO-8859-1. Or UTF-8, which is easier to adopt because it’s like an extended ASCII. On the other hand if you’re dealing with GB2312 then UTF-8 isn’t necessarily for you. Of course, in that case you should really be using GBK instead of GB2312. Or was it supposed to be GB18030?
Character encodings get messy and confusing quickly. Our metal head friends like their own genre of müzik / 音楽 / musikk – one word, three languages, many symbols. In this page, those symbols (or glyphs) share one encoding: UTF-8.
The W3C was founded to develop common protocols to lead the evolution of the World Wide Web. The path W3C follows to making text on the Web truly global is Unicode. Unicode is fundamental to the work of the W3C; it is a component of W3C Specifications, from the early days of HTML, to the growing XML Family of specifications and beyond.
Unicode has its learning curve. With Normalization Forms. Characters. Code Units. Glyphs. Collation. And so on. The gist of Unicode is that it’s a universal coding scheme to represent all that’s to come of the characters used for written language; hopefully never to be eclipsed.
The security problems of Unicode stem from the conversion from one character set to another. When home-town fans of 少年ナイフ want to praise their heroes in a site’s comment section, they’ll do so in Japanese. Yet behind the scenes, the browser, web site, or operating systems involved might be handling the characters in UTF-8, Shift-JIS, or EUC.
The conversion of character sets introduces the chance for mistakes and breaking assumptions. The number of bytes might change, leading to a buffer overflow or underflow. The string may no longer be the C-friendly NULL-terminated array. Unsupported characters cause errors, possibly causing an XSS filter to skip over a script tag. A lot of these concerns have been documented (and here). Some have demonstrable exploits, as opposed to conceptual problems that run rampant through security conferences, but never see a decent hack.
The first attacks didn’t need IDNs. They used trivial letter substitution with look-alikes, such as swapping
l(the letter L) and
1(the number one) in
dead1iestattacks.com. IDNs provided more sophistication by allowing domains with changes visually harder to detect like
What’s been less well documented (from what I could find) is the range of support for character set encodings in security tools. The primary language of web security seems to be English based on the popular conferences and books. But useful tools come from all over. Wivet originated from Türkiye (here’s some more UTF-8: Web Güvenlik Topluluğu). Sqlmap and w3af support Unicode. So maybe this is a non-issue for modern tools.
In any case, it never hurts to have more “how to hack” tools in non-English languages or test suites to verify that the latest XSS finder, SQL injector, or web tool can deal with sites that aren’t friendly enough to serve content as UTF-8. Or you could help out with documentation projects like the OWASP Developer Guide.
Sometimes translation is really easy. The phrase for “heavy metal” in French is “heavy metal” – although you’d be correct to use “Métal Hurlant” if you were talking about the magazine or movie. Character conversion can be easy, too. As long as you stick with a single representation. Once you start to dabble in the Unicode conversions from UTF-8, UTF-16, UTF-32, and beyond you’ll be well-served by keeping up to date on encoding concerns and having tools that spare you the brain damage of implementing everything from scratch.• • •
My current writing project has taken time away from adding new content lately. Here’s a brief interlude of The Twelve Web Security Truths I’ve been toying with as a side project. They are modeled on The Twelve Networking Truths from RFC 1925.
• • •
- Software execution is less secure than software design, but executing code attracts actual users.
- The time saved by not using parameterized queries to build SQL statements should be used to read about using parameterized queries.
- Content with XSS vulns are affected by the Same Origin Policy, which is nice for XSS attacks that inject into the site’s origin.
- CSRF countermeasures like Origin headers mitigate CSRF, not XSS. Just like X-Frame-Options mitigates clickjacking, not XSS.
- Making data safe for serialization with JSON does not make the data safe for the site.
- There are four HTML injection vulns in your site today. Hackers will find two of them, the security team will find one, the dev team will introduce another one tomorrow.
- Deny lists miss the attack payload that works.
- A site that secures user data still needs to work on the privacy of user data.
- Hashing passwords with 1,000-round PBKDF2 increases the work factor to brute force the login page by a factor of 1. Increasing this to a 10,000-round PBKDF2 scheme provides an additional increase by a factor of 1.
- A site is secure when a compromise can be detected, defined, and fixed with minimal effort and users are notified about it.
- Off-by-one errors only happen in C.