NTLM Authentication - получение имени, домена и хоста Windows в PHP
Я работаю над приложением Single Sign-On (SSO) PHP.
Пользователи регистрируются в своем сеансе Windows, и они хотят, чтобы они автоматически вошли в приложение со своей учетной записью Windows (связанной с LDAP Active Directory).
Я пробовал этот script:
<?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!";
}
}
}
}
?>
Этот script работает над этой конфигурацией:
- Сервер Windows 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 script.
-
У меня это в моем 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, я получаю приглашение для входа и пароля. Когда я отправляю приглашение, script получает логин из приглашения, но это не то, что я хочу сделать: мне нужно заставить это работать с 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" Требовать действительного пользователя
Разрешить заказ, запретить Разрешить от всех
Ответы
Ответ 1
Когда я пытаюсь использовать Firefox, я получаю приглашение для входа и пароля. Когда я отправляю приглашение, script получает логин из приглашения, но это не то, что я хочу сделать: мне нужно заставить это работать с 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
Ответ 2
Модуль mod_authnz_sspi
прозрачно обрабатывает все аспекты процесса аутентификации, что означает, что вам не нужна ваша аутентификация PHP script. Если модуль настроен правильно, вы должны просто указать $_SERVER['REMOTE_USER']
в script. Любой пользователь, который не может быть аутентифицирован, получит стандартную ошибку Apache 403 - Forbidden
.
Я подозреваю, что проблема связана с вашим httpd.conf
, поскольку вы используете синтаксис Apache 2.2 allow/deny
, и это не будет работать в Apache 2.4 (если у вас не включен модуль mod_access_compat
). Вы должны прочитать Обновление до версии 2.4 из 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>