Search code examples
phpwindowsdownloadexecode-signing

PHP download removes digital code signature


I have a php web application that allows our users to download our software installer setup.exe.

We've just started signing our code, both the the installer and the applications within it, which is all fine. However when I upload this installer to our web server, and download it through the php web application, setup.exe is no longer digitally signed! It's as if it was never signed in the first place.

Here's what I've tried:

  1. [right click] -> [properties] shows that setup.exe is not code signed. However setup.exe is the expected version.
  2. I ran the installer, and installed our software. The applications that are installed ARE code signed, and the expected version matching the installer.
  3. I tried downloading setup.exe directly from the web server through the cpanel File Manager and this is fine - setup.exe is digitally signed and the expected version.
  4. Attempts to download setup.exe from the web site via different browsers (firefox, edge) result in the same behaviour.

EDIT

  1. I also tried moving the setup.exe file to a public area on the site, and downloading it directly through the browser - works perfectly. All I can think is that the issue has to be in how PHP is serving the application to the browser.

I thought the HTTP headers or the PHP download function we have written might be related to the problem.

Our generic include.php file has the following headers to prevent caching:

header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Fri, 01 Jan 2016 01:00:00 GMT");   // a date in the past

And this is our download function:

function DownloadFile($file, $filename) {
    // check if file exists and write header info and output file
    if (file_exists($file)) {
        header('Content-Description: File Transfer');
        header('Content-Type: application/x-msdownload');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));
        readfile($file);
        return true;
    } 
    else { 
        // file does not exist
        return false; 
    }
}

Which is primarily from the readfile() example on php.net. Are there any red flags here? What's going on?


Solution

  • The issue was caused by the way the web application serves the file following a header redirect to the same page - the page itself also serving other data as content.

    After the call to DownloadFile() the rest of the page, including HTML content etc, was sent as part of the file - resulting in the additional data corrupting the digital signature.

    The solution was to force the script to exit after the file was sent.

    $file = $_SERVER['DOCUMENT_ROOT'] . '/../product/release/' . $path;
    DownloadFile($file, "ProductInstaller.exe");
    exit; // prevent the rest of the page from being sent as part of the file