PHP CURL request with PEM certificate unstable results - php

I'm trying to get results from a server which is protected with an PEM cerficate and private key.
I have results, but it is unstable. Most of te time i have response, sometimes an Curl error: NSS: private key from file not found.
The code:
$url = escape($this->url).'/jira/rest/api/2/project';
// Variables
$apiGrantType = 'client_credentials';
$cliendId = $this->username; // Client ID
$clientSecret = $this->password; // Client Secret
$certUserPwd = $cliendId . ":" . $clientSecret; // Client ID:Client Secret
$certFile = PATH_FILES.'certificate/cert.pem'; // Private Cert
$certKey = PATH_FILES.'certificate/key.pem'; // Private Cert
$certPassword = '*****'; // Cert Password
$curl = curl_init();
curl_setopt($curl, CURLOPT_USERPWD, PWSTR);
curl_setopt($curl, CURLOPT_URL, $url);
//curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_SSLCERT, $certFile);
curl_setopt($curl, CURLOPT_SSLKEY, $certKey);
curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $certKey);
//curl_setopt($curl, CURLOPT_TIMEOUT, 30);
//curl_setopt($curl, CURLOPT_VERBOSE, 1);
if(!$exec = curl_exec($curl))
{
echo 'Curl error: ' . curl_error($curl);
}
dump($exec);
Sometimes it runs perfectly and returns the requested response.
Othertimes it return Curl error: NSS: private key from file not found.
Server where request is calling from is running on CENTOS 7 and PHP 7.2.
How can i make my request stable?

I know this is an old thread, but recently had same issue.
Based on the above code, it looks like the curl definitions are within a class, and that one is used with include/include_once or require/require_once.
In my case seems that using include_once/require_once was the reason, hence
you can replace any include_once/require_once in favour of include/require.
alternatively use something like
if (!class_exists('some_class')) {
include 'some_path/some_class.php';
}

Related

WAMP : PHP Curl "working" but returning empty string, file_get_contents is working

