FAQ

Page Discussion History

Difference between revisions of "Symfony"

(removing more 'if' statements and replace them by 'try_files')
(Fixed remark about executable PHP scripts)
 
(4 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 
Secure Symfony 2.0 config (derived from the 1.4 config below & tested)
 
Secure Symfony 2.0 config (derived from the 1.4 config below & tested)
 +
 +
'''Important Remark!''' The following configuration makes all PHP scripts except app.php, app_dev.php and config.php downloadable instead of executing them. This is probably not desired if you'd like to use a tool like phpMyAdmin in the same virtual host.
  
 
<geshi lang="nginx">
 
<geshi lang="nginx">
  
 +
upstream phpfcgi {
 +
    server 127.0.0.1:9000;
 +
    # server unix:/var/run/php5-fpm.sock; #for PHP-FPM running on UNIX socket
 +
}
 
server {
 
server {
  listen 80;
+
    listen 80;
  
  server_name symfony2;
+
    server_name symfony2;
  root /var/www/symfony2/web;
+
    root /var/www/symfony2/web;
  
  error_log /var/log/nginx/symfony2.error.log;
+
    error_log /var/log/nginx/symfony2.error.log;
  access_log /var/log/nginx/symfony2.access.log;
+
    access_log /var/log/nginx/symfony2.access.log;
  
  # strip app.php/ prefix if it is present
+
    # strip app.php/ prefix if it is present
  rewrite ^/app\.php/?(.*)$ /$1 permanent;
+
    rewrite ^/app\.php/?(.*)$ /$1 permanent;
  
  location / {
+
    location / {
    index app.php;
+
        index app.php;
    try_files $uri @rewriteapp;
+
        try_files $uri @rewriteapp;
  }
+
    }
  
  location @rewriteapp {
+
    location @rewriteapp {
    rewrite ^(.*)$ /app.php/$1 last;
+
        rewrite ^(.*)$ /app.php/$1 last;
  }
+
    }
  
  # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+
    # pass the PHP scripts to FastCGI server from upstream phpfcgi
  location ~ ^/(app|app_dev)\.php(/|$) {
+
    location ~ ^/(app|app_dev|config)\.php(/|$) {
    fastcgi_pass   127.0.0.1:9000;
+
        fastcgi_pass phpfcgi;
    fastcgi_split_path_info ^(.+\.php)(/.*)$;
+
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
    include fastcgi_params;
+
        include fastcgi_params;
    fastcgi_param  SCRIPT_FILENAME   $document_root$fastcgi_script_name;
+
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param  HTTPS             off;
+
        fastcgi_param  HTTPS off;
  }
+
    }
 
}
 
}
 +
  
 
server {
 
server {
  listen 443;
+
    listen 443;
+
 
  server_name symfony2;
+
    server_name symfony2;
  root /var/www/symfony2/web;
+
    root /var/www/symfony2/web;
 +
 
 +
    ssl on;
 +
    ssl_certificate /etc/ssl/certs/symfony2.crt;
 +
    ssl_certificate_key /etc/ssl/private/symfony2.key;
  
  ssl on;
+
    error_log /var/log/nginx/symfony2.error.log;
  ssl_certificate      /etc/ssl/certs/symfony2.crt;
+
    access_log /var/log/nginx/symfony2.access.log;
  ssl_certificate_key  /etc/ssl/private/symfony2.key;
+
 
+
  error_log /var/log/nginx/symfony2.error.log;
+
  access_log /var/log/nginx/symfony2.access.log;
+
  
  # strip app.php/ prefix if it is present
+
    # strip app.php/ prefix if it is present
  rewrite ^/app\.php/?(.*)$ /$1 permanent;
+
    rewrite ^/app\.php/?(.*)$ /$1 permanent;
  
  location / {
+
    location / {
    index app.php;
+
        index app.php;
    try_files $uri @rewriteapp;
+
        try_files $uri @rewriteapp;
  }
+
    }
  
  location @rewriteapp {
+
    location @rewriteapp {
    rewrite ^(.*)$ /app.php/$1 last;
+
        rewrite ^(.*)$ /app.php/$1 last;
  }
+
    }
  
  # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+
    # pass the PHP scripts to FastCGI server from upstream phpfcgi
  location ~ ^/(app|app_dev)\.php(/|$) {
+
    location ~ ^/(app|app_dev|config)\.php(/|$) {
    fastcgi_pass   127.0.0.1:9000;
+
        fastcgi_pass phpfcgi;
    fastcgi_split_path_info ^(.+\.php)(/.*)$;
+
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
    include fastcgi_params;
+
        include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME   $document_root$fastcgi_script_name;
+
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param HTTPS             off;
+
        fastcgi_param HTTPS on;
  }
+
    }
 
}
 
}
  

Latest revision as of 15:26, 3 March 2013

Secure Symfony 2.0 config (derived from the 1.4 config below & tested)

Important Remark! The following configuration makes all PHP scripts except app.php, app_dev.php and config.php downloadable instead of executing them. This is probably not desired if you'd like to use a tool like phpMyAdmin in the same virtual host.

upstream phpfcgi {
    server 127.0.0.1:9000;
    # server unix:/var/run/php5-fpm.sock; #for PHP-FPM running on UNIX socket
}
server {
    listen 80;
 
    server_name symfony2;
    root /var/www/symfony2/web;
 
    error_log /var/log/nginx/symfony2.error.log;
    access_log /var/log/nginx/symfony2.access.log;
 
    # strip app.php/ prefix if it is present
    rewrite ^/app\.php/?(.*)$ /$1 permanent;
 
    location / {
        index app.php;
        try_files $uri @rewriteapp;
    }
 
    location @rewriteapp {
        rewrite ^(.*)$ /app.php/$1 last;
    }
 
    # pass the PHP scripts to FastCGI server from upstream phpfcgi
    location ~ ^/(app|app_dev|config)\.php(/|$) {
        fastcgi_pass phpfcgi;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param  HTTPS off;
    }
}
 
 
server {
    listen 443;
 
    server_name symfony2;
    root /var/www/symfony2/web;
 
    ssl on;
    ssl_certificate /etc/ssl/certs/symfony2.crt;
    ssl_certificate_key /etc/ssl/private/symfony2.key;
 
    error_log /var/log/nginx/symfony2.error.log;
    access_log /var/log/nginx/symfony2.access.log;
 
    # strip app.php/ prefix if it is present
    rewrite ^/app\.php/?(.*)$ /$1 permanent;
 
    location / {
        index app.php;
        try_files $uri @rewriteapp;
    }
 
    location @rewriteapp {
        rewrite ^(.*)$ /app.php/$1 last;
    }
 
    # pass the PHP scripts to FastCGI server from upstream phpfcgi
    location ~ ^/(app|app_dev|config)\.php(/|$) {
        fastcgi_pass phpfcgi;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTPS on;
    }
}

Secure Symfony 1.4 config generalized from a production system & tested.

server {
  listen 80;
 
  server_name mysite.com;
 
  root /var/www/mysite.com/web;
  access_log /var/log/nginx/mysite.com.access.log;
  error_log /var/log/nginx/mysite.com.error.log;
 
  location ~ ^/(index|frontend|frontend_dev|backend|backend_dev)\.php$ {
    include fastcgi_params;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
    fastcgi_param HTTPS off;
    fastcgi_pass   127.0.0.1:9000;
  }
 
  location / {
    index index.php;
    try_files $uri /index.php?$args;
  }
}
 
server {
  listen 443;
 
  ssl on;
  ssl_certificate      /etc/ssl/certs/mysite.com.crt;
  ssl_certificate_key  /etc/ssl/private/mysite.com.key;
 
  server_name mysite.com;
 
  root /var/www/mysite.com/web;
  access_log /var/log/nginx/mysite.com.access.log;
  error_log /var/log/nginx/mysite.com.error.log;
  location ~ ^/(index|frontend|frontend_dev|backend|backend_dev)\.php$ {
    include fastcgi_params;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
    fastcgi_param HTTPS on;
    fastcgi_pass   127.0.0.1:9000;
  }
 
  location / {
    index index.php;
    try_files $uri /index.php?$args;
  }
}

SECURITY NOTE:

This above config is not vulnerable to file upload attacks on PHP, while configs that use

location ~ \.php$ {
  ...
}

are vulnerable. The common workaround to file upload attacks is to set fix_pathinfo=0 in php.ini. This breaks pathinfo URLs, and symfony relies on them. The solution used here is to explicitly specify the files that get parsed as php.

For more information, see the nginx+php-cgi security alert


my working symfony config:

    location / {
        try_files $uri $uri/ /index.php$uri?$args;
    }
    location ^~ /sf/ {
        alias /usr/share/php/data/symfony/web/sf/;
    }
    location ~ "^(.+\.php)($|/)" {
        fastcgi_split_path_info ^(.+\.php)(.*)$;
 
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_pass   127.0.0.1:9000;
        include        fastcgi_params;
    }

Using Nginx as a development server for symphony, this is a php (cli) script which configurate and launch Nginx in a directory, the result is similar to django development server.

#!/usr/bin/php
<?php
// by Jean-Bernard Addor 2011
if (1 != assert_options(ASSERT_ACTIVE) or 1 != assert_options(ASSERT_WARNING)):
  trigger_error('Assertion ignored');
endif;
 
 
$return_var = 0;
echo passthru('mkdir --parents '.'/tmp'.getcwd(), $return_var);
assert ('0 == $return_var');
// assert : directory must be writable and executable
 
// $process = proc_open("env PHP_FCGI_CHILDREN=15 php-cgi -b /tmp".getcwd()."/php.socket", $descriptorspec, $pipes);
// env should be modified here, if this is really needed
$php_process = proc_open("php-cgi -b /tmp".getcwd()."/php.socket",
  array(0 => STDIN, 1 => STDOUT, 2 => STDERR), $php_pipes);
assert('FALSE != $php_process');
 
file_put_contents("/tmp".getcwd()."/nginx.conf", '
worker_processes  1;
error_log  /dev/stderr;
pid        /tmp'.getcwd().'/nginx.pid;
events {
    worker_connections  1024;
}
 
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    client_max_body_size 10m;
    sendfile        on;
    gzip  on;
    keepalive_timeout  65;
 
    server {
        listen 8080;
        server_name 127.0.0.1;
        server_tokens off;
        root '.getcwd().'/web;
        index index.php index.html index.htm;
 
        access_log  /dev/stdout;
 
        location / {
                try_files $uri /index.php;
        }
 
        location ^~ /frontend_dev.php/ {
                try_files $uri /frontend_dev.php;
                # try_files $uri /frontend_dev.php?q=$uri&$args /index.php?q=$uri&$args;
        }
 
        location ^~ /sf/ {
          root '.getcwd().'/lib/vendor/symfony/data/web/;
        }
 
        location ~ \.php$ {
                include /etc/nginx/fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_pass  unix:/tmp'.getcwd().'/php.socket; # 127.0.0.1:9000;
        }
 
        location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
                expires max;
                log_not_found off;
        }
    }
}
');
// connections should only be accepted from localhost! (security issue)
 
