perlshellsecuritysshdirectory-traversal

How to safely write to a remote file using Net::OpenSSH


Suppose an application server receives a binary blob from a client and writes that to a file on a remote host.

client -(HTTP)> application server -(SSH)> remote data host

Given that the file name is user input coming from the client, what has to be done on the (app) server to protect against attacks (directory traversal attacks)?

The user account used to connect to the remote host has a home directory /home/datauser and should not access anything outside of its home.

This is the code:

# User input
my $name = ...; # bob's car.jpg
my $dir = ...; # pics/vacation "country"/

# Slashes are illegal
die "Illegal dir" if ($dir =~ /\.\.\//); # ../ is an attack
die "Illegal name" if ($name =~ /\//); # / in file name is an attack

# Escape single quotes
my $safe_path = $dir.$name;
$safe_path =~ s/'/'\\''/g;

# my $ssh = Net::OpenSSH->new(...);
my $root_dir = '/home/datauseruser';
my $cmd =
    "cd '$root_dir' && ".
    "cat >>'$safe_path'";
$ssh->system({stdin_data => $bytes}, $cmd);

Is this a good approach? Are there more vulnerabilities?

Note: Random access is required, so scp cannot be used.


Solution

  • The mayor potential security issue with sending things through an SSH connection is that the remote shell is unavoidable. In other words, every time you ask SSH to execute a command, instead of just fork+exec'ing the given command (@CMD), it actually calls a shell ($LOGIN_SHELL -c $CMD), so command arguments must be properly quoted.

    You are are already doing the quoting yourself, and the code seems correct to me. But anyway, you can let Net::OpenSSH do that for you and have something less to worry about:

    my $safe_path = $ssh->shell_quote($path);
    $ssh->system("cat >>$safe_path");
    

    Finally, actually, there is a way to avoid the shell: using SFTP.

    Net::OpenSSH integrates with Net::SFTP::Foreign, which you can use to access the remote file system in an easy manner.