PHPのGuzzileクライアントでNTLM認証を超える

会社のイントラサービスがNTLM認証とやらで守られていて、いろいろ調べたところのメモ

#NTLMとは
この辺参照。ちと難しい
https://www.innovation.ch/personal/ronald/ntlm.html

Windowsにログインしているユーザ情報で、Webのアクセスの認可・認証をする感じでしょうか。Windowsにログインしている端末からだとユーザ認証を求められず（何も意識せずに）ブラウザでアクセスできるけど、プログラムから接続すると拒否されるような場合は、この環境かもしれません。

ちなみにProxyとは別の話です。外に通信するために特殊な認証をクリアしないといけない場合は、[Cntlmを使ってNTLMv2認証Proxy環境をやっつける](https://qiita.com/yohskeey/items/6f17ea051fbe5568f3bf)を参照。

#結論
Guzzile なら簡単だった（ちゃんとマニュアルを読めば解決）
https://guzzle3.readthedocs.io/http-client/client.html#auth


#検証
##とりあえず普通につなげてみる

```php:test.php
$guzzile = new \GuzzleHttp\Client();

$option = ['verify' => false,];
$option['debug']=true;
$option['http_errors']=false;
        
$url = 'http://local-service...';

$response = $guzzile->get($url, $option);
$body = $response->getBody();
$data = (String)$body;
echo $data;
```

通信ログ（の一部）

```
*   Trying x.x.x.x...
* TCP_NODELAY set
* Connected to xx.xx.com (x.x.x.x) port 80 (#0)
> GET / HTTP/1.1
Host: xx.xx.com
User-Agent: GuzzleHttp/6.2.1 curl/7.54.1 PHP/7.0.21

< HTTP/1.1 401 Unauthorized
< Content-Type: text/plain; charset=utf-8
< Server: Microsoft-IIS/8.5
< SPRequestGuid: 
< request-id: 
< X-FRAME-OPTIONS: SAMEORIGIN
< SPRequestDuration: 2
< SPIisLatency: 0
< WWW-Authenticate: NTLM
< X-Powered-By: ASP.NET
< MicrosoftSharePointTeamServices: 
< X-Content-Type-Options: nosniff
< X-MS-InvokeApp: 1; RequireReadOnly
< Date: Sun, 02 Jun 2019 06:15:15 GMT
< Content-Length: 16
<
* Connection #0 to host xx.xx.com left intact
401 UNAUTHORIZED
```

結果は、```401 UNAUTHORIZED``` が返ってくる。認証が必要だということですね。残念。この通信に```WWW-Authenticate: NTLM```が入っているのがヒントですね。NTLM認証下で動いているということですね。


##オプションを追加する
Guzzileのauthをオプションを追加します。コードの修正は1行追加するだけなので簡単です。authオプションの3番目のオプションがあるのですね。

```php
$guzzile = new \GuzzleHttp\Client();

$option = ['verify' => false,];
$option['debug']=true;
$option['http_errors']=false;

//↓ココ追加
$option['auth'] = ['user', 'password','ntlm'];
        
$url = 'http://local-service...';

$response = $guzzile->get($url, $option);
$body = $response->getBody();
$data = (String)$body;
echo $data;
```

通信ログ

```
*   Trying x.x.x.x...
* TCP_NODELAY set
* Connected to xx.xx.com (x.x.x.x) port 80 (#0)
* Server auth using NTLM with user '<USER>'
> GET /index.html HTTP/1.1
Host: xx.xx.com
Authorization: NTLM TlRMTVNTUAAxxx==
User-Agent: GuzzleHttp/6.2.1 curl/7.54.1 PHP/7.0.21

< HTTP/1.1 401 Unauthorized
< Server: Microsoft-IIS/8.5
< WWW-Authenticate: NTLM TlRMTVNTUAAxxxxxxxxxx=
< SPRequestGuid: 
< request-id: 
< X-FRAME-OPTIONS: SAMEORIGIN
< SPRequestDuration: 1
< SPIisLatency: 0
< X-Powered-By: ASP.NET
< MicrosoftSharePointTeamServices: 15.0.0.4569
< X-Content-Type-Options: nosniff
< X-MS-InvokeApp: 1; RequireReadOnly
< Date: Sun, 02 Jun 2019 11:30:03 GMT
< Content-Length: 0
<
* Connection #0 to host xx.xx.com left intact
* Issue another request to this URL: 'http://xx.xx.com/'
* Found bundle for host xx.xx.com: 0x15025758 [can pipeline]
* Re-using existing connection! (#0) with host xx.xx.com
* Connected to xx.xx.com (x.x.x.x) port 80 (#0)
* Server auth using NTLM with user '<USER>'
> GET /index.html HTTP/1.1
Host: xx.xx.com
Authorization: NTLM TlRMTVNTUAAxxxxxxxxxx=
User-Agent: GuzzleHttp/6.2.1 curl/7.54.1 PHP/7.0.21

< HTTP/1.1 200 OK
< Cache-Control: private, max-age=0
< Content-Type: text/html; charset=utf-8
< Expires: Sat, 18 May 2019 11:30:03 GMT
< Last-Modified: Sun, 02 Jun 2019 11:30:03 GMT
< Server: Microsoft-IIS/8.5
< X-SharePointHealthScore: 0
< X-AspNet-Version: 4.0.30319
< Set-Cookie: 
< Set-Cookie: 
< Set-Cookie: 
< SPRequestGuid: 0fa2e29e-1880-7002-7a3b-08c82a909236
< request-id: 0fa2e29e-1880-7002-7a3b-08c82a909236
< X-FRAME-OPTIONS: SAMEORIGIN
< SPRequestDuration: 453
< SPIisLatency: 0
< Persistent-Auth: true
< X-Powered-By: ASP.NET
< MicrosoftSharePointTeamServices: 15.0.0.4569
< X-Content-Type-Options: nosniff
< X-MS-InvokeApp: 1; RequireReadOnly
< Date: Sun, 02 Jun 2019 11:30:03 GMT
< Content-Length: 95613
<
* Connection #0 to host xx.xx.com left intact

<!DOCTYPE html >
以下、HTMLなので省略

```
無事にHTMLが取れました。

#もうちょっと細かく見てみる
IDとパスワードを渡して通信すると、Guzzileによって自動的に2回の通信が行われます。

1回目の通信で、```Authorization```ヘッダーがGuzzileによって付与されます。

```
> GET /index.html HTTP/1.1
Host: xx.xx.com
Authorization: NTLM TlRMTVNTUAAxxx==
```

そうすると、サーバーから```WWW-Authenticate```ヘッダーが返ってきます。

```
< HTTP/1.1 401 Unauthorized
< Server: Microsoft-IIS/8.5
< WWW-Authenticate: NTLM TlRMTVNTUAAxxxxxxxxxx=
```


2回目の通信は、1回目の通信のレスポンスヘッダーの値を```Authorization```としてに渡します。

```
> GET /index.html HTTP/1.1
Host: xx.xx.com
Authorization: NTLM TlRMTVNTUAAxxxxxxxxxx=
```

そうすると、無事、欲しかったコンテンツが返ってきます。