Я работаю над 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!";
}
}
}
}
?>
Этот скрипт работает в этой конфигурации:
Но теперь мне нужно реализовать это на этой конфигурации, и она не работает:
Я продолжаю получать сообщение «Ошибка флага NTLM!» Из-за этого условия:
if (ord($chained64[13]) != 178){
echo "NTLM Flag error!";
exit;
}
Я старался :
if (ord($chained64[13]) != 130){
потому что ord ($ chained64 [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:
Я попытался установить базовую аутентификацию intead SSPI, и она не работает.
AuthType Basic
AuthName «Требуется аутентификация» AuthUserFile «E: /ДОРОЖКА/.htpasswd»Require valid-user
Заказать разрешить, отказать
Разрешить от всех
Когда я пытаюсь использовать Firefox, я получаю приглашение ввести логин и пароль. Когда я отправляю приглашение, скрипт получает логин из приглашения, но это не то, что я хочу сделать: я должен заставить это работать с IE, и я не хочу снова вводить логин и пароль. Я хочу войти в систему текущего сеанса Windows.
Вы можете удалить приглашение, изменив настройки Firefox:
Для 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
так как вы используете 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>