That's not really the problem. The problem is there is no distinction between data and control leading to everything coming to you in one binary stream. If the control aspect would be out-of-band then the problem would really go away.
Length prefixes will just turn into one more thing to overwrite or intercept and change. That's much harder to do when you can't get at the control channel but just at the data channel. Many old school protocols worked like this.
This is the important takeaway here. Changing the encoding simply swaps out one set of vulnerabilities and attacks for another. Separating control flow and data is the actual silver bullet for this category of attacks.
Unfortunately, there’s rarely ever a totally clear logical separation between the two. Anything you want to bucket into “control”, someone else is going to want the client to be able to manipulate as data.
Granted, if you made that control channel stateful, you'd make a lot of problems go away. But you could do that with a combined control/data stream too.
What am I missing? How would an out-of-band control channel make things easier?
That said, I think many issues with the web could be solved by implementing new protocols as opposed to shoehorning everything into HTTP just to avoid a firewall...
So <html>abc</html> would go as
<html><datum 1></html> where datum 1 would refer to the first datum in the data stream, being 'abc' and no matter what trickery you'd pull to try to put another tag or executable bit or other such nonsense in the datum it would never be interpreted. This blocks any and all attacks based on being able to trick the server or eventual recipient browser of the two streams to do something active with the datum, it can only be passive data by definition.
For comparison take DTMF, which is inband signalling and so easily spoofed (and with the 'bluebox' additional tones may be generated that unlock interesting capabilities in systems on the line) and compare with GSM which does all its signaling out-of-band, and so is much harder to spoof.
The web is basically like DTMF, if you can enter data into a form and that data is spit back out again in some web page to be rendered by the browser later on you have a vector to inject something malicious and it will take a very well thought out sanitation process to get rid of all the possibilities in which you might do that.
If the web were more like GSM you could sit there and inject data in to the data channel until the cows came home but it would never ever lead to a security issue.
No amount of extra encoding and checks will ever close these holes completely as long as the data stays 'in band' with the control information.
I could easily see making <script> and <link> resources required to be separately requested (like images are now -- ignoring data/base64 resources), but we're back to redefining HTML.
I'm not arguing against that...
It's really hard to have these types of debates though, because everyone focuses on different problems of the HTTP/HTML webapp request/response cycle. Like you said, adding separate control/data channels would help, but that doesn't solve SQL injection attacks (which is a whole other class, but that's not really an HTTP/HTML issue, it's a backend issue and I don't see how you'd avoid that with a simple protocol change). Simply making HTTP stateful could potentially solve a different class of session highjacking, etc...
There are so many attack vectors that I think it does make sense to think about what a replacement for HTTP/HTML would look like. Most of these problems arise from trying to re-engineer a document format (HTML) to support interactive webapps. We should think about how to do this better... (without recreating ActiveX -- shudder).
This has been implemented in HTTP (not HTML); you can enable the requirement right now by serving your pages with an appropriate Content-Security-Policy header.
(html "abc")
This guarantees that no matter what is inside "abc" it simply can't escape into the control stream: (html "This is not (malicious \"boo\")")
This is just a pretty display of what would actually be these bytes: (4:html29:This is not (malicious "boo"))
It doesn't matter what one puts in the atom: it can't escape and damage the control stream. (html "user content")
user content := " (script "something malicious")"
(html "" (script "something malicious"))
the length-prefixed version cannot escape in this way.Or, run your data through stored procedures instead. It took me a while to figure out why stored procedures were so much more secure than regular queries. I finally figured out it was because a stored procedure does exactly what the grandparent post says: It treats all inputs as data with no possibility to run as code.
Perhaps the most naive example: https://pastebin.com/acQqhDvy
I think they're more useful for organization and abstraction than security. Then again, a well organized and smartly abstracted system can lead to better security!
But I think bind parameters are probably a better example of security.
Binding effectively separates the data from the logic. So you define two separate types of things, and then safely join those things together by binding them. It doesn't matter too much whether that happens in the application making a call to the database or in the database in a stored procedure. Obviously this same concept can be applied at many different points along the application stack. The analogous concept in the UI is templating. You define a template and then safely inject data into that template.
This isn't well defined. Take this pseudocode stored procedure (OK, it's a python function):
def retrieve_relevant_data(user_input):
if user_input == 1:
return BACKING_STORE[5]
elif user_input == 2:
perform_side_effects()
return BACKING_STORE[1]
else:
return "Go away."
You can provide any input to that. You could think of this as a function which "treats all input as data with no possibility to run as code" (it never calls eval!). But you could also usefully think of this as defining a tiny virtual machine with opcodes 1 and 2. If you think of it that way, you'll be forced to conclude that it does run user input as code, but the difference is in how you're labeling the function, not in what the function does.The security gain from a stored procedure, on this analysis, is not that it won't run user input as code. It will! The security gain comes from replacing the full capability of the database ("run code on your local machine") with the smaller, whitelisted set of capabilities defined in the stored procedure.
The security gain is that it you are only able to run queries that the DBA allows you to. If you can't write arbitrary queries, you won't get arbitrary results. If you can only run a stored procedure, you are abstracted away from those side effects. Another way of saying this -- the security risk is shifted from the app developer to the DBA. Someone is still writing a query (or procedure code), so there will always be some risk.
This could also be achieved with a well written microservice/package that developers go through without depending on dba.
String escaping SQL? How is anyone thinking that is still a thing in 2017? The problem has been solved for two decades
* At least with .Net/Entity Framework/Linq you mock out your dbcontext and test your queries with an in memory List<>
https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113)....
Agree.
> harder to unit test
Disagree. I've implemented unit tests that connect to the normal staging instance of our database, clone the relevant parts of the schema into a throw-away namespace as temporary tables, and run the tests in that fresh namespace. About 100 lines of Perl.
That was five years ago. These days, it's even easier to do this correctly since containers allow you to quickly spin up a fresh Postgres etc. in the unit test runner.
It also need not be correct. If you're only ever doing "SELECT * FROM $table WHERE id = ?", you're fine, but a lot of real-world queries will use RDBMS-specific syntax. For example, from the top of my head, the function "greatest()" in Postgres is called "max()" in SQLite. How is it called in your mock?
Mocking out tables with in-memory lists adds a huge amount of extra code that's specific to the test (the part that parses and executes SQL on the lists). C# has this part built in via LINQ, but most other languages don't.
By the way, I see no practical difference between "in-memory lists" and SQLite, which is what I'm currently using for tests of RDBMS-using components, except for the fact that SQLite is much more well tested than $random_SQL_mocking_library (except, maybe, LINQ).
The way that Linq works and the fact that it’s actually compiled to expression trees at compile time and that the provider translates that to the destination at runtime whether it be database specific SQL, MongoQueries or C#/IL, does make this type of testing possible.
If this was the case, it would be near-impossible to write HTML by hand. And if you're writing HTML with a tool (React, HAML etc.), the tool could be doing HTML escaping correctly instead. This isn't an issue with HTML, it's an issue with human error.
All security issues are due to human error. Those are solved by building better tools.
> If this was the case, it would be near-impossible to write HTML by hand.
If, besides the text form, there would be a well-defined length-prefixed binary representation, we could simply compile HTML to binary-HTML, which would immediately made the web not only safer, but also much more efficient (it's scary if you think just how much parsing and reparsing goes on when displaying a web page).
My point is that there's nothing wrong with HTML. HTML isn't a tool, it's a format for storing and transmitting hypertext. If you're using React or HAML or any of the other HTML-generating tools, you're effectively immune from XSS. I'm putting forth that developers aren't using effective tools (shame on every templating engine that doesn't escape by default), and that calling the web as a platform bad is a bit nonsensical. It's like saying "folks are writing asm by hand and their code has security issues, therefore x86_64 is insecure".
However, no such tool exists. I think there's a deeper issue here: the sheer number of ways you can generate XSS alone, even ignoring the other exploit types, is far beyond what any tool is capable of stopping. Look at one of the XSS holes found by Homakov that I linked to from my article:
http://sakurity.com/blog/2015/06/25/puzzle2.html
The XSS occurs on this line of JavaScript, not HTML:
$.get(location.pathname+'?something')
That's a simple line of JQuery that does an XmlHttpRequest to the same page that was loaded with an additional parameter. By itself, it is not an XSS. But if the backend is/was running Ruby on Rails (presumably some old version by now) then it could turn into an XSS due to a combination of features that all look superficially harmless.Show me the tool that would have avoided that type of exploit, without already knowing about it and having some incredibly specific hardcoded static analysis rule.
When I argue that the web is unsafe by design, it's because cases like that aren't rare, they're common. To paraphrase Veekun, scratch the surface of web security and you'll find yourself in a bottomless downward spiral, uncovering more and more horrifying trivia.
I think you're missing another two obvious explanations:
1. Lack of education when picking a tool (copy paste from bad SO answers is a frequent source of bad code).
2. Developers don't care. If it works, why bother wrapping your head the rest of the way around to understand why it works or whether it's secure?
> By itself, it is not an XSS. But if the backend is/was running Ruby on Rails (presumably some old version by now) then it could turn into an XSS due to a combination of features that all look superficially harmless.
Sure, ERB before RoR essentially had security turned off by default (as I noted). And this issue could happen with any other non-web system, turning into any other kind of vulnerability. This isn't a web problem, it's a system security problem. Bad inputs in a "native" app could lead to security issues in the output of apps on other devices. Badly implemented binary data decoders in a desktop application could do far worse than a XSS in the browser.
This problem is misattributed as a "web problem" because there are far more complete systems on the web than there are on nearly any other platform. It's like the tired argument that Mac is more secure than Windows, but Windows has historically had an overwhelmingly outsized market share, making OS X issues far less valuable to attackers.
> When I argue that the web is unsafe by design, it's because cases like that aren't rare, they're common.
I don't disagree that these issues are common, but I disagree that the web is unsafe by design. The web is a platform. If everyone wrote their Python APIs without a framework, I can guarantee they would be littered with security holes. If everyone wrote their own text renderer in C++, just displaying strings on the screen would be a dangerous task.
There are good tools that make it really hard to fuck up on the web. Seriously, try to accidentally have a XSS vulnerability in an isorendered React app with Apollo. The problem is folks that want to jQuery-jockey their way across the finish line and don't understand that they are making terrible mistakes.
How many developers do you think might have written a web server in their time, or will do in the next 10 years? And how many know will pass URL components straight through to glibc for resolution, as is the obvious way to do it, and create an exploitable SSRF vuln on their network? How many developers will have even heard of this type of problem?
New ways to exploit weird edge cases and obscure frameworks crop up constantly - it is a full time job even to keep up with it all. At some point you can't blame people walking through a minefield because they keep getting blown up. The problem is the mines.
this issue could happen with any other non-web system, turning into any other kind of vulnerability. This isn't a web problem, it's a system security problem.
That's just not the case, sorry. Have you ever actually written desktop apps that use binary protocols? It's a web problem:
• It relies on the over-complex and loose parsing rules for URLs
• It relies on unexpected behaviour in one of the most popular web libraries
• It relies on bizarre and unexpected behaviour in XmlHttpRequests
• It relies on the fact that web apps routinely import code from third party servers to run in their own security context.
I have been programming for 25 years and I have never seen an exploit like that before in managed desktop apps using binary protocols to a backend.
Seriously, try to accidentally have a XSS vulnerability in an isorendered React app with Apollo.
An isorendered React app with Apollo? I think that may be the most web thing I've heard all week ;)
I think I'll take the bet:
https://medium.com/node-security/the-most-common-xss-vulnera...
That article shows the patterns I cover in my article:
• Buffers can get terminated early, even in a theoretically "XSS-proof" framework.
• JSON can get interpreted as code
• Even experienced web developers can't get it right
If you've never written a desktop app before, I'd suggest grabbing IntelliJ or NetBeans and trying it out. TornadoFX is a good framework to try.
How so? If you allow the user to send arbitrary data, and your handling of that data is where the problem lies, it isn't going to matter whether the client sends a length-prefixed piece of data. You still have to sanitize that data.
HTML, and whether it uses closing tags or not, is pretty much irrelevant to the way injection attacks work, as far as I can tell. Maybe I'm missing something...do you have an example or a reference to how this could solve injection attacks?
It would be interesting to see if this idea could work in practice.
[0]: https://dev.twitter.com/overview/api/entities-in-twitter-obj...
I guess it would have to be protobufs over TLS, and abuse port 443, to get through firewalls from hell.
I feel like this is conflating two different problems and potential solutions.
I'm not saying injection attacks aren't real. I'm saying that whether HTML uses closing tags or not is orthogonal to the solution. But, again, maybe I'm missing something obvious here. I just don't see how what you're suggesting can be done without types and I don't see how types require prefixing data size in order to work.
No it wouldn't. It wouldn't fix sql injection and it also wouldn't fix the path bug the op linked.
The problem is not length, it is context unaware strings. The problem is our obsession with primitive types that pervade our codebases.
Injection in general is simply a trust problem. If you can trust all inputs fully (hint: you can't, because nobody can), then you will never have an injection attack.
If you are exposing code to an untrusted, hostile environment (which is pretty much the web), no language that does anything useful will protect you against not caring about security.
Even if you absolutely need to inject a string in a sql query, sanitizing it is trivial. In .net / MS SQL, a simple x = x.Replace("'","''") does the trick. For any other common data type, strong typing should be sufficient to prevent any injection.
Obviously nobody is going to be typing length prefixes manually, so our tools are going to do it for us.
Now we're back where we started where you accidentally inline user content as HTML, except now HTML has the added cruft of someone's HN comment solution.
But like you I'm not totally convinced. I think this idea would make it easier for people trying to do the right thing to get it right; but for the blissfully ignorant? Might not help at all. Either way it needs a more flushed out spec.
From the XKCD:
Robert'); DROP TABLE Students; --
The issue here is that ');
Is being intepreted as the end of a string; it assumes that there will be something like: format("SOME_FN('%s');",user_name)
going into SQL, and this fools the system.SQL solves this already with parameterized queries, and many HTML libraries also solve this in various ways, but if it were instead:
format("SOME_FN(%d:%s)", len(user_name), user_name)
then there is no value you can put in user_name that will let you escape the function call.Length prefixes are one way of working this, but only scratch the surface of the issue. As others have pointed out, it's also the fact that the control elements are inline with the data.
<p:25><script:14>somethingBad()
Will still run somethingBad(). You are at least sandboxed to the containing element though, so restricting certain elements to only appear in parts of the HTML tree could prevent this (e.g. if all scripts were disallowed in BODY then merely constraining user-generated content to the BODY would work; right now you could still get hit by someone including </body> in their content.Oh thank God. I'm going to forward this to my wife.
Ha ha. I'll get my coat.
Or were senders always going to send true values for length and data?
Really, you can't trust any sender, so the data should be validated anyway.
There's been known attacks where a sender says here's 400 bytes and the receiver stupidly trusted that length specifier, and the sender's sends more (or less) crafted bytes and BOOM!
Known good data start and end specifiers, which HTML has, seems a good answer when dealing with untrusted senders (read:everyone)
> The 90s were slowly reinventing UNIX and stuff invented at Bell Labs.
Yes, this reminds me of: "Wasn't all this done years ago at Xerox PARC? (No one remembers what was really done at PARC, but everyone else will assume you remember something they don't.)" [1]
> "Buffers that don’t specify their length"
> Is this really a common problem in web apps? Most web apps are built in languages that don't have buffer overrun problems. There are many classes of security bug to be found in web apps, some unique to web apps...I just don't think this is one of them. This was a common problem in those C/C++ programs from the 90s the author is seemingly pretty fond of. Not so much web apps built in PHP/JavaScript/Python/Ruby/Perl/whatever.
Most injection attacks are due to this; if html used length-prefixed tags rather than open/close tags most injection attacks would go away immediately.
1: https://www.cs.purdue.edu/homes/dec/essay.criticize.html