NTLM Authentication – получение имени, домена и хоста Windows в PHP

Я работаю над PHP-приложением единого входа (SSO).
Пользователи регистрируются в своем сеансе Windows, и они хотят, чтобы они автоматически вошли в приложение со своей учетной записью Windows (связанной с LDAP Active Directory).

Я пробовал этот скрипт:

<?php $headers = apache_request_headers(); // Récupération des l'entêtes client if (@$_SERVER['HTTP_VIA'] != NULL){ // nous verifions si un proxy est utilisé : parceque l'identification par ntlm ne peut pas passer par un proxy echo "Proxy bypass!"; } elseif(!isset($headers['Authorization'])) { //si l'entete autorisation est inexistante header( "HTTP/1.0 401 Unauthorized" ); //envoi au client le mode d'identification header( "WWW-Authenticate: NTLM" ); //dans notre cas le NTLM exit; //on quitte } if(isset($headers['Authorization'])) //dans le cas d'une authorisation (identification) { if(substr($headers['Authorization'],0,5) == 'NTLM '){ // on vérifit que le client soit en NTLM $chaine=$headers['Authorization']; $chaine=substr($chaine, 5); // recuperation du base64-encoded type1 message $chained64=base64_decode($chaine); // decodage base64 dans $chained64 if(ord($chained64{8}) == 1){ // |_ byte signifiant l'etape du processus d'identification (etape 3) // verification du drapeau NTLM "0xb2" à l'offset 13 dans le message type-1-message (comp ie 5.5+) : if (ord($chained64[13]) != 178){ echo "NTLM Flag error!"; exit; } $retAuth = "NTLMSSP".chr(000).chr(002).chr(000).chr(000).chr(000).chr(000).chr(000).chr(000); $retAuth .= chr(000).chr(040).chr(000).chr(000).chr(000).chr(001).chr(130).chr(000).chr(000); $retAuth .= chr(000).chr(002).chr(002).chr(002).chr(000).chr(000).chr(000).chr(000).chr(000); $retAuth .= chr(000).chr(000).chr(000).chr(000).chr(000).chr(000).chr(000); $retAuth64 =base64_encode($retAuth); // encode en base64 $retAuth64 = trim($retAuth64); // enleve les espaces de debut et de fin header( "HTTP/1.0 401 Unauthorized" ); // envoi le nouveau header header( "WWW-Authenticate: NTLM $retAuth64" ); // avec l'identification supplémentaire exit; } else if(ord($chained64{8}) == 3) { // |_ byte signifiant l'etape du processus d'identification (etape 5) // on recupere le domaine $lenght_domain = (ord($chained64[31])*256 + ord($chained64[30])); // longueur du domain $offset_domain = (ord($chained64[33])*256 + ord($chained64[32])); // position du domain. $domain = str_replace("\0","",substr($chained64, $offset_domain, $lenght_domain)); // decoupage du du domain //le login $lenght_login = (ord($chained64[39])*256 + ord($chained64[38])); // longueur du login. $offset_login = (ord($chained64[41])*256 + ord($chained64[40])); // position du login. $login = str_replace("\0","",substr($chained64, $offset_login, $lenght_login)); // decoupage du login $lenght_host = (ord($chained64[47])*256 + ord($chained64[46])); $offset_host = (ord($chained64[49])*256 + ord($chained64[48])); $host = str_replace("\0","",substr($chained64, $offset_host, $lenght_host)); if ( $login != NULL){ echo $login; } else { echo "NT Login empty!"; } } } } ?> 

Этот скрипт работает над этой конфигурацией:

  • Windows Server 2003
  • Apache 2.2 с модулем mod_auth_sspi

Но теперь мне нужно реализовать это в этой конфигурации, и это не работает:

  • Сервер Windows 2008
  • Apache 2.4.6 с модулем mod_authnz_sspi

Я продолжаю получать «ошибку флага NTLM!», Из-за этого условия:

 if (ord($chained64[13]) != 178){ echo "NTLM Flag error!"; exit; } 

