Live data from Hacker News

Be Careful with Python's New-Style String Format

lucumr.pocoo.org

121–130 of 155 posts

Re: Be Careful with Python's New-Style String Format

#121
post #9

No, Rust does not have the ability to access any variable in the program via a format string. Rust has this: format!("{argument}", argument = "test"); // => "test" That's just named arguments to the format. Also, that's a macro; it's expanded at compile time. Python's approach is lame. It should have used something with a limited list of named arguments, or maybe a dict.

> It should have used something with a limited list of named arguments, or maybe a dict. That's what "old-style" python interpolation did :) You could do |"hi my name is %s and I live in %s" % (name, place)| or use named arguments with a dict (|"hi my name is %(name) and I live in %(place)" % {"name": name, "place": place}|). I like JS format strings -- you can write arbitrary code in them, but they are compile time…

> I like JS format strings -- you can write arbitrary code in them, but they are compile time only

Genuinely curious: what exactly does "compile-time" mean in the case of JS? Is it like C macros (modifying the source code before parsing)?

Re: Be Careful with Python's New-Style String Format

#122
post #116

Earlier quoted context omitted.

The author discovered this exploit in the template engine he wrote (Jinja2).

While the vuln is there, it's a rather obscure way of using the engine in the first place, so in "good" templates it shouldn't be exploitable. See the example in the release: https://www.palletsprojects.com/blog/jinja-281-released/

Sure, but like tedunangst pointed out, there are valid reasons to allow users to edit those templates, in which case you can't assume they'll be "good".

Re: Be Careful with Python's New-Style String Format

#123
post #107

Earlier quoted context omitted.

The first bell added is likely to be field access.

Which immediately becomes a security risk because classes can overload __getattr__ and __getitem__. So now you're executing random code. Of course, %s does the same thing (__str__) but at least %s doesn't take any arguments.

The assumption behind string.format and friends is that people who override __getattr__ and __getitem__ know what they're doing. Which is probably a reasonably safe tradeoff in order to allow attribute access.

I'm not worried about executing arbitrary code, because it's not user-supplied and it's normal for a templating library to access attributes of objects you pass to it (if it was unsafe you shouldn't have done that). This is more of a snafu with the number of sensitive and powerful attributes available by default on Python objects.

Re: Be Careful with Python's New-Style String Format

#124
post #121

Earlier quoted context omitted.

> It should have used something with a limited list of named arguments, or maybe a dict. That's what "old-style" python interpolation did :) You could do |"hi my name is %s and I live in %s" % (name, place)| or use named arguments with a dict (|"hi my name is %(name) and I live in %(place)" % {"name": name, "place": place}|). I like JS format strings -- you can write arbitrary code in them, but they are compile time…

> I like JS format strings -- you can write arbitrary code in them, but they are compile time only Genuinely curious: what exactly does "compile-time" mean in the case of JS? Is it like C macros (modifying the source code before parsing)?

No, it's not that, I was using the term loosely.

I meant that you cannot use a string value as a format string. It only exists in the form of a literal, and it gets evaluated when the interpreter evaluates that literal. You cannot store it and format it later.

Re: Be Careful with Python's New-Style String Format

#125

Earlier quoted context omitted.

I think "it's not obvious how to sanitize input" is the main point here --- one advantage of the %-style format strings, they're easier to parse and escape.

But we don't want the string escaped. We want it interpreted.

We want it interpreted, but we don't have to let the language handle it directly. A much safer way is to define our own syntax for this and interpret it in our code, with that code only having access to a limited set of safe substitutions.

For instance, suppose we want to allow user formatting of contact information. A contact entry has a name, address, phone number, and email address. The user supplies a template string using %_NAME_, %_ADDR_, %_PHONE_, and %_EMAIL where they would like the name, address, phone number, and email address substituted, respectively.

I'd probably be doing this in Perl, and I'd do it something like this:

  sub format_contact
  {
    my($template, $name, $addr, $phone, $email) = @_;
    my %val = (name => $name, addr => $addr,
            phone => $phone, email => $email);
    $template =~ s/%_([a-z]+)_/$val{lc($1)}/gi;
    return $template;
  }

Re: Be Careful with Python's New-Style String Format

#126
post #92

Earlier quoted context omitted.

I would have said that SQL injection is caused by emitting unescaped user input to your SQL server

If I call eval(string) am I emitting unescaped user input to the eval function? I guess the definition of "injest" here is reading bytes off the wire?

If string is unescaped user input, then yes, you are.

Re: Be Careful with Python's New-Style String Format

#128
post #121

Earlier quoted context omitted.

> I like JS format strings -- you can write arbitrary code in them, but they are compile time only Genuinely curious: what exactly does "compile-time" mean in the case of JS? Is it like C macros (modifying the source code before parsing)?

No, it's not that, I was using the term loosely. I meant that you cannot use a string value as a format string. It only exists in the form of a literal, and it gets evaluated when the interpreter evaluates that literal. You cannot store it and format it later.

Ah, I see. Thanks!
Post reply on HN