Как я могу безопасно обнаруживать SSL в CakePHP за обратным прокси-сервером nginx?

CakePHP (все версии, которые я видел) проверяют на $_SERVER['HTTPS'] чтобы увидеть, был ли запрос выполнен поверх HTTPS вместо простого HTTP.

Я использую nginx как балансировщик нагрузки, за которым стоят серверы приложений Apache. Поскольку соединение SSL завершается на балансировщике нагрузки, $_SERVER['HTTPS'] не задан в отношении CakePHP.

Я бы хотел найти безопасный способ обнаружения HTTPS на серверах приложений.

До сих пор я ввел это в мою конфигурацию CakePHP:

 $request_headers = getallheaders(); if ( (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) || ( isset($request_headers['X-Forwarded-Proto']) && $request_headers['X-Forwarded-Proto'] == 'https' ) ) { $ssl = true; // overwrite environment vars (ugly) since CakePHP won't honour X-Forwarded-Proto $_SERVER['HTTPS'] = 'on'; $_ENV['HTTPS'] = 'on'; } else { $ssl = false; } 

И затем в конфигурации nginx я использовал proxy_set_header X-Forwarded-Proto https; для добавления флага к любым запросам между балансировщиком нагрузки и серверами back-end приложений.

Это работает отлично, но любой, кто делает прямой запрос к серверам приложений, может обмануть их, думая, что они просматривают SSL, когда это не так. Я не уверен, является ли это угрозой безопасности, но это не кажется хорошей идеей.

Это риск для безопасности? Какое лучшее решение?

Поскольку использование X-Forwarded-Proto кажется чем-то стандартным , решение может быть хорошим патчем, который должен быть отправлен в ядро ​​CakePHP, поэтому я думаю, что любой ответ может законно включать редактирование основных файлов.

mod_rpaf позволит вам это сделать.

Это устанавливает значение HTTPS в Apache для «on» на основе заголовков, отправленных nginx, поэтому Cake будет работать из коробки (как и любые другие приложения, запущенные в Apache).

Он также корректирует значения для REMOTE_ADDR, SERVER_PORT и HTTP_HOST.

Вот мой пример config:

 <IfModule mod_rpaf.c> RPAF_Enable On RPAF_ProxyIPs 127.0.0.1 10.0.0.0/24 RPAF_SetHostName On RPAF_SetHTTPS On RPAF_SetPort On </IfModule> # If mod_rewrite redirects then we lose the HTTPS status to REDIRECT_HTTPS. # This resets it back. This happens with Cake's front controller <IfModule setenvif_module> SetEnvIf REDIRECT_HTTPS on HTTPS=on </IfModule> 

Добавить детектор запросов

Не редактируйте ядро. Если вы намерены подать патч, не полагайтесь на свой патч до тех пор, пока он не будет принят. В противном случае вы находитесь на пути к расхождению и поддерживаете свою собственную вилку CakePHP.

Как только вы определите свою точную логику реализации, вы можете использовать детектор запросов, чтобы почитать его.

например:

 //AppController::beforeFilter public function beforeFilter() { $this->request->addDetector('ssl', array( 'env' => 'HTTP_X_FORWARDED_PROTO', 'value' => 'https' )); } 

После этого ваш пользовательский заголовок будет правильно идентифицирован cake как ssl-запрос.

Обратите внимание, что ключи глобального значения $_SERVER нормализуются как все кепки и подчеркивание, например:

 $ curl --header "X-Forwarded-Proto:https" http:://yoursite.com 

$_SERVER['HTTP_X_FORWARDED_PROTO'] – таким образом, это ключ для проверки.

Учет для (в) безопасности

Да, это то, о чем вам следует позаботиться – либо отключите прямой доступ к вашим веб-серверам, чтобы он мог реагировать только через ip loadbalancer; или изменить свой детектор так, чтобы он не возвращал true для запросов прямого доступа, независимо от значения заголовка X-Forwarded-Proto – как показано в документации, вы можете использовать обратный вызов для выполнения любой логики, а не просто для проверки значений некоторой переменной среды.

Я нашел комментарий AD7six – переопределить функцию redirect () – очень полезно в моей настройке только для SSL, так как у моего приложения были некоторые переадресации на http.

Я просто добавил следующее в AppController.php поэтому функция перенаправления всегда использует https:

 function redirect($url, $status = NULL, $exit = true) { return parent::redirect(str_replace('http://', 'https://', Router::url($url, true))); }