Escaping Code for Different Shells
- by Jon Purdy
Question:
What characters do I need to escape in a user-entered string to securely pass it into shells on Windows and Unix? What shell differences and version differences should be taken into account? Can I use printf "%q" somehow, and is that reliable across shells?
Backstory (a.k.a. Shameless Self-Promotion):
I made a little DSL, the Vision Web Template Language, which allows the user to create templates for X(HT)ML documents and fragments, then automatically fill them in with content. It's designed to separate template logic from dynamic content generation, in the same way that CSS is used to separate markup from presentation.
In order to generate dynamic content, a Vision script must defer to a program written in a language that can handle the generation logic, such as Perl or Python. (Aside: using PHP is also possible, but Vision is intended to solve some of the very problems that PHP perpetuates.) In order to do this, the script makes use of the @system directive, which executes a shell command and expands to its output. (Platform-specific generation can be handled using @unix or @windows, which only expand on the proper platform.) The problem is obvious, I should think:
test.htm:
<!-- ... -->
<form action="login.vis" method="POST">
<input type="text" name="USERNAME"/>
<input type="password" name="PASSWORD"/>
</form>
<!-- ... -->
login.vis:
#!/usr/bin/vision
# Think USERNAME = ";rm -f;"
@system './login.pl' { USERNAME; PASSWORD }
One way to safeguard against this kind of attack is to set proper permissions on scripts and directories, but Web developers may not always set things up correctly, and the naive developer should get just as much security as the experienced one. The solution, logically, is to include a @quote directive that produces a properly escaped string for the current platform.
@system './login.pl' { @quote : USERNAME; @quote : PASSWORD }
But what should @quote actually do? It needs to be both cross-platform and secure, and I don't want to create terrible problems with a naive implementation. Any thoughts?