I am running PHP Wampserver 3.2.6 under Windows 11 with Avast Antirus Free edition.
And PHP Version 8.1.0.
Now I have setup a simple curl script to fetch data from a remote host. But this returns nothing.
When I put the entire thing online on a server it works just fine. But from a local machine it doesn't work.
I have tried running the entire thing under postman. And there it works just fine.
private function __curl($url, $decode = true){
// * create curl resource
$ch = curl_init();
// * set url
curl_setopt($ch, CURLOPT_URL, $this->api_url.$url);
// * return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// * set headers
$headers = array('X-IM-API-KEY: '.$this->api_key);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_HEADER, true);
// * if any postdata set
if(!empty($this->postdata)){
// * initialize post
$this->__post();
// * set postdata
curl_setopt($ch, CURLOPT_POST, count($this->postdata));
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->postfieldstr);
}
// $output contains the output string
$output = curl_exec($ch);
// close curl resource to free up system resources
curl_close($ch);
// * if no decode
if(!$decode) return $output;
// * return result
return json_decode($output, true);
}
When I just use file_get_contents it works fine.
file_get_contents($this->api_url.$url);
The result :
{"success":false,"error":true,"message":"fields
missing","data":{"email":"not set","password":"not
set","app_version":"1.1"}}
Of course it will give an error because it expects POST parameters with the username and password.
I have the following configuration visible under PHPinfo :
I hope someone can tell me what my mistake would be.
EDIT
When I add :
curl_error($ch);
I get the following error :
SSL certificate problem: unable to get local issuer certificate
But when viewing the address in FireFox I get no error at all.
(letscrypt)
EDIT : Answer added by : #codenathan
Adding the following code to disable host and peer verification does the trick actually.
I think in combination with the local firewall the letscrypt certificate simply didn't get through in the way it was supposed to.
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
Since I need this for developement purposes this actually does the trick for me.
curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
However it is not ideal to switch this off : https://www.saotn.org/dont-turn-off-curlopt_ssl_verifypeer-fix-php-configuration/

Working with ChannelAdvisor REST API in PHP - requests not working in CURL

I would like to integrate with ChannelAdvisor REST API using the SOAP Credentials Flow.
Based on their documentation, I have setup the following in PostMan (rest client in Chrome browser) like this:
When I make the rest; the rest api server returns the expected response:
So, I tried to replicate this in PHP with the following class:
<?php
class ChannelAdvisorREST {
/**
* ChannelAdvisor constants & properties
*/
const BASE_URL = 'https://api.channeladvisor.com/v1';
private $config;
/**
* Class constructor
*/
public function __construct()
{
$this->config = \Config::get('channeladvisor');
}
// TEST
public function test($accountId)
{
// var_dump($this->config);
var_dump(self::getAccessToken($accountId));
}
// TEST
/**
* Method to get access token from rest server.
*
* #param $accountId
* #return string
*/
private function getAccessToken($accountId)
{
return self::curlPOST('/oauth2/token', [
'client_id' => $this->config['api_app_id'],
'grant_type' => 'soap',
'scope' => 'inventory',
'developer_key' => $this->config['api_developer_key'],
'password' => $this->config['api_password'],
'account_id' => $accountId
]);
}
/**
* Method to generate a HTTP POST request
*
* #param $endpoint
* #param $fields
* #return string
*/
private function curlPOST($endpoint, $fields = array())
{
// Open connection
$ch = curl_init();
// Set the url, number of POST vars, POST data
curl_setopt($ch, CURLOPT_USERPWD, $this->config['api_app_id'] .':'. $this->config['api_shared_secret']);
curl_setopt($ch, CURLOPT_URL, self::BASE_URL . $endpoint);
curl_setopt($ch, CURLOPT_POST, count($fields));
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields, '', '&'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded'
));
// Execute post request
$result = curl_exec($ch);
// Close connection
curl_close($ch);
// Finished
return $result;
}
}
When I execute the test($accId) method on this class, I get the following response:
boolean false
Any idea why it isn't quite working as same as the PostMan test?
P.S. I have already verified all the config/parms etc... are correct and same as my PostMan test. This class is a snipped version from my original code (created in Laravel 4.2, but this issue is not related to Laravel).
Two things:
Make sure that you send the same headers as your browser sends. For example, I don't see the Authorization-header in your code, and that one is probably quite crucial for authorizing the request on the server-side. Also for the scope you use 'inventory' instead of 'orders inventory'. Be very strict in this exercise.
Test the post-data not in an array, but write down the query-string as it should be according to yourself, this way you know there is not some issue by CURL trying to convert your array into a query-string (note, both is possible for CURL, array and query-string).
So most easy to test with:
client_id=1234&grant_type=soap&scope=order%20inventory...etc add other variables...
I have found the problem. The issue was caused by my php not being configured with curl.cainfo.
I found this by adding the following debug code to my curlPOST method like this:
private function curlPOST($endpoint, $fields = array())
{
// Open connection
$ch = curl_init();
// Set the url, number of POST vars, POST data
curl_setopt($ch, CURLOPT_USERPWD, $this->config['api_app_id'] .':'. $this->config['api_shared_secret']);
curl_setopt($ch, CURLOPT_URL, self::BASE_URL . $endpoint);
curl_setopt($ch, CURLOPT_POST, count($fields));
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields, '', '&'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded'
));
curl_setopt($ch, CURLOPT_VERBOSE, true);
$verbose = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
// Execute post request
$result = curl_exec($ch);
// Debug error
if ($result === FALSE) {
printf("cUrl error (#%d): %s<br>\n", curl_errno($ch), htmlspecialchars(curl_error($ch)));
rewind($verbose);
$verboseLog = stream_get_contents($verbose);
echo "Verbose information:\n<pre>", htmlspecialchars($verboseLog), "</pre>\n";
}
#fclose($verbose);
// Close connection
curl_close($ch);
// Finished
return $result;
}
This outputted the following error message:
cUrl error (#60): SSL certificate problem: unable to get local issuer certificate
Verbose information:
* Hostname was found in DNS cache
* Hostname in DNS cache was stale, zapped
* Trying 216.27.89.14...
* Connected to api.channeladvisor.com (216.27.89.14) port 443 (#7)
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 7
boolean false
Which helped me track down the issue with my php.

PHP curl post request to server using cloudflare (Full SSL) has SSL error and Blank SESSION Cookie

Hi I'm doing a website right now. Both of these files is in one server and domain and I'm using cloudflare to boost the loading. I'm using Full SSL option on cloudflare because I bought my own SSL Geotrust on my server. I already upgraded my curl on the server to 7.41.0.
One php file consist of the function
Function File:
<?php
function get_content($session){
$endpoint = "https://sample.ph/php/resource.php";
// Use one of the parameter configurations listed at the top of the post
$params = array(
"yel" => $session
);
$curl = curl_init();
curl_setopt($curl,CURLOPT_URL,$endpoint);
$strCookie = 'PHPSESSID='.$_COOKIE['PHPSESSID'];
curl_setopt($curl, CURLOPT_COOKIE, $strCookie);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_VERBOSE, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
$postData = "";
//This is needed to properly form post the credentials object
foreach($params as $k => $v)
{
$postData .= $k . '='.urlencode($v).'&';
}
$postData = rtrim($postData, '&');
curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($curl, CURLOPT_HEADER, 0); // Don’t return the header, just the html
curl_setopt($curl, CURLOPT_CAINFO,"/home/sample/public_html/php/cacert.pem"); // Set the location of the CA-bundle
session_write_close();
$response = curl_exec($curl);
if ($response === FALSE) {
return "cURL Error: " . curl_error($curl);
}
else{
// evaluate for success response
return $response;
}
curl_close($curl);
}
?>
Resource File
<?php
session_start();
if(isset($_POST['yel'])){
$drcyt_key = dcrypt("{$_POST['yel']}");
if($drcyt_key == $_SESSION['token']){
echo "Success";
}
}
?>
How do you think will I fix this?
The SSL Verification error. Upon debugging sometimes I got cURL Error: SSL certificate problem, verify that the CA cert is OK. Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
Sometimes I got cURL Error: SSL peer certificate or SSH remote key was not OK
When I put curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); to FALSE, which is not a good idea; There comes a second problem for the SESSION COOKIE becoming blank on first load.
I HOPE YOU CAN HELP ME. THANK YOU.
This issue looks to be an outdated certificate bundle or outdated OpenSSL version on the server. You should both ensure you have the latest root certificates on your computer and also ensure that you have the latest versions of OpenSSL (including the PHP OpenSSL module).

Connecting to FTPS over PHP

I am currently working on a project that involves connecting to the clients FTPS server and downloading a file that they are updating automatically with the data we require. I am wanting to access this server via PHP so I can automate it.
The issue that I have is that FTP_SSL_CONNECT will NOT work due to the client using Implicit TLS security on the server.
Does anyone have any experience of getting this connection working?
Thanks,
T
$username = 'username here';
$password = 'password here';
$get_file = "file to get here";
//set ftps url
$url = "ftps urls here";
$local_prefix = 'local folder prefix here';
$location = "ftps://" . $username . ":" . $password . "#" . $url;
$port = "any port number here";
//********************************************************************//
//initialize cURL and begin
print("Initializing cURl and saving file from scure ftp.");
$curl = curl_init();
//create or open a file for writing
$file = fopen("$local_prefix$get_file", "w");
//set cURL options *note* these must occur in order for implicit ftp to work correctly
curl_setopt($curl, CURLOPT_URL, "$location$get_file");
curl_setopt($curl, CURLOPT_PORT, "$port");
curl_setopt($curl, CURLOPT_USERPWD, "$username:$password");
curl_setopt($curl, CURLOPT_FILE, $file);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
curl_exec($curl) or die("did not save file");
curl_close ($curl);
fclose($file);
You need to add these option to curl call
CURLOPT_FTP_SSL => CURLFTPSSL_ALL, // require SSL For both control and data connections
CURLOPT_FTPSSLAUTH => CURLFTPAUTH_DEFAULT, // let cURL choose the FTP authentication method (either SSL or TLS)

Upload file to Bitbucket via PHP script

I try to upload my database to bitbucket downloads section of my repository via a PHP script using curl library. Normally i go to my phpmyadmin and export my database to a folder, then going to my bitbucket account under downloads section of my repository and upload manually. I need a script that automates these tasks.
I tried using curl library like this:
// bitbucket username and password
define('USERNAME', 'my_username');
define('PASSWORD', 'my_password');
$url = 'https://bbuseruploads.s3.amazonaws.com/';
//This needs to be the full path to the file you want to send.
$file_name_with_full_path = realpath('apache_pb2.gif');
$post = array('extra_info' => '123456', 'file_contents' => '#' . $file_name_with_full_path); // image file example here
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, USERNAME . ":" . PASSWORD);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
//curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$resp = curl_exec($ch);
// validate CURL status
if (curl_errno($ch))
throw new Exception(curl_error($ch), 500);
The result:
<error><code>InvalidArgument</code>
<message>Unsupported Authorization Type</message>
<argumentvalue>Basic hash_code_here</argumentvalue>
<argumentname>Authorization</argumentname>
<requestid>hash_code_here</requestid>
<hostid>hash_code_here</hostid>
</error>
If you need further clarifications please let me know.
Just for the case someone finds this old corpse here:
I solved it by this funktion function:
/**
* Uploads a file to Bitbucket Download area of the configured repository
* Does the same as thi bash command:
* curl -X POST "https://USER:PW#api.bitbucket.org/2.0/repositories/owner/slug/downloads/" --form files=#"test.txt"
* #param string $filename
* #param string $apiEndpoint
* #param string $apiUser
* #param string $apiPassword
* #return bool|string
* #throws Deployer\Exception\Exception
*/
public static function sendFileToDownload(
string $filename,
string $apiEndpoint,
string $apiUser,
string $apiPassword
) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $apiEndpoint);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLAUTH_BASIC, 1);
curl_setopt($curl, CURLOPT_USERPWD, "$apiUser:$apiPassword");
$data = [
'files' => curl_file_create($filename),
];
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$erg = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($erg === false || $status > 300) {
throw new Deployer\Exception\Exception("Result: ".print_r($erg, true), $status);
}
curl_close($curl);
}
}
For me the most important point was this section:
$data = [
'files' => curl_file_create($filename),
];
This results in a local file been uploaded to my repos download area with the name $filename (filename path).
Had this same problem today. "Unsupported Authorization Type" response from an API CURL request.
I spent a long time consulting with Google. In the end, I discovered that my URL had two slashes in it together (accidentally). See, the API I'm connecting to has a URL, and then an API endpoint. Like this:
https://www.example.com/api/v2/foo -or-
https://www.example.com/api/v2/bar
But I accidentally was combining them like this:
curl_setopt($curl, CURLOPT_URL, $url ."/". $endpoint);
given my data:
$url = "https://www.example.com/api/v2/"
$endpoint = "foo"
I ended up with this:
"https://www.example.com/api/v2//foo"
Problem solved by removing the ."/". and just making it this: .
BONUS POSSIBILITY: Something else interesting I learned in the process was that the API folks prefer double-quotes around json data posted to them. I haven't verified it makes a difference, but instead of wrapping double-quotes in single-quotes (or visa-versa), escaping the internal double-quotes is how I left things. And it's working.

Categories