5 ms·
Code injection is currently the #1 language-related security vulnerability [1][2] in memory-safe languages, which is why languages should be very careful when a
by pron 5y ago
Code injection is currently the #1 language-related security vulnerability [1][2] in memory-safe languages, which is why languages should be very careful when adding string interpolation as it may well be their most security-sensitive feature: "Templated string injection attack prevention will be of primary concern. The result of template processing can to be used in sensitive applications, such as database queries. Validation of templates and expression values prior to use can prevent catastrophic outcomes." [3].
[1]: https://owasp.org/www-project-top-ten/ https://owasp.org/www-project-top-ten/
[2]: https://www.softwaretestinghelp.com/sans-top-20-security-vulnerabilities/ https://www.softwaretestinghelp.com/sans-top-20-security-vul...
[3]: https://openjdk.java.net/jeps/8273943 https://openjdk.java.net/jeps/8273943
- pcwalton 5y agoIn reality, sqlx [1], probably the most popular SQL library for Rust, has a query! format string that ensures that all parameters are properly escaped. As far as I can tell, you can't use the new format string support to create SQL queries with that macro yet, so there is no security problem. When that's fixed and query! is updated for the new format string support, I'm certain that they will escape their parameters, so there will be no security problem then either. Because all format strings are in macro context, where the macro has full control over what to do with all substituted parameters, Rust already has sanitized string interpolation. In terms of that JEP, the macro invocation is the policy object. [1]: https://github.com/launchbadge/sqlx https://github.com/launchbadge/sqlx
- samwillis 5y agoWhat I particularly like about Pythons new(ish) “f strings” is that, firstly it’s opt in, you have to mark your strings with the “f” prefix. Secondly it is converted to opcodes at compile time, so f strings can’t be modified at runtime unlike standard “old fashioned” Python string interpolation. As someone noted in another comment, this with Rust is effectively opt in with the println! and format! macros. Does it converted to machine code at compile time?
- kimundi 5y agoYes, the macros parse the string at compiletime and generate code that does the formatting as given by them. Here is an example for how the code would look like expanded: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1956e05cb7ffc42075424024dca301d7 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- meltedcapacitor 5y agoThe Rust println! macros interpret the format string at compile time. You cannot do: let f = "{:.3}"; println!(f, 3.145); because then the macro cannot be sure what the string will be at runtime and thus cannot be expanded. Personally I dislike the whole idea of embedding sub-languages in strings inside host languages, but this is a lost cause frankly, and if one must do it, this is a pretty good way.
- pron 5y agoThe issue is that the format string does not require the variables to be sanitised. If the resulting string is then used the input to some sensitive operation, the operation no longer knows which parts of the string originated in the format string and which came from variables.
- ljm 5y agoMakes me think of tainted strings, a feature of Perl and Ruby (although I haven't often seen them used in Ruby).
- danhau 5y agoIsn’t that‘s just an input sanitization problem? If you chuck raw user input into some sensitive piece of code, you‘ve lost - doesn‘t matter whether its passed as a string or not. I don’t see how Rust‘s comptime string formatting is a pitfall here?
- tialaramex 5y agoSure, Rust does not require that your arbitrary string is actually safe HTML, a valid SQL string, XML element, DNS name and IPv4 address. As a result, if you take arbitrary input, and then run it as an SQL query who knows what will happen, despite it being a memory safe language. On the other hand, (safe) Rust has strong type checking, so you can make types named SafeHTML, ValidSQLString, XMLelement and so on, with the properties you desire enforced. This does not prevent the same idiots who try to make an SQL query using format!() from doing so, as they won't use ValidSQLString anyway. If all strings fill you with such fear, probably General Purpose Programming languages aren't for you, maybe you will feel safer in WUFFS. It looks at first glance as though WUFFS has strings, but it actually doesn't, they're just a human readable label for WUFFS non-OK statuses (e.g. errors like "#bad Huffman code"). You can rest assured that your WUFFS code for processing a PDF can't have SQL injection for two reasons: 1. WUFFS doesn't have any strings to inject SQL into, and 2. WUFFS can't talk to an SQL database at all. In exchange for this safety you give up the ability to do anything outside the tiny sphere of interest of the language.
- adwn 5y agoHow is that related? The new format strings must be static literals because they're decoded at compile time. It is impossible to use them for any kind of code injection.
- pron 5y agoIt's not enough that they're static literals. The replaced variables aren't. The language should provide a way for the format string to enforce that the expressions are sanitised.
- paulgb 5y agoThe compiler doesn’t know if the string being templated is SQL, HTML, YAML, JSON, or something else, so it doesn’t know how to sanitize the string. IMHO generating code of any sort through string manipulation is a code smell, even if you ignore the security issues. There are better options like parameterized queries for SQL, macros for HTML, serde_json::json! for JSON, etc.
- pron 5y ago> The compiler doesn’t know if the string being templated is SQL, HTML, YAML, JSON, or something else, so it doesn’t know how to sanitize the string. That is the problem, and why security experts wouldn't let us add string interpolation to Java until we had a way to require the format string to say how it will be used and enforce proper validation. [1] Even if programmers should be more careful, the fact remains that this is the #1 security vulnerability caused by language features. The language should, if possible, make it easier for developers to avoid the mistake rather than make it easier for them to make it. [1]: https://openjdk.java.net/jeps/8273943 https://openjdk.java.net/jeps/8273943
- stubish 5y agoLooks like Java wants to skip format strings and jump straight to domain specific templating. I'm dubious doing this at the language level is a good idea. Picking a domain I'm familiar with, how do we handle this? SQL."SELECT * FROM orders WHERE \{mcol} LIKE '%' || \{qry} || '%'" How does the the templating engine know that 'mcol' needs to be quoted as an SQL identifier using ", and that qry either needs to be quoted as an SQL string using ' or a replacement made to use a bind variable? And since 'qry' is being used in a LIKE expression, do any % or ? characters in it need to be escaped, or should they be passed through? I guess you need to force hints in the template (force, because we are trying stop people being able to write buggy code) SQL."SELECT * FROM orders WHERE \{mcol:sql_id} LIKE '%' || \{qry:sql_likestr} || '%'" But none of that stops someone just doing this, which is the most common form of the bug: "SELECT * FROM orders WHERE " + mcol + " LIKE '%" + qry + "%'" To stop people throwing arbitrary strings at a database connection, risking SQL injection attacks and other buggy behavior, you just need to stop people throwing strings at a database connection. Instead having the driver only accept an object. Which does not rely on a templating engine at all. std::fmt does not encourage or make it easier to write code with SQL injection bugs, and if you want to stop them, the only way is to stop the driver accepting arbitrary strings and instead force developers to use an API to correctly generate their SQL. Which is exactly what most ORMs do, although they generally allow people to force arbitrary strings in for convenience or non-standard SQL stanzas.
- MathiasPius 5y agoThe Rust formatting macros depend on string literals because the macro expansion necessarily happens at compile-time, which means that it's not possible to override the actual format string at runtime, nor is recursive resolution possible. It's obviously still possible to implement the Display trait for a type in a way which makes it susceptible to code execution attacks, but that doesn't really have anything to do with string formatting.
- deleted 5y ago[deleted]
- rocqua 5y agoGiven that the logic for handling these strings is in the println macro, does that not limit these strings to 'being displayed'? I don't know enough about rust but I would imagine it is hard to println into a SQL statement. Hence it is not 'natural' to use this feature to build commands to be executed elsewhere.
- adwn 5y agoAlthough I strongly disagree with pron's point, format strings aren't limited to printing them to stdout. There's format!(), for example, which returns a String for further use, and you could even write a sql_query!() macro, which creates an SQL query from a format string: let query = sql_query!("SELECT * FROM users WHERE id = {}", user_id); query.execute(); Of course, this isn't a valid argument against format strings, because you could just as well write a function which does the same thing without language-level format strings (although it would be a lot less flexible than format strings): let query = sql_query("SELECT * FROM users WHERE id = @PLACEHOLDER@", user_id); query.execute(); EDIT: Actually, it would be possible to write a Rust macro sql_query! which takes a format string and creates a sanitized SQL query from it. That's because the actual string interpolation isn't built into the language, only the format_args! helper macro [1]. This macro doesn't insert the arguments into the format string, it only associates the placeholders with the arguments and returns a struct which can be used to create the output string – or a sanitized SQL query. [1] https://doc.rust-lang.org/std/macro.format_args.html https://doc.rust-lang.org/std/macro.format_args.html
- pron 5y ago> Of course, this isn't a valid argument against format strings, because you could just as well write a function which does the same thing without language-level format strings In that case, you should ask yourself what's the point of format strings at all? The answer is that they're more attractive because using them is more convenient. You want to make the more attractive feature the safer feature to draw people away from danger, not toward it.
- thinkharderdev 5y ago
- littlestymaar 5y agoI think this excerpt from your third link is key: > Constructing SQL queries or JSON expressions with templates is convenient, but is at risk for injection attacks. Improving mechanisms for constructing composite strings without similarly improving or enabling safer mechanisms for constructing queries would surely widen the attack surface. The fact that in many languages (and ecosystem actually, because it's not directly a language issue) building insecure queries (or HTML, or anything) is the simple way, and doing thing right requires specific thoughts from the developer[1] is what leads to so many injection attacks in the wild. I think this is mostly a cultural thing, and having being developed recently, way after the injections attacks have become ubiquitous, the Rust ecosystem has been focusing on providing better developer experience for the safe path than the vulnerable one. Diesel and SQLx use prepared statement by default, Serde serialize JSON without exposing strings at all to the developer, HTML templating libraries have sanitization built-in, etc. [1]: this example is also taken from your link: String query = "SELECT * FROM Person p where p.last_name = '$name'"; ResultSet rs = connection.createStatement().executeQuery(query); vs PreparedStatement ps = connection.prepareStatement("SELECT \* FROM Person p where p.last_name = ?"); ps.setString(1, name); ResultSet resultSet = preparedStatement.executeQuery()
- pron 5y agoThat's a valid point, but what you're saying is that the feature is only intended to help create log messages and the like, which raises the question of why add a feature with so little utility when it could have much greater utility? There's a missed opportunity here. But let me push on it a little more. I think that what you're seeing isn't so much a culture thing but a small ecosystem thing. Imagine that Rust takes off and in ten years there are 1M professional Rust programmers who use the language because that's the one chosen by their employer. You'll not have one JSON library (or whatever other format will be used then) but 50 and so on, and most programmers will not be experienced ones but relative novices (to programming in general). How likely would it be for them to generate JSON with format! ? So this feature provides a better user experience for the less safe path. A feature should be designed with the next 20 years in mind.
- 5y ago
- Latty 5y agoImplementing string formatting the right way can actually solve this problem, of course. In modern JS/TS, for example, string formatting isn't just used for constructing strings, and it can limit input types as well. This means you can construct some kind of prepared statement or similar object with a string format, and it will force the correct handling of potential injections. Of course you can still just format a string and shove it in, but I'd argue that a good SQL library should simply not accept pure string queries and require the format that guarantees injection-safety.
- avgcorrection 5y agoI don’t see why this development makes this more likely to happen compared to string concatenation. The log4j fiasco didn’t happen because of Java (wink) but because of a plain invulnerable feature. I don’t think Rust makes that more or less likely to happen.