Issue
I have a web server running Apache 2 on Raspbian Stretch. It is going to be a programming contest website, where users can send code via a HTML form, that sends their source code to PHP via a POST request. PHP then runs (using exec()
) a Python script with arguments such as the submitted code path. The script then executes the code (using subprocess.run()
) with a custom input and compares it to an expected output. All of that works just fine.
However, I want to make sure no one is going to send malicious code to overwrite files such as index.php, or read the expected outputs, for example. I'd like to know if there is any way to prevent an application that is being executed by subprocess.run()
from reading, creating and writing to files other than stdin
, stdout
and stderr
.
I have tried using Docker but to no avail, I want to avoid Docker if possible as it will run on a Raspberry Pi, so RAM usage is a bit of an issue here.
I am considering using a chroot jail, but I am still looking for other less complicated ways of doing that.
This is the PHP code I'm using. It calls the Python 3 code verifier (variables are retrieved from a HTML form and from a SQL query, those are not relevant):
$cmd = "python3 verify.py $uploadedFile $questionID $uploadedFileExtension $questionTimeLimit 2>&1";
And this is the Python 3 code that executes the submitted code:
def run_cmd(args, v_stdin, timelimit=10):
p = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=v_stdin, encoding='utf-8', timeout=timelimit)
return p.stdout.strip(), p.stderr.strip()
# ...
out, err = run_cmd(['python3', sys.argv[1], 'example-input.in', int(sys.argv[4]))
# ...
The combination of both programs works correctly. It runs the code with a dummy input, compares outputs and returns a status to PHP. However, the code could include malicious content:
open('/var/www/html/contest/index.php', 'w').write('oops!') # rip index.php :(
All I need is a way of executing the user-sent code in a way that its attempts to read or write to files (other than stdin
, stdout
and stderr
) are denied.
Any thoughts?
Solution
doing this securely, to put it simply, is difficult. it's relatively easy to escape even a chroot jail if you're not really careful about how you set it up. basically the Unix security model isn't built to make this sort of thing easy and it's assumed that things are mostly cooperative
docker would probably be my suggestion, but there are other lighter weight solutions like chroot (but they'd probably still have the ability to do naughty things with the web server's network connection) or maybe something like firejail
with docker you'd probably want to create a single minimal docker image/container containing Python and whatever libraries are appropriate. you'd then use volumes to make the user supplied code appear inside the VM at runtime. you don't want to be creating containers all the time, that would entail lots of cleanup work
see https://security.stackexchange.com/q/107850/36536 for some more info on using docker as a sandbox, basically there are still lots ways out of it unless you're careful
Answered By - Sam Mason Answer Checked By - Cary Denson (WPSolving Admin)