Need to execute the following line from PHP:

$res = shell_exec('sudo sh /home/nicklas/cronjobs/make_account.sh username password');

The problem is nothing happens on execution. If i try to echo $res it comes out blank. I've tried using system() also, same result. I'm guessing it doesn't work because i need to run the scrip with root access, and the www-data user doesn't have that by default. I added the following line to /etc/sudoers in hope of getting access:

www-data ALL=(ALL:ALL) NOPASSWD: /home/nicklas/cronjobs/make_account.sh

But no success. I've tried restarting apache inbetween, doesn't change anything.

Am i missing something?

  • 1
    if you use shell_exec() or similar, remember to use escapeshellcmd() or escapeshellarg() to escape any shell metachars in the data.
    – cas
    Commented Sep 8, 2012 at 3:25

2 Answers 2


I strongly disagree that running something with higher privs from a web server is always a bad idea.

The security problems from a web application come from trusting the user-supplied data, not from the exact process that's using that data.

In other words, if you don't validate and sanitise your data, it's no safer to pass it to a root-owned process via named-pipe than it is to write a wrapper script to process that data and allow the www-data user to run it via sudo.

Actually, most of the processing/validation/sanitising of data should be done before the data is passed to the sudo script, which should a) do a final check on the data, and b) perform only the operations that need higher privs.

In fact, I'd argue that it's possibly safer to do the latter because it's a simpler solution that's easier to understand....the more complicated you make something, the more likely you are to make a mistake.

Whichever method you use (named-pipe, sudo wrapper, or whatever) the crucial thing is that you examine the user supplied data and make sure it fits very narrowly defined criteria before doing anything with it.

There are numerous articles and howtos on CGI security on the web, including several here on the stackexchange sites, but some of the common themes are:

  • regard the data as 'tainted' and untrustworthy until you've checked it and/or modified it to make it safe.
  • check the data - e.g. test if it matches a regexp of allowed or prohibited characters. safest option is to abort if there's anything odd, otherwise transform it with a regexp or something to remove potentially unsafe elements.
  • always quote the data if you pass the data to an external shell script. e.g. the shell script should use double-quotes around the variables.
  • similarly, when inserting data into database you should use a database library that supports placeholder values rather than relying on escaping or quoting. Here is a humorous illustration of why.

I could go on, but there's too much to summarise in an answer here - CGI Security is a broad topic. Try a google search for CGI Security or Validating CGI Input

With your 'I don't care because it's only me using it' attitude to security, I kind of feel like I'm giving you a loaded shotgun to blow off your feet with, but shooting yourself in the foot is a valuable learning experience. Here's a simple script to create a user account. It requires root privs.

The script relies on adduser, which is available on debian and debian-derived systems and maybe others. modify to use useradd instead if it's not on your system.

#! /bin/bash 

# make the script abort on any error
set -e


[ -z "$U" ] && echo "Error: No username provided" >&2 && exit 1
[ -z "$P" ] && echo "Error: No password provided" >&2 && exit 1

# simple check - only allow lower-case letters and digits in usernames.
[ "$U" !~ '^[a-z0-9]*$' ] && echo "Error: Invalid Username" >&2 && exit 1

# create user if they don't already exist
if ! getent passwd "$U" > /dev/null ; then

   # create the user using adduser, must provide the gecos field 
   # and disable the password so adduser doesn't ask for them. 
   adduser --gecos "$U" --disabled-password "$U"

   # now change the password
   echo "$U:$P" | chpasswd
    echo "Error: Username already exists" >&2
    exit 1

Save the script somewhere, and make it executable with chmod.

To allow www-data to run it as root without a password, edit /etc/sudoers with visudo and add the following:

Cmnd_Alias APACHEADDUSER = /path/to/makeaccount.sh

  • The problem is not about making the data secure by sanitizing/validating it before using it. I'm not concerned at all about the security since only i will be using it. Either way i still sanitize and validate the data before passing it on to the shell script, so that should work fine. The problem is that i don't know to execute a shell script with root access (without entering password) from a user that has no root access. You mentioned a "wrapper script", can you explain a bit more about that?
    – qwerty
    Commented Sep 8, 2012 at 14:52
  • I'll add an example to my answer.
    – cas
    Commented Sep 8, 2012 at 21:53
  • Sorry, i didn't mean to sound ignorant, i know security is very important but it just wasn't what i was looking for. I do very much appreciate your help and will keep your suggestions in mind. I already have a bash script that creates a user, but the last code block sounds very interesting. I've tried to add www-data to the sudoers list, but it didn't seem to work (as written in the question), but i will try the code you suggested.
    – qwerty
    Commented Sep 8, 2012 at 22:52
  • sorry, my comment was meant as a slightly humorous warning, not as a criticism. have you checked your apache error log to find out whether it's your script failing or the sudo failing? the sudoers entry you posted looked fine, but also check your auth.log.
    – cas
    Commented Sep 8, 2012 at 22:58
  • My bad, heh. I'm new to Linux, so i wasn't even aware that log existed, although i assumed there was a log somewhere. The log says auth could not identify password for [www-data] so i'm pretty sure it's sudo failing. I even tried doing system("sudo ls"); from PHP just to see if sudo worked. It didn't. Anyways, i added the code to /etc/sudoers as you advised but it still doesn't seem to be working. I have triple checked the path, so that's not the problem. If it helps, here is my sudoers file: pastebin.com/FNhdaJzL
    – qwerty
    Commented Sep 9, 2012 at 13:12

For security reasons you should never try to execute something with the user www-data with more privileges than it naturally has. There was a reason why some times ago the Apache access was moved to www-data. If you need to do something on your machine as root - and changing passwords or creating accounts is this 'something' - you should build an interface. Let the php-script put something somewhere and scan this via scripts executed from root and handle it there. You could f.e. create a file containing the users to add in a directory where www-data has access, and then perform this via root-cronjob every 5 minutes (or less) and move the file to a done-folder with timestamp to have control over what is happening.

  • I already knew it was a security breach, but since i'm just experimenting on a development server to learn i was going to let it pass. But fair enough, i'll do it the way it should be done. Your idea sounds fine except i need to create the user instantly, it has to be done as soon the PHP script goes off. Any ideas how to solve that? Thank you btw!
    – qwerty
    Commented Sep 5, 2012 at 10:59
  • Sounds like this could be something for a named pipe. Named pipes are typical methods for inter-process-communication on Unix (and meanwhile on other systems too). For an introduction you may read en.wikipedia.org/wiki/Named_pipe
    – numchrun
    Commented Sep 5, 2012 at 12:13
  • I've been fibbling with named pipes for some time now and i really can't figure out how to actually use it for my purpose. I understand the principle of named pipes, but can't apply it to my needs. This is how i imagine it would work: Send data (in my case username and password) to named pipe from PHP, no root needed. I have another process running (with root access) which constantly reads from the pipe, and then executes the script with root access. Am i on the right track? If so, my question is, how do i create that other process that runs with root access and constantly reads from the pipe?
    – qwerty
    Commented Sep 7, 2012 at 11:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .