Could not construct ApplicationDefaultCredentials with Recaptcha Enterprise - php

I'm getting a "Could not construct ApplicationDefaultCredentials" from Google Recaptcha Enterprise, but only on our remote server. I've tried everything I can think of to isolate the issue, but I've had no luck.
I have two Recaptcha Enterprise keys: One for testing, and one for prod.
The testing key works fine on localhost. I've tried both the testing and prod key on our staging server, but I keep getting the same error.
Could not construct ApplicationDefaultCredentials
Things I've checked:
The key is successfully requesting tokens (I can see them in the form)
The service account .json credentials are being picked up correctly (I've tried outputting the contents to ensure they can be read)
The domains are correctly configured and allowed (Google helpfully lets you know if this isn't the case)
The Project ID is also correctly being picked up and sent
Basically all the values are present (project ID, site ID, service account details) and the domain is allowed, but as soon as it's on the remote staging server, it is failing to create credentials.
I'm struggling to figure out what the difference could be.
public static function createRecaptchaAssessment(
string $siteKey, // Present
string $token, // Present
string $projectId // Present
): Assessment {
$options = [
'keyFile' => config('services.google.app_credentials'), // Present
'projectID' => $projectId
];
$client = new RecaptchaEnterpriseServiceClient($options); // <-- Throws exception for ApplicationDefaultCredentials not being able to be created
...
Things to consider: The staging server is hosted on an elasticbeanstalk.com subdomain, and the site is password protected with .htpasswd. I know sometimes elasticbeanstalk.com is blacklisted because it is a blanket domain, but we're only specifying the subdomain and there's no "This domain is not allowed" message from Google. And there shouldn't be any inbound connections being blocked by .htapasswd that I'm aware of.
I've tried creating a new Service Account, just incase there was something configured incorrectly (it has Recaptcha Enterprise Agent permissions) but nothing changed.
Any ideas on how else I could debug this would be gratefully appreciated. (Note: This is a PHP/Laravel 9 project hosted on AWS Elastic Beanstalk, but I don't think that's a factor.)

Key takeaway: The ApplicationDefaultCredentials error is almost certainly related to the Service Account .json not being picked up by your application.
Full version: So apparently I took some poor advice from another SO answer which suggested you could pass the path to the Service Account credentials .json file through the following array:
$options = [
'keyFile' => config('services.app_credentials'), // DON'T DO THIS
'projectID' => $projectId
];
I spent a long time looking through the Google library only to discover that neither array keys are used. Actually what happens is this:
If the library isn't passed a path to the Service Account credentials .json file, it then looks for an environment variable called GOOGLE_APPLICATION_CREDENTIALS.
If you have this ENV and your environment supports it, that's actually what it is going on. (Which is why you might find that it works locally, but not remotely, as I did.)
When you deploy Laravel remotely, all the .env variables are cached and so not available through getenv()... meaning the library is not able to find GOOGLE_APPLICATION_CREDENTIALS even if you have it included in your .env.
The solution is to add the path through an array key credentials:
$options = [
'credentials' => config('services.app_credentials')
];
$client = new RecaptchaEnterpriseServiceClient($options);
Now it works perfectly.

Related

Wordpress REST API Issue

I'm currently trying to wrap my head around Wordpress and its relation to the REST API. All of theses issues are only occurring in the context of plugin development. So far I had setup my endpoint and written my callback which will be executed whenever someone is sending a GET-Request to said endpoint. However, my callback is never called. This is the file which I've written so far.
/*
* Plugin Name: cola-learning
*/
if(!defined('ABSPATH')){
die;
}
if(!function_exists('add_action')){
echo 'You cant access this ressource!';
exit;
}
function PrintRESTResponse()
{
return rest_ensure_response("student");
}
function SetupREST()
{
return register_rest_route("student/v1","/view/",[
'methods' => 'GET',
'callback' => 'PrintRESTResponse'
],false);
}
add_action('rest_api_init','SetupREST');
Perhaps it might also help, if I'll give some background information about my development machine:
- OS: Windows 10
- Server: Apache Web Server (included inside XAMPP)
- Wordpress Version: 5.3.2
- PHP Version: 7.4
- Development IDE: Eclipse 2019-12 CDT ( with PHP Plugin )
From my research, everything should work fine. However, it doesnt :/ Did I miss something crucial?
Update:
WordPress REST API Routing
WordPress is having a default route for all request which are directed to the REST API ( at least if you use XAMPP with the Bitnami WordPress application module). Now, if one wants to send a request to said REST API, the person needs to use an URL with the form of ip:port/wp-json/rest_route. ip ressembles the ip address of the server which is hosting wordpress. port is the port of said server. wp-json, however, is the portion which differs an ordinary request from a request to the REST API. Everything after this portion (rest_route) is the rest route which I've defined in the above source code.
What went wrong?
My request was pointed to the wrong endpoint. Therefore I used the URL 'localhost:wpPort/wordpress/student/v1/view/'. However, the 'wp-json/' portion is missing. Therefore WordPress will search for a page that doesn't exist in the first place. Instead I should have used the URL 'localhost:wpPort/wordpress/wp-json/student/v1/view/'.
After some troubleshooting it turns out the URL being used to test was missing a part of the endpoint path.
Here is the correct path to test the newly registered endpoint.
localhost:80/wordpress/wp-json/student/v1/view/
Here's some more reading on WP Rest API endpoints:
https://developer.wordpress.org/rest-api/extending-the-rest-api/routes-and-endpoints/

How to use AWS PHP SDK v3.0, without credential file

Hi I am using aws SDK Version 3 for php to upload files on s3
I need to get rid of credentials file ( .aws/credentials) because it's causing issues on my production server,
The hard coded credentials method isn't working in my code. link pasted below.
https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_credentials.html#hardcoded-credentials
kindly provide a valid and working solution how to use hard coded credentials.
please note if i use credential file everything works OK. so the problem is with credentials code.
here is my code when I initiate my s3 object
$s3Client = new S3Client([
'profile' => 'default',
'region' => 'us-west-2',
'version' => '2006-03-01',
'scheme' => 'http',
'credentials'=>[
'key' => KEY,
'secret' => SECRET
]
]);
You just need to remove the 'profile' => 'default', line, which has the effect of overriding your hard-coded credentials.
I've been dealing with your same problem with much frustration today, and finally solved it. See related answer here for the same problem on a different Amazon service.
per AWS documentation, https://docs.aws.amazon.com/aws-sdk-php/v2/guide/credentials.html
If you do not provide credentials to a client object at the time of
its instantiation (e.g., via the client's factory method or via a
service builder configuration), the SDK will attempt to find
credentials in your environment when you call your first operation.
The SDK will use the $_SERVER superglobal and/or getenv() function to
look for the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment
variables. These credentials are referred to as environment
credentials.
V3 doc here https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_credentials.html
In my case I am using an IAM role in the machines which host the app, it is easier to manage permissions from IAM dashboard and you will avoid hardcoded or config file with credentials.

How to connect to a WebSphere MQ Queue using SSL authentification in PHP

I want to connect to a WebSphere MQ Queue using SSL authentification from a PHP application (script).
Queue Manager Version is 7.0 or 7.5
MQ Client Version is 8.0
PHP Version is 7.0 (docker)
Using PHP mqseries pecl extension v0.15 (with custom fixes)
So far I was able to connect without SSL connection.
Here are the steps I have to done so far:
I have installed the WebSphere MQ Client v8 for Linux (CentOS in my case)
I have downloaded the PECL 0.15 version of the mqseries php extension. (There as a small bug in the extension, I had to recompile it to make it work properly. I used to get a segmentation fault on MQGET).
I linked the the mqseries.so to php and enabled the extension.
I succesfully (without SSL)
Connect to the queue manager
Open the queue for reading
Get messages on the queue
Close the connection
When I set the USE_SSL in my script to true, I get the error code 2393 that means "An MQCONN or MQCONNX call was issued with SSL configuration options specified, but an error occurred during the initialization of the SSL environment." This error message is very generic and does not help me pin point where is the problem.
The MQ_KEYSTORE is set as /path/to/my/key and my filename is key.kdb and has at the same level key.sth as suggested by this documentation
The MQ_SSL_CIPHER_SPEC is the same in the script than specified by on the queue manager for the specified MQ_CHANNEL_SSL. Checked multiple times. These are NOT the Cipher Suite used with JMS connections
The security cache has been refreshed on the queue manager.
On the server side, I checked the error logs for the queue manager and didn't seem to see my channel name. I say "seem" because there was a lot of noise and there were a few ??? channel name in the lot. So I feel like it did not reach the queue manager for some reason.
I also used the "amqssslc" command found in the MQ Client installation bin folder to test my ssl config. I get the same error than using the PHP script.
I also used WireShark to sniff packets on the corresponding MQ_PORT. The content of the packages contained certificate information. So there is something that looks like a SSL hand shake going on.
I am now out of ideas as of how to debug the case. Does anyone has an idea of what to check next? Is there connection logs on my MQ Client installation that I should check?
Here is an example of a connection using SSL in PHP
Here is a simplified version of my MQ script (I removed the outputs). Some of the constants are not disclosed for security purposes.
All MQSERIES_* constants are defined in the extension
All MQ_* are hardcoded parameters to test my script but their definition does not appear in the script excerpt.
<?php
// Constants defined here
$options = [
'Version' => MQSERIES_MQCNO_VERSION_4,
'Options' => MQSERIES_MQCNO_STANDARD_BINDING,
'MQCD' => [
'Version' => 7,
'ChannelName' => MQ_CHANNEL,
'ConnectionName' => sprintf('%s(%s)', MQ_HOST, MQ_PORT),
'TransportType' => MQSERIES_MQXPT_TCP,
]
];
if (USE_SSL) {
$options['MQSCO'] = [
'KeyRepository' => MQ_KEYSTORE
];
$options['MQCD']['ChannelName'] = MQ_CHANNEL_SSL;
$options['MQCD']['SSLCipherSpec'] = MQ_SSL_CIPHER_SPEC;
}
mqseries_connx(MQ_QUEUE_MANAGER, $options, $conn, $comp_code, $reason );
$mqods2 = [
'ObjectType' => MQSERIES_MQOT_Q,
'ObjectName' => MQ_QUEUE
];
mqseries_open($conn, $mqods2, MQSERIES_MQOO_INPUT_AS_Q_DEF | MQSERIES_MQOO_FAIL_IF_QUIESCING, $obj, $comp_code, $reason);
$gmd = [];
$gmo = [
'Options' => MQSERIES_MQGMO_FAIL_IF_QUIESCING | MQSERIES_MQGMO_WAIT, 'WaitInterval' => 3000
];
$msg = "";
$data_length = "";
for ($i = 0; $i < 1000; $i++) {
mqseries_get($conn, $obj, $gmd, $gmo, 10000, $msg, $data_length, $comp_code, $reason);
if ($reason === 2033) {
printf("No more messages to process\n");
break;
}
// Business logic
}
mqseries_disc($conn, $comp_code, $reason);
?>
UPDATE
Using the client side logs that I didn't know existed. We finally found out that our server was not using a proper certificate. The server's certificate was actually self-signed, thus not granting us access because our .kdb file didn't have it's public key. We added the server's own public key to the .kdb file making that step work out.
As suggested in the comments from JoshMC, for our next problem, we suspect the label is wrong in the keystore file. We did not specify a specific label from the .kdb file. From the docs "For queue managers and clients respectively, the following sources are searched in sequence for a non-empty value. The first non-empty value determines the certificate label. The certificate label must exist in the key repository. If no matching certificate in the correct case and format is found that matches a label, an error occurs and the SSL or TLS handshake fails."
And it also states that "The certificate label cannot contain spaces.".
I will try again tomorrow with proper label naming and sending a specific label name. I will try to see if the name convention ibmwebspheremq<user_that_runs_the_php_process> actually impacts the validity.
UPDATE 2
It finally worked out. As mentioned by JoshMC, the private key needs to have the specific label ibmwebspheremq. I didn't try the CertificateLabel yet. I might dig into that next week.
At first we were using the GUI (ikeyman) from ibm to generate the .kdb file. But we found out it had quite a few bugs in it. For instance, it imports twice the private key with the same label (auto-generated label from the certificate). Editing the label name was not possible (hence the connection problems). To solve that part we used the command line tool ikeycmd that basically offers the same features on the command line (minus the bugs). To run the command, you need the IBM jre (Not verified, needs to be checked out) and run it as java com.ibm.gsk.ikeyman.ikeycmd. From here, there is a whole world of documentation on IBM web site about how to create a certificate, rename labels, check details, etc. Lots and lots of fun!
With a full IBM MQ client install, client side errors will be logged to the directory: /var/mqm/errors
The errors if any will be logged to the file AMQERR01.LOG (this gets rotated with two other files ending in 02 and 03. If the error is something that MQ is not expecting it may also create a file that ended in .FDC with additional details.
The Queue Manager will log a channel name of ??? if the connection has failed during the channel negotiating prior to the client the sending the channel name. If the IBM MQ the queue manager is using v7.5 or lower the channel name is not exchanged until after the TLS handshake is complete.
With v8 and later clients connecting to v8 and later queue managers, MQ will use TLS SNI to exchange the channel name during TLS negotiation, however I am unsure if MQ has also been enhanced to log this channel name where in prior versions it logged ???. Note that IBM MQ Classes for Java and IBM MQ Classes for JMS client even at v8 and later do not support the SNI feature and do not send the channel name until after the TLS handshake is complete.
Check that your client key.kdb has a private key. There are three ways which MQ uses to identify which private key to use.
With MQ Client v7.5 and earlier the label of the private key must be:
ibmwebspheremq<user_that_runs_the_php_process>
With MQ client v7.5 and earlier an alternative is to set the private key as the default certificate and make sure the following environment variable is set in the environment which executes the program. Note this also works with 8.0.0.7/9.0.0.1 but I prefer the next method over this one.
AMQ_SSL_ALLOW_DEFAULT_CERT=1
With MQ Client v8 and later you can use CertificateLabel=anylabelvalue in the SSL stanza of the mqclient.ini. Example below:
SSL:
CertificateLabel=anylabelvalue
The CertificateLabel setting is documented in the IBM MQ v8 KC page "SSL
stanza of the client configuration file".
A template for mqclient.ini can be copied from /var/mqm/mqclient.ini and placed in the same directory where the application executes. The location can also be specified via a environment variable and MQ will look in a few other locations for the file if it the variable is not set and it does not find it in the same directory where the application executes. The various ways in which MQ will look for this file is documented in the IBM MQ v8 KC page "Location of the client configuration file"

SoapClient fails to load WSDL where SoapUI has no issue

At my office I can connect directly to the relevant soap servers and my code works as it should.
However - because of security - it is not allowed that VPN connections (from home) access the SOAP servers. So I have to resort to using an SSH tunnel over a jump-station.
Because the WSDL files contain absolute urls I can't use the "location" to change this, it won't load the WSDL. Therefor I've resolved to adding entries to the hosts files mapping that server name to 127.0.0.1 and and SSH tunnel to our jumpstation forwarding the correct ports.
This allows me to use the original WSDL without modification. I just have to comment out the hosts entries when at the office.
Via SoapUI everything works. I can load the WSDL, it fully parses it (it has a lot of includes - big corporate soap service) and I can launch SOAP requests that get answered correctly.
However, if I do the same via the php SoapClient (running against an apache on localhost) it throws an exception:
SOAP-ERROR: Parsing Schema: can't import schema from 'http://wsdl.service.addres:port/wsdlUri?SCHEMA%2Fsoa.osb.model%2Fsrc%...'
(I've replaced the server and the rest of the request because its not relevant.)
If I take that entire URL and paste it into my browser it results in an WSDL-XML.
The PHP code to load create the SoapClient is as simple as this:
$options =
[
'trace' => 1,
'exception' => 1,
];
$wsdl = '/path/to/local/wsdlfile.xml';
$client = new \SoapClient($wsdl, $options);
Anyone have a clue where to look? I can't think of anything to try anymore.
I know for sure that my PHP is setup correctly (as the code has been working for months at the office and nothing was changed here). Is PHP doing DNS resolving differently and somehow getting another (or no) IP for that corporate server perhaps?

Zend_OpenId_Consumer login function hangs - even in the sample code

I've been trying to get a simple OpenId log-in working using first php-openId (Jain) and then the ZendFramework, and in both cases I run up against a similar problem. I'm using the example Zend code from step 38.2.2 of http://framework.zend.com/manual/en/zend.openid.consumer.html.
I see the log-in page fine, but when I enter my openId, this code executes:
if (isset($_POST['openid_action']) &&
$_POST['openid_action'] == "login" &&
!empty($_POST['openid_identifier'])) {
echo "New consumer";
$consumer = new Zend_OpenId_Consumer();
echo "Gotim";
if (!$consumer->login($_POST['openid_identifier'])) {
$status = "OpenID login failed.";
echo "Failure";
}
echo "Continuing";
The code seemingly hangs on the function call to $consumer->login. I see the output from my first two echos, but I never see either Failure or Continuing (and without the echos I just get a 500 Server Internal Error when I try to log-in). If I enter an invalid openId then I get the correct "OpenID login failed." error message.
I apologize if I'm being a moron here. I'm a former programmer who got promoted to management, and I'm trying to get back into it - but I'm using a bunch of technologies that are new to me (PHP, OpenID, Zend Framework), and this just isn't making any sense right now.
Does anyone have any ideas where to look? I can't believe that the Framework itself is bugged (especially not when I had a similar problem with the Jain openId stuff also). I'm using fatcow.com for web hosting - not sure if that's relevant.
Your code seems to be failing when trying to make the external HTTP connection to the OpenID provider. This could be caused by a number of things, but here's a couple suggestions:
Perhaps you're behind a proxy?
If not that, maybe your current host does not allow contacting remote servers using sockets (the url_fopen ini directive). The default adapter for Zend_Http_Client is its socket adapter.
So, I would recommend trying is to pass a Zend_Http_Client instance using a different adapter to your Zend_OpenId_Consumer class using the setHttpClient method.
There are a couple different client adapters available:
Zend_Http_Client_Adapter_Socket (default)
Zend_Http_Client_Adapter_Proxy
Zend_Http_Client_Adapter_Test
Zend_Http_Client_Adapter_Curl
Try something like this:
$client = new Zend_Http_Client(null, array(
'adapter' => 'Zend_Http_Client_Adapter_Proxy',
'proxy_host' => 'example.com',
'proxy_port' => 8000,
'proxy_user' => 'user',
'proxy_pass' => 'pass'
));
$consumer = new Zend_OpenId_Consumer();
$consumer->setHttpClient($client);
Your ISP should have documentation about their proxy, if this is in fact the issue.
If proxying is not your problem, try the curl adapter and see if you have any more luck.
I still don't really understand what's wrong, but I tried running the same code on a different web host (x10hosting.com) and my sample code worked immediately. Therefore I have to conclude that this is some kind of limitation of the Fatcow webhosting service, which is rather a shame. I guess I'll be changing hosts.
If you are getting a 500 error, there is definitely a more detailed message in the web server logs. If your server is running apache, then it is most likely under /usr/local/apache/logs/error_log or /etc/httpd/logs/error_log or something similar. I am assuming you are on a shared hosting plan, so you won't have access to these. You should contact your webhosting company, as they will be able to look through the logs for you.

Categories