I'm not sure exactly how you are performing your validation, so I can't give detailed steps. However, there is a "best practice" here that I can direct you in anyway.
Principle of Least Privileges
Like all things, it's the difference between blacklisting and whitelisting. The latter is rarely successful. Whitelisting (or more generally, the Principle of Least Privilege) is always the way to go. In essence what this means is that you don't want to try to stop the user from using some list of banned characters. Instead you want to verify that the file/directory that they are actually trying to copy is in the list of permitted files/directories.
Work with the absolute path
They way I do this in similar situations is by taking the user input and "canonicalizing" it - let the OS convert it from a relative path to an absolute path, and then verify that the final, real, absolute path is actually an allowed destination. Once you have an absolute path, verifying that it is an allowed direction is quite easy. In the case of linux I would use something like realpath which does exactly that. I'm not sure if I can guarantee that this approach might not still have edge cases (where realpath
ignores an alternate encoding that scp
doesn't, potentially leading to a missed exploit), but I suspect the risks will come down substantially. How to actually implement this kind of check in your particular environment is another matter, and I can't help you with that one.
Small Gotcha
Also, you'll have to watch out for symlinks - if someone accidentally puts a symlink in your application that points to /
, then scp
will happily follow it and try to copy over your entire filesystem.
Another Approach
Of course all the above could be fixed even more easily by having a process run on the server that packages everything and then moves it off into a /backup
directory to be copied out (per the comment from @domen)