1

I have to deal with a legacy project, where a PHP API creates a CSV export from MySQL and provides the file to download it later.

The problem is that the API can't copy the CSV file to the directory, from which it can be downloaded (through Apache)

The process goes like this:

  1. Someone clicks a button on a website to create a csv export.
  2. The API creates a SQL statement and executes it with shell_exec(mysql ...);
  3. The Database creates a CSV file with the data and puts it into /tmp/
  4. The API copies the file into a dedicated download directory for apache
  5. The API sends a mail with a download link to the user, who clicked the button.

Everything of the above works except step 4.

What I found in the API was:

$exportCommand = "mysql ....";
// mysqlExportTmpDir = /tmp
// mysqlExportTargetDirectory = download directory for apache
$moveExportFileCommand = "cp ".$this->mysqlExportTmpDir."/$filename ".$this->mysqlExportTargetDirectory;
shell_exec("( $exportCommand; $moveExportFileCommand; $sendNotificationCommand) &");

As I said the MySQL Command works, the mail notification works, but the cp command doesn't.

When I look into the logs I see this error:

cp: cannot stat '/tmp/610278f414d84.csv': No such file or directory

I also tried shell_exec("ls /tmp/*.csv"); to check if the older export files are visible to the API, but then I get this:

ls: cannot access '/tmp/*.csv': No such file or directory

There are definitely CSV files in there. Therefore I thought it might be a permission problem and tried to execute this in the terminal: sudo -u www-data cp /tmp/610274ad5a8a5.csv /path/to/api/download. I copied the cp command from the logs, therefore it should be exactly the same.

That works. Now I'm confused because www-data runs Apache and with shell_exec("whoami") I also get www-data from the PHP API. Maybe the sudo -u www-data cp ... command doens't work as I expected (I expected to have the same permissions as that user). But I don't know.

Does somebody know, why PHP can't access the csv Files in /tmp and copy them to a different directory?

The target directory belongs to the group www-data and files in there can be downloaded from a browser.

I know the approach to export data from a database is strange, but it seemed to work until a year ago. Now there is no one from this project in my company anymore and I don't know what changed.

5
  • Check if SE Linux is running on the machine. You can do this by logging into the machine and running sestatus. What SE Linux does is add another layer of security on top of the basic Linux stuff like permissions. If the output includes the word, enforcing in it, try to disable it by using permissive mode like this: sudo setenforce permissive. If the file copies then, you know it is an SE Linux issue. If not? You can re-enable SE Linux like this: sudo setenforce enforcing. There are ways to have enforcing behavior and permit stuff like this. But that would need to be a fuller answer. Jul 29, 2021 at 15:16
  • 1
    The command sestatus wasn't found, but thank you for the idea.
    – JWo
    Jul 29, 2021 at 15:25
  • Good to know. Another idea: Why is shell_exec being used at all in the code? Why not use rename which I the PHP version of mv or cp on the command line? The command would be: rename($this->mysqlExportTmpDir."/'.$filename, $this->mysqlExportTargetDirectory."/'.$filename); In general, one should avoid using shell_exec in PHP and use native functions like rename instead. Jul 29, 2021 at 18:44
  • 1
    I know it's weird and I don't know why it is used. I guess because the mysql export is also done with a shell command. Those who wrote this left the company some time ago. I already tried PHP's copy(), which resulted in a similar error. I'll have a look at rename() in the morning. Thanks for input. I don't really know much about PHP.
    – JWo
    Jul 29, 2021 at 19:24
  • 1
    @Giacomo1968 nope. rename() results in the same error message. rename(/tmp/6103ae729858b.csv): failed to open stream: No such file or directory, but the file exists, when I type file /tmp/6103ae729858b.csv into the terminal. I also tried to chown www-data on that file. Didn't help either. Permission are 666, when MySQL puts the files into that directory.
    – JWo
    Jul 30, 2021 at 7:58

1 Answer 1

1

There is a private tmp directory for Apache located in /tmp/*apache2.service*/tmp. This exists because of the private tmp setting in /lib/systemd/system/apache2.service. I found a little article here.

Whenever I call copy(/tmp/....) in the PHP script it will look in the Apache specific private tmp directory. Apache can't see contents of /tmp with this option enabled.

Therefore I can only disable this option or make MySQL dump the CSV files elsewhere.

1
  • Excellent self-solved question! My recommendation? Simply disable the private tmp. That is security feature that attempts to isolate Apache’s tmp from the main /tmp/ but honestly it seems to get in the way. By disabling it you are really not putting your system in deeper risk. But if you are queasy about doing that, creating a common directory to save the files to in a directory like /opt/ would be another solution. Something like /opt/csv_exports/ and such and ensure that Apache has read and write access to that. Still believe that shell_exec for file copying should be changed. Jul 30, 2021 at 17:02

You must log in to answer this question.

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