Postel’s Principle is a Bad Idea
programmingisterrible.com
programmingisterrible.com
That said, I wouldn't suggest that our Insertion, Evasion paper presented an argument regarding the Principle in either direction. Even if we forbad leniency, there'd still be ambiguous standards.
I think it would be very interesting if you could give an example or two of this.
* A classic would be IE's abuse of TCP RST: http://www.stroppykitten.com/cms/index.php?option=com_conten...
* A decent chunk of email server code (SMTP & IMAP implementations in particular) is there to handle erroneous client behaviours. The worst cases are those where the workaround leads to misbehaviours (or less optimal behaviours) for conforming clients. If I remember correctly, the popular Outlook series of clients is a notorious source of such warts. A number of SMTP sender libraries will skip over significant parts of the protocol state machine; configuring a mail server to handle that degenerate case can weaken its anti-spam provisions.
That reminds me of the bizarre big because they used a short uint to store the message UID. Maybe it wasn't a short but it definitely wasn't 32-bit as per the spec, there was some magic number that if you went over 'boom'. As an end user it appeared that some messages just disappeared.
And, to make things more confusing, the RFC is a little vague about the re-use of UIDLs.
In the terminology of this article, Torvalds is firmly in the Raymond Chen camp as far as "The kernel is not allowed to break user software" is concerned. The difference between Windows and Linux (and especially between Windows 95 era Windows and Linux of the same vintage) is, apparently, that Linux didn't come from MS-DOS, and so never had to allow application software to get hooks into low-level parts of the kernel.
There was never an official version of Linux for hardware without memory protection, and there never will be. Scope is important.
Even aside from the fact this is wrong:
https://bugzilla.redhat.com/show_bug.cgi?id=638477#c129
http://kerneltrap.org/node/5725
The point I was making was that Linux didn't expose the same kind of deep, undocumented behavior because, as I said, it always had the ability to hide its inner workings.
Vendor A writes a parser that is helpful and is liberal and infers missing quotes and stuff. Vendor B writes something that's mostly to spec, but accidentally doesn't properly quote things. It works fine, because A is liberal and infers these quotes.
Vendor C comes along and builds exactly to spec. But despite being perfectly to spec, it doesn't interop because B sends invalid data! But B is a big vendor, and their stuff works with A.
So now C must add a hack to their parser to deal with the fact that, because A was liberal, B got their implementation wrong.
One example is the loose routing parameter, "lr". It has no value, you just add the name of the parameter "uri;lr" in contrast to other parameters like "tag=bla". Some implementations send "lr=on", and that should be mostly harmless. Except other implementations take that to mean "lr" has a value, and no longer accept just "lr" as turning the feature on.
SIP is full of these things, many of them in the parsing layer alone, let alone actual semantics of what things mean. Browsers are another example: vendor A decides to allow closing tags out of order - how do you do handle such unspecified stuff cross browser?
For instance, the STEP file standard very clearly states that all input files must be 7-bit ASCII. Many of the programs that generate these files (including earlier versions of my own) paid no attention to this and wrote out 8-bit values in strings if the user requested it. Clearly this behavior is wrong. (The principle agrees: "Be conservative in what you do.")
However, rejecting an entire CAD file merely because the text strings in it used an illegal encoding is downright silly. It in no way can change the meaning of the geometry of the file. There is no hidden vector in there for malicious attacks. It makes perfect sense to accept illegal files like this and do your best to make them work, even if it might not get quite the same text strings the user intended.
I think jbert's point about being conservative in what you do in all respects is a strong one. Taking that into account suggests that maybe carefully marking the illegal character as such in the string might well be worthwhile, and is definitely more appropriate than trying to guess what 8-bit character standard was intended.
When an implementation saves this file, it normalizes that other encoding to use an ASCII single quote, then proceeds to write out the rest of the string. This isn't caught inside the implementation, because the encoding library only normalizes when writing. When it reads the data in, it still just represented it as bytes, and there was no ASCII single quote byte until the end of the dangerous string.
So, yes, it's possible that even something as simple as "string encoding" could be used to implement an attack.
And again, if you're worried that there may be an attack vector, change high-bit-set characters to "[Illegal character value N]". Though it might be more merciful to assume they just wanted ISO-8859-1 characters and substitute the appropriate control code.
Liberal vs strict is a false dichotomy. The third solution is to accept all possible inputs, but in a specified way.
Instead of taking draconian XML approach you can solve the problem by taking HTML5 approach and make error handling as interoperable as handling of correct input. In case of STEP files you could require all implementations to clear the 8th bit (or drop or clamp bytes out of range — whatever as long as it's specified and mandatory).
Trying to do something with 8-bit characters -- whether skipping them, indicating an illegal character in the string, or trying to guess what was really meant -- cannot make that situation any worse.
In the case of ie, ie decoded one way and forum software might decode a different way. So the forum software says the string is safe for the browser (according to its decoding rules) but then the browser applies different rules and gets a bad string.
You may not be seeing the danger because you implicitly think a step file from unsafe sources is always unsafe. But imagine if you had a safe file detector program, except it applied different rules than the program you're actually going to open the file with.
HTML5 takes this path with its parsing algorithm, and in fact is cited as an example in the post. However, the designers of the parsing algorithm saw it as being an application of Postel's Principle, rather than an example of the opposite.
The post is really more nuanced than it sounds and would better be titled "Specifications Should Define How to be Liberal in What You Accept".
For example, if you have a set of unused flag bits documented as "reserved, must be zero", then a receiver that silently ignores non-zero bits allows senders that erroneously set those bits to propagate. This is fine, until one day in a future standard you want to define new behaviour for one of those bits, and find you can't - because there's large numbers of senders out there that erroneously set it but don't have any idea about the new behaviour.
Postel didn't talk about off-spec behaviour. He talked about the borderline details, which were often quite hazy in early RFCs. When an RFC says the line length is at most 512 bytes and the terminator is CRLF, does that mean 510+CRLF or 512+CRLF? Postel says to accept 512+CRLF and send 510+CRLF.
If a write a receiver and want to accept 1024 bytes instead, maybe that's a good idea and maybe it's a bad idea. But if you do that, don't invoke Postel's Principle in defense.
Its a pity that RFC's and other internet standards are not written and implemented more rigorously - for example Google have problems interpreting the xml sitemap standard and that is only 3 pages FFS.
Quoting one implementer, whose code did not accept non-ASCII passwords: "Oh, the password syntax is on page 88? My printout ends after page 68". In that RFC, the details are spelt out in appendices, and Appendix A starts on page 69. (And I'm sure pg assigns bonus karma if you can identify the RFC.)
Once you understand the problem, the clarification, and that your interop peers do not, I bet your implementation's handling of the issue will be conservative in what it sends and liberal in what it receives.
Taking the perl-over-c-stdlib example (but I think it applies in other cases), if the "perl layer" was more strict in what it sent to the stdlib layer, there would have been no problem.
i.e. the error is in thinking of only the network as the place to apply the maxim. In fact, you should scrupulously adhere to every interface you pass data to (internal or external) - and interpret as reasonably as possible all interfaces you receive data from.
[I'd agree that the latter pt can be weakened. But it does help interop - and if you clean up your act before you hit the next layer then you limit any damage.]
Since then we've had computer viruses, worms, and other malware; we've had hackers, crackers, spies, criminals, and semi-competent people flooding the Internet; we have people not just making accidental requests but fuzzing and fusking to try to break things or bypass controls.
It's a great principle for the human stuff, but it feels really outdated for technical stuff.
A bigger problem is when the liberal implementations become the de-facto standard.
They even use the phrase "infer" in several places, encouraging systems to take obviously malformed packets and try to figure out what they meant.
Being liberal in accepting input, apart from security issues, seems to create a worse situation. Implementation A messes up something, but B seems to be OK with it. C then accidentally requires it, while D rejects it. Depending on how large and responsive the vendors behind those implementations are, you end up with a nasty state of affairs, with random hacks here and there.
It's hard enough to create unambiguous, comprehensible, specifications. Telling implementations to be liberal only makes it worse.
If the use of a format for interoperability can only be reasonably used by a single vendor, it has no benefit over a binary protocol.
The entire SOAP and XML-RPC space is postels law writ large.
I had to only face the true horrors on one occasion, for a Responsys integration. They had the C# examples and the Java examples. The API that they offered for the two had differences because some methods would work with one, some with the other.
I'm a Perl programmer, so tried that. After all you just have to translate the language, right? Wrong. After banging my head against that mess for a week or so, I finally gave up, wrote the communication in Java, and had a Perl launcher for it.
But that exact behaviour, trying to infer intent, meant that tons of unspecified behaviour had to be added to all browsers to try to mimic which each one did to handle totally invalid cases.
So, even if leniency did make it easier to create a web page, it also contributed greatly to the already difficult task of creating consistent cross-browser rendering.
Look at JavaScript, and the recent semi-colon debacle with Bootstrap and some other tool. Having "implementer defined" leniency just means you'll get multiple interpretation and problems.
Word processors won out over text processors for the nontechnical user partially for this reason.
Postel's law applied to HTML let nontechnical users get things done with less impedance. It's less important now not because it was the wrong choice, but because users have moved higher up the stack to CMSes that handle formatting etc.
Consistency across browsers back then was only ever of serious concern to professionals in design or browser programming.
It's obviously easier to write than to read; but it's definitely easy to make a mistake. There's a lot of illogical cruft that's accumulated in HTML; so even a careful implementer might make a mistake (and might not detect it since most other implementations are so liberal).
(1) Now that MSIE does support true XML parsing of XHTML, almost no one is choosing to use it over HTML.
(2) Of the few experts who conditionally served either text/html or application/xhtml+xml depending on the UA, or serve XML unconditionally now, almost all have bugs in their sites which can get them to produce ill-formed XML which then shows an error page in the browser (for instance, submitting comments with certain sorts of errors). This is evidence that the draconian error handling approach is too challenging even for experts and imposes the costs of small mistakes on users.
On Markdown side, you have sexy but ill-defined grammar, and on RST you have a slightly less nice-looking guy with a much better defined grammar, which allow building a saner tooling upon it.
The Robustness Principle is a prescription that lays down a strategy for growing robust systems. It works. The problem is that the robustness it provides isn't quite what people want it to be.
A great example of this was Google's Code Search product, before it was canceled. Since full backtracking search was blowing out the tiny thread stacks in servers, they had to reduce what they allowed to actually regular expressions - expressions generating a regular language. Queries could be turned into DFAs of linear size with respect to input, making arbitrary public regex searches over code indices feasible.
Ross Cox's regular expressions write-ups are quite a fascinating deep-dive: http://swtch.com/~rsc/regexp/