Debugging PHP SOAP over SSL using Charles
I’m currently integrating against a SOAP server using PHP which wasn’t working as I expected, so I wanted to find out what was happening over the wire. I have Charles installed and use it regularly with OS X’s system-wide proxy settings. However, PHP’s SoapClient doesn’t use these, so I had to work out how to do it manually.
$options = [ "cache_wsdl" => WSDL_CACHE_NONE, "soap_version" => SOAP_1_1, "trace" => 1, "proxy_host" => "localhost", "proxy_port" => 8888, ]; $client = new \SoapClient($wsdl, $options);
I did this and saw traffic in Charles. However, my service endpoint is SSL and I saw this error:
PHP Fatal error: SOAP-ERROR: Parsing WSDL: Couldn't load from 'https://example.com/Service.svc?singleWsdl' : failed to load external entity "https://example.com/Service.svc?singleWsdl" in SoapServiceProcessor.php on line 167
Looking in Charles, I saw the note:
You may need to configure your browser or application to trust the Charles Root Certificate. See SSL Proxying in the Help menu.
Again, we turn back to Lorna for how to do sort this out. This time, we need Manipulating HTTP with Charles Proxy, that she wrote for TechPortal. Unhelpfully, that website doesn’t use section links, so scroll all way down to the Charles and SSL section to find out the relevant information about how to set up Charles for SSL proxying.
On OS X, you simply do:
- Help -> SSL Proxying -> Install Charles Root Certificate
- Proxy -> SSL Proxying Settings:
- Check Enable SSL Proxying
- Add the endpoint’s domain to the list of locations
Finally, I needed to tell SoapClient to trust Charles’ root certificate so that it can decrypt the SSL traffic.
This is done by downloading the Charles root certificate (Help -> SSL Proxying -> Save Charles Root Certificate) and storing it somewhere. I chose to put it in /usr/local/etc/charles-ssl-proxying-certificate.crt.
Finally, configure a new stream_context that knows about this certificate and add it to the SoapClient:
$options = [ "cache_wsdl" => WSDL_CACHE_NONE, "soap_version" => SOAP_1_1, "trace" => 1, "proxy_host" => "localhost", "proxy_port" => 8888, "stream_context" => stream_context_create([ 'ssl' => [ 'cafile' => '/usr/local/etc/charles-ssl-proxying-certificate.crt' ] ]; ]; $client = new \SoapClient($wsdl, $options);
Now everything works and I can see the actual data that’s being sent to the SSL SOAP service and I solved my problem!