echo passthru('nginx -c /tmp'.getcwd().'/nginx.conf -t', $return_var);
assert ('0 == $return_var');
usleep(200000); echo "Launching Nginx\n";
// echo passthru('nginx -c /tmp'.getcwd().'/nginx.conf', $return_var);
// assert ('0 == $return_var');
 
$nginx_process = proc_open('nginx -c /tmp'.getcwd().'/nginx.conf',
  array(0 => STDIN, 1 => STDOUT, 2 => STDERR), $nginx_pipes);
assert('FALSE != $nginx_process'); // was blocking with passthru!! PHP 5.3.2-1ubuntu4.9
usleep(200000); // to be sure that stdout and stderr are printed
 
echo "Waiting for ctrl-c (", posix_getpid(), ")\n";
$oldset = array();
pcntl_sigprocmask(SIG_BLOCK, array(SIGHUP, SIGINT), $oldset);
pcntl_sigwaitinfo(array(SIGHUP, SIGINT, SIGUSR1));
pcntl_sigprocmask(SIG_SETMASK, $oldset);
 
echo "\nShutting doen Nginx\n";
echo passthru('nginx -c /tmp'.getcwd().'/nginx.conf -s stop', $return_var);
assert ('0 == $return_var');
 
echo "\nShutting down php-cgi (fcgi)\n";
$php_proc_terminate = proc_terminate($php_process);
$php_proc_close = proc_close($php_process);
assert(-1 != $php_proc_close);
 
// just for cleaness
$nginx_proc_terminate = proc_terminate($nginx_process);
$nginx_proc_close = proc_close($nginx_process);
assert(-1 != $nginx_proc_close);
 
?>