[Python-Dev] PEP 215 redux: toward a simplified consensus?

Barry A. Warsaw barry@zope.com
Mon, 25 Feb 2002 16:51:00 -0500


>>>>> "JE" == Jeff Epler <jepler@unpythonic.dhs.org> writes:

    JE> Imagine that you have:
    >>    def print_crypted_passwd(name, plaintext, salt="Xx"): crypted =
    >> crypt.crypt(plaintext, salt) print _("""%(name)s, your crypted
    >> password is %(crypted)s.""") % locals()

    JE> and that some crafty devil translates this as msgstr
    JE> "%(name)s, your plaintext password is %(plaintext).  HA HA HA"

    JE> i.e., the translator (or other person who can influence the
    JE> format string) can access other information in the dict you
    JE> pass in, even if you didn't intend it.

That's a very interesting vulnerability you bring up!

In my own implementation, _() uses sys._getframe(1) to gather up the
caller's locals and globals into the interpolation dictionary,
i.e. you don't need to specify it explicitly.  Damn convenient, but
also vulnerable to this exploit.  In that case, I'd be very careful to
make sure that print_crypted_passwd() was written such that the
plaintext wasn't available via a variable in the caller's frame.

    JE> Personally, I tend to view this as showing that using %
    JE> locals() is unsanitary.

Nope, but you have to watch out not to mix cooked and raw food on the
same plate (to stretch an unsavory analogy).
    
    JE> But that means that the problem is in using the locals()
    JE> dictionary, a problem made worse by making the use of locals()
    JE> implicit.

    JE> (And under $-substitution, if locals() is implicit, how do I
    JE> substitute with a dictionary other than locals()?

def print_crypted_passwd(name, crypted):
    print $"$name, your crypted password is $crypted"

print_crypted_passwd(yername, crypt.crypt(plaintext, salt))

-Barry