Я пытался :

 if (ord($chained64[13]) != 130){ 

потому что ord ($ clained64 [13]) возвращает 130, но я не могу пойти в этом состоянии:

 } else if(ord($chained64{8}) == 3) { $lenght_domain = (ord($chained64[31])*256 + ord($chained64[30])); // longueur du domain $offset_domain = (ord($chained64[33])*256 + ord($chained64[32])); // position du domain. $domain = str_replace("\0","",substr($chained64, $offset_domain, $lenght_domain)); // decoupage du du domain //le login $lenght_login = (ord($chained64[39])*256 + ord($chained64[38])); // longueur du login. $offset_login = (ord($chained64[41])*256 + ord($chained64[40])); // position du login. $login = str_replace("\0","",substr($chained64, $offset_login, $lenght_login)); // decoupage du login $lenght_host = (ord($chained64[47])*256 + ord($chained64[46])); $offset_host = (ord($chained64[49])*256 + ord($chained64[48])); $host = str_replace("\0","",substr($chained64, $offset_host, $lenght_host)); if ( $login != NULL){ echo $login; } else { echo "NT Login empty!"; } } 

Поскольку ord($chained64{8}) всегда возвращает 1.


Изменить 2015-05-11:

  • Я попытался выполнить команду «whoami» в php, например: echo exec('whoami'); -> когда я выполняю эту команду в cmd.exe, я получаю текущего зарегистрированного пользователя, но когда я выполняю его в PHP, я получаю nt_authority / system.

  • Я предположил, что, когда PHP выполняет команду «whoami», Windows проверяет логин службы Apache. Я вошел в свойства Apache на вкладке «Вход», чтобы войти в систему как действительный пользователь Active Directory. Но тогда, когда PHP выполняет echo exec('whoami'); , Я получаю только логин, используемый для Apache, а не для текущего пользователя.

  • Я использую Internet Explorer 8 для выполнения PHP-скрипта.

  • У меня это в моем Apache httpd.conf ( _PATH_ – это путь к моим php-файлам, может быть, это неправильно?):

    <Directory "E:/_PATH_"> Options None AllowOverride All Order allow,deny Allow from all AuthName "SSPI Protected Place" AuthType SSPI SSPIAuth On SSPIAuthoritative On SSPIOfferBasic On SSPIOmitDomain On Require valid-user </Directory>


Изменить 2015-05-12:

  • Я зарегистрирован как пользователь домена на машине

  • Когда я пытаюсь с Firefox, я получаю приглашение для входа и пароля. Когда я отправляю приглашение, скрипт получает логин из приглашения, но это не то, что я хочу сделать: мне нужно заставить это работать с IE, и я не хочу вводить снова логин и пароль. Мне нужен вход в текущий сеанс Windows.

  • В Firefox я рассказал о: config, чтобы установить network.automatic-ntlm-auth.trusted-uris в мой домен, благодаря @ThaDafinser. Теперь я больше не получаю подсказки в Firefox, и все работает, но мне всегда нужно заставить работать IE.

  • В IE я установил Local Intranet Security на минимальное значение, но ничего не изменилось.

  • В IE «Автоматический вход с текущим именем пользователя и паролем» проверяется для локальных интрасети и надежных сайтов.

  • Когда я заставляю IE запрашивать учетные данные в приглашении, если я отправляю приглашение, IE не возвращает учетные данные, в отличие от Firefox.


Редактировать 2015-05-13:

  • Я добавил URL-адрес для доверенных сайтов в IE, ничего не изменилось.

  • Я установил уровень безопасности для доверенных сайтов, ничего не изменилось.

  • Я снял флажок «Использовать HTTP 1.1 через прокси-соединения» в IE> Свойства обозревателя> Дополнительно, я все еще не могу иметь информацию о сеансе в Internet Explorer, даже если я использую подсказку.

  • Я добавил полный URL-адрес в Internet Explorer> Свойства обозревателя> Безопасность> Локальная интрасеть> Сайты> Дополнительно

  • В Internet Explorer> Свойства обозревателя> Безопасность> Локальная интрасеть> Сайты> Дополнительно, я также добавил ту же часть домена (mycompany.com), что я добавил в Firefox, чтобы заставить ее работать, но это не помогло.

Редактировать 2015-05-18:

Изменен мой httpd.conf для совместимости с Apache 2.4, в соответствии с тем, что @timclutton сказал в своем ответе:

 <Directory "E:/_PATH_"> Require all denied AllowOverride All Options None AuthName "SSPI Authentication" AuthType SSPI SSPIAuth On SSPIAuthoritative On SSPIOmitDomain On Require valid-user Require user "NT AUTHORITY\ANONYMOUS LOGON" denied </Directory> 

Изменить 2015-05-19:

  • Я попытался установить базовую аутентификацию с SSPI, и она не работает.

    AuthType Basic AuthName «Требуется аутентификация» AuthUserFile «E: / PATH /.htpasswd» Требовать действительного пользователя

    Разрешить заказ, запретить доступ ко всем

    Когда я пытаюсь с Firefox, я получаю приглашение для входа и пароля. Когда я отправляю приглашение, скрипт получает логин из приглашения, но это не то, что я хочу сделать: мне нужно заставить это работать с IE, и я не хочу вводить снова логин и пароль. Мне нужен вход в текущий сеанс Windows.

    Вы можете удалить приглашение, изменив настройки Firefox:

    • Тип: «about: config» в адресной строке
    • Проверьте наличие network.automatic-ntlm-auth.trusted-uris
    • Задайте значение для своего домена или части домена, например mycompany.com (отдельно с запятыми)

    Для IE вам необходимо установить параметры безопасности для своей страницы (интрасети) ниже, чем для остальной части Интернета. См. https://superuser.com/questions/148063/why-does-internet-explorer-keep-asking-me-for-ntlm-credentials-in-an-intranet-zo.

    Модуль mod_authnz_sspi прозрачно обрабатывает все аспекты процесса аутентификации, что означает, что скрипт проверки подлинности PHP не нужен. Если модуль настроен правильно, вы просто должны ссылаться на $_SERVER['REMOTE_USER'] в своем скрипте. Любой пользователь, который не может быть аутентифицирован, получит стандартную ошибку Apache 403 - Forbidden .

    Я подозреваю, что проблема httpd.conf с вашим httpd.conf поскольку вы используете синтаксис allow/deny Apache 2.2, и это не будет работать в Apache 2.4 (если только модуль mod_access_compat включен). Вы должны прочитать Upgrading to 2.4 from 2.2 в документации Apache.

    Убедитесь, что E:/_PATH_ – это точная папка, из которой запущен ваш PHP-скрипт, и для каждого запроса на этот путь потребуется аутентификация.

    Для Apache 2.4 работает следующее:

     <Directory "/path/to/webroot"> AllowOverride All Options ExecCGI # since I run PHP via mod_fcgi; should also work as 'Options none'. AuthName "SSPI Authentication" AuthType SSPI SSPIAuth On SSPIAuthoritative On SSPIOmitDomain On Require valid-user Require user "NT AUTHORITY\ANONYMOUS LOGON" denied </Directory>