user1544337
user1544337

Reputation:

Correct and secure way to have PHP execute a shell script

I'm working on an application where people (anyone) can upload code (Java and possibly C(++) to start with) that will be compiled and run on the server. This is of course a huge security risk and it will be necessary that all this is sandboxed properly. This sandboxing is out of the scope of this question though. Assume that is have been taken care of.

Next to this, there will be functions in the system that will rely on shell commands and PHP's exec(), shell_exec(), etc. functions. The commands that will need to be executed aren't very many, mainly java(c), gcc, g++, etc. It would be fairly easy to make a list of the commands that we would need, if necessary. It will not be possible for a user to execute other commands than the once we decide. For example, someone uploads some java code and asks the server to compile it. Then the server will run javac. The user's input can only change javac's parameters (that are escaped using escapeshellarg()).

I'm wondering what security precautions I should still take. I was planning to use PHP's safe mode, so that only files in the safe_mode_exec_dir could be executed. I was also planning to have the ownership of the shell files set to root:www-data so that www-data cannot change permissions or ownership, and furthermore to have the permissions something like rwxr-xr-- so that www-data cannot modify the file. However, safe mode has been removed from PHP as of 5.4.0. What is the current way to do stuff like this?

Would it be safer to have these shell commands run by an entirely different user, that doesn't even have access to any other directories than safe_mode_exec_dir? How would I then go about that?

Another option would be to have PHP only maintain a list of things that need to be done, and let a cronjob run every minute or so, by a restricted user, to walk through the list and perform the necessary operations. Would that be a safer approach? Because of the up-to-one-minute delay, I would prefer to do this from PHP directly.

I have full access to my server but due to policies I am not allowed to use virtualisation.

Upvotes: 9

Views: 4468

Answers (4)

OscarGarcia
OscarGarcia

Reputation: 2114

Secure mode in PHP is not as secure as you might think.

Functions like safe_mode_exec_dir limits the directory (or directories) from which you can run an application or script, but this application or script could call another outside that path (or paths).

Everyone recommends you use LXC and I agree with all of them. It is the best solution to your problem, but I'll put my two cents.

You could prepare a LXC container configured for each user of your application:

Create a local user for every user (separating privileges to avoid access or modifying files from another user) and login into your LXC container using SSH to execute your tasks inside it:

This solution will allow you to execute tasks as quick as you login into your SSH account without waiting for creating/starting a LXC container every time you need it to execute your tasks with a little overhead maintaining it running.

You could send and receiving files to/from the LXC using SCP:

And you could "keep clean" from background processes sending (using root user) a killall (-9) with argument "-u", ect or using "pkill -u ..." or drastically restarting the container.

You could even recreate the user (deleting its home directory) before each time you want to run the task (and before uploading necessary working files to LXC container).

Best regards.

Upvotes: 1

Adam
Adam

Reputation: 18807

You want to create a service which enables to compile java program and launch them.

I undestand that your question is not about how to launch those program securely because you assume that it has been taken care of.

So you want to know how to securely launch a shell script, in which you will give the name of the source code, and the arguments to javac.

First, you have many things to do.

The fact that you want to use system calls implies that you will allow exec in your whole Virtualhost. So if your FTP password, one of your PHP files, or anything is compromised, an attacker can upload a script, a binary program and execute it.

  1. You have to have your web site partition and temporary folders used by PHP in noexec mode in fstab
  2. You have to limit the files you can access within PHP with open_basedir restrictions, which has survived from the end of safe_mode. You will then allow with open_basedir the directory (which of course does not have the noexec flag)
  3. The directory containing your script and the script will have root.www-data permissions, and will not be writable by www-data
  4. You will have to protect the arguments passed to the script (which you have done with escapeshellarg) and any potential injection in the filename of the java file (but we assume that it will be renamed before being transmitted in order to avoid filename collision in the different java files you will received)

Upvotes: 1

Johnride
Johnride

Reputation: 8736

In my opinion, a good and simple way to get a secure environment to compile and run scripts is an LXC container.

You say you cannot use virtualization but technically it is just process isolation. Like a chroot on steroids. I have a container host that is itself a VM and I've got no issue with it so far. Really LXC is not virtualization and should suit your needs.

Here are some intro links :

For example, you can create a template container per game with all the required libraries to compile and run the uploaded code. This template can be secured and limited as you want for cpu, ram and disk IO. I think it is also a good idea to turn off the network either with lxc.network.flag = down or lxc.network.type = empty

Then, when the code is uploaded, you can clone the template container, put the code in it and have it build and run the code.

All this would be done by a shell script called from php, or by a succession of php system calls but that does not sounds good.

Using unprivileged containers is a must for the kind of stuff you want to do as it provides an additional security layer.

I recommend using Ubuntu 14.04 as the LXC host. I think that a tweaked busybox template with the proper tools to compile and run the code is the lightest container you can get.

Here is the idea I get :

// clone the prepared template
lxc-clone -o myTemplate -n newContainer

// put the code archive in the new container
cp path/to/code path/to/container/and/where/you/want

// Start the container as a daemon
lxc-start -n newContainer -d

// Then launch the right script for the type of code in the container
lxc-attach -n newContainer -- su containeruser -c /path/to/script.sh

So the small job is to create the template with the required libs. The other job is to write the script that is called in the end.

Good luck with your project, I hope this helps.

Upvotes: 6

Bryan Devaney
Bryan Devaney

Reputation: 188

realistically, when giving people this kind of freedom, the only real safe step is to spin up a new virtual instance for each user session and 'burn' it as soon as the session closes. If you want some sort of permanence, cat their input, and run it on a new instance next time they visit. even this has HUGE scope for being exploited but damage to your system should be limited.

Upvotes: 1

Related Questions