Secure file writing (escaping characters from the file name)

I have a cgi script that writes files on the filesystem. The files are
provided by the users. I need to save them with (almost) the same name
as the user requests. What characters I need to escape?

This is on linux. Right now the file: “Mick J. / Chris Jagger -
Racketeer Blues” does not get saved because of the “/” character. I
don’t escape any characters now. I want to keep as many of the original
characters in the file name as I can. For the characters that cannot be
escaped, I suppose I need a translation table… to figure out what was
the original filename.

Any pointers? More importantly about escaping special characters, and
avoiding directory traversal.

On 9/12/07, Constantin G. [email protected] wrote:

Any pointers? More importantly about escaping special characters, and
avoiding directory traversal.

Hi Constantin,

Maybe instead of escaping/removing any character in the filename you
can store the file with a unique name and have an index with the user
supplied filename and the name of the file in the file system?

Luis P. wrote:

On 9/12/07, Constantin G. [email protected] wrote:

Any pointers? More importantly about escaping special characters, and
avoiding directory traversal.

Hi Constantin,

Maybe instead of escaping/removing any character in the filename you
can store the file with a unique name and have an index with the user
supplied filename and the name of the file in the file system?

The files are also shared over the network with samba, so they need to
have a meaningful name. That’s why I need to escape just the “bad
characters” and keep most of the other info in.

Constantin G. wrote:

The files are also shared over the network with samba, so they need to
have a meaningful name. That’s why I need to escape just the “bad
characters” and keep most of the other info in.

The unique caracter not acceptable for the filename in unix is /. So,
why you not replace this caracter ‘/’ to, for example, a blank caracter
‘’?

Felix W. wrote:

If you only need this to work on POSIX compatible filesystems, all you
need
to remove are “/” (slash character, as it separates path components) and
\000 (nul, as it terminates strings in many languages) as POSIX file
names
can accept all other ASCII characters.

That’s interesting…

irb(main):001:0> File.open(“aa.php\000continue.jpg”, “w”)
=> #<File:aa.php>

Creates the file aa.php. Welcome remote code execution vulnerabilities.

irb(main):002:0> File.open("…/aaa", “w”)
=> #<File:…/aaa>

Directory traversal. Creates “aaa” in the parent directory.

irb(main):002:0> File.open(“bbb\bbb.tst”, “w”)
=> #<File:bbb\bbb.tst>

Linux accepts it. I guess it can be a directory traversal in windows.
Samba exports it as BRRFFZ~N.TST

Any pointers? More importantly about escaping special characters, and
avoiding directory traversal.

Posted via http://www.ruby-forum.com/.

If you only need this to work on POSIX compatible filesystems, all you
need
to remove are “/” (slash character, as it separates path components) and
\000 (nul, as it terminates strings in many languages) as POSIX file
names
can accept all other ASCII characters. So that’s simply a matter of:

irb(main):001:0> str = “aaaa/\000aaa”
=> “aaaa/\000aaa”
irb(main):002:0> str.tr(“/\000”, “”)
=> “aaaaaaa”
irb(main):003:0>

To make sure that there’s no trickery with trying to change directories,
simply use:

irb(main):003:0> File.basename(“…/…/test”)
=> “test”
irb(main):004:0>

Hope that helps,

Felix

need

irb(main):001:0> File.open(“aa.php\000continue.jpg”, “w”)
=> #<File:aa.php>

Creates the file aa.php. Welcome remote code execution
vulnerabilities.

That is why you’d want to replace the nul character with a zero length
string, as I indicated. That would concatenate the above with
“aa.phpcontinue.jpg”, which would not execute (unless you have a really
weird webserver setup).

irb(main):002:0> File.open(“…/aaa”, “w”)
=> #<File:…/aaa>

Directory traversal. Creates “aaa” in the parent directory.

That’s why you’d replace the filename with File.basename(filename) as I
indicated: that will strip out all but the last pathname elements, where
the
last one is just the filename.

irb(main):002:0> File.open(“bbb\bbb.tst”, “w”)
=> #<File:bbb\bbb.tst>

Linux accepts it. I guess it can be a directory traversal in windows.
Samba exports it as BRRFFZ~N.TST

Posted via http://www.ruby-forum.com/.

True. Just because it’s a valid POSIX filename doesn’t mean Windows will
display it correctly. It will, however, display something and be able to
access the file.

Felix

[Constantin G. [email protected], 2007-09-12 19.39
CEST]

Any pointers? More importantly about escaping special characters, and
avoiding directory traversal.

You can “semi-URL-escape” the filenames. I mean, use the same method as
CGI::escape, but with more characters allowed. Just adapt the original
function, adding more characters to the regex to allow them, and taking
out
the last #tr (spaces to “+”)). It is in cgi.rb:

def CGI::escape(string)
string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
‘%’ + $1.unpack(‘H2’ * $1.size).join(‘%’).upcase
end.tr(’ ', ‘+’)
end

Later, you can easily restore the original filename with CGI::unescape.

For Unix/Linux you can let pass any character except “/” and “\000”; for
Windows/Mac OS, here is a list of forbidden characters:
http://www.xvsxp.com/files/forbidden.php

Good luck.