PHP , MySQL and SSL authentication - php

I have a question about building a authentication system with SSL certificates. My Idea is to store the data in the database(I know how to do that) and when the user gives the certificate the system to check the cert values and to know where to put the user. But there are some things that are not quite clear(I might sound nooby, but don't judge me)
How to make the certificate with PHP?
How to make the system to request a specific details from the client?(As is on StartSSL)
Do I have to sign the private certificate or something?
P.S: I am using HostGator Business Plan if this makes any difference. I have requested them to issue a private ssl certificate.

I have never used StartSSL however many individuals and companies alike use SSL APIs and auth now, like the new(ish) Facebook sdk.
Note that self signed certificates are not supported as a valid security mechanism by many browsers and other software.
You cannot make SSL certificates in PHP, instead you must make then using a tool like OpenSSL. Here is a brief tutorial I found on Google: http://www.akadia.com/services/ssh_test_certificate.html .
SSL is mainly designed to make the transference of data across the line a little more secure and when reading in connections through PHP you would validate the certificate to see if it matches the one it is supposed to (http://stackoverflow.com/questions/3081042/how-to-get-ssl-certificate-info-with-curl-in-php) much like how a browser downloads a sites SSL cert and then uses that to create a secure connection. I wouldn't imagine you would have a certificate per user.
After this all your data goes over HTTPS rather than HTTP allowing for SSL auth.
Depending on the SSL auth system, if it is an API then your cURL request would be sent over HTTPS rather than HTTP.
If you are making this for a login page on a website then it is a lot simpler than I have said above (well in theory, there are still a lot of thing you can mess up). If you are doing this then you would simply add the SSL cert to your server and then add it to your server config (another quick tutorial for Apache from Google: http://www.digicert.com/ssl-certificate-installation-apache.htm ) and then literally proceed as you normally would redirecting the user to a https of the login page and the login processing page (making sure you have a vhost for 443 if your in Apache).
Edit: Openssl does have a PHP API as I just remembered so I was wrong there.
This is how I see SSL auth going down.

1) Method for create new SSL certificate with PHP^
$dn = array(
"countryName" => 'Country',
"organizationName" => 'Org',
"commonName" => 'Common name',
"emailAddress" => 'email#email.com',
);
$configArgs = array(
'digest_alg' => 'SHA1',
);
$clientKey = openssl_pkey_new();
$csr = openssl_csr_new($dn, $clientKey, $configArgs);
$password = trim(base64_encode(openssl_random_pseudo_bytes(8)), '/=');
$cert = openssl_csr_sign(
$csr,
'file:///etc/ssl/ca/ca.pem',
'file:///etc/ssl/ca/ca.pem',
1095,
$configArgs,
$serial
);
openssl_pkcs12_export($cert, $clientCertPkcs12, $clientKey, $password);
openssl_x509_free($cert);
$sslData = array(
'serial' => $serial, // random serial
'sslkey' => $password,
'created_at' => time(),
'sslpfx' => $clientCertPkcs12
);
openssl_pkey_free($clientKey);

Related

Trouble with ActiveMq with Ssl using Php Stomp

I'm having problems connecting a Php client app to an Ssl enabled ActiveMq installation. I've looked at many sources and am getting more confused as I go.
My setup so far uses authentication via users/groups.properties and authorizationPlugin. This works fine on regular connections
For ActiveMq Ssl I followed a few articles and created the Jks store and certs and also configured with the following
<sslContext>
<sslContext keyStore="file:${activemq.base}/conf/server.ks"
keyStorePassword="$STORE_PASS"
trustStore="file:${activemq.base}/conf/server.ts"
trustStorePassword="$STORE_PASS" />
</sslContext>
<transportConnector
name="stomp+ssl" uri="stomp+ssl://0.0.0.0:61617?needClientAuth=true"/>
I also tried the ACTIVEMQ_SSL_OPTS approach. Both load fine when starting the server. Logs show Sll connector started. I also checked the php cli to make sure Sll is enabled on stomp installation
The problem I'm having is with the Php stomp client. First, these are the articles I read.
http://activemq.apache.org/how-do-i-use-ssl.html
http://php.net/manual/en/stomp.construct.php
https://github.com/stomp-php/stomp-php/wiki/Connectivity
From my understanding, there are two php stomp libs based on the documentation I can't figure out how to set all this up. The php site docs simply give an example of using the constructor with ssl protocol
$link = stomp_connect('ssl://localhost:61612', $user, $pass, $headers);
This doesn't work, I get a null cert error in the logs.
The other article that uses FuseSource stomp has options for including a client cert when establishing a connection but after getting further into the article it looks like it's just to authenticate via Sll cert and not with a user/pass.
https://github.com/rethab/php-stomp-cert-example/blob/master/README.md
So I went back to the previous stomp installation thinking there's a way to pass the client cert files but there doesn't seem to be an interface for it and no docs on the headers param which I'm assuming is not how to go about this.
Can someone shed some light on were in this complex mess I went wrong.
I don't know if you're still interested, but just in case someone stumbles upon this question hoping for an answer.
We're using https://github.com/stomp-php/stomp-php/ for our Stomp connection and this is roughly how we create the client:
function createClient($broker_url, $login, $password) {
$client = new \Stomp\Client($broker_url);
$sslContext = [
'ssl' => [
'cafile' => '/path/to/cert',
'verify_peer' => true,
'verify_peer_name' => false,
'ciphers' => 'HIGH',
],
];
$client->getConnection()->setContext($sslContext);
$client->setLogin($login, $password);
$client->connect();
return new \Stomp\StatefulStomp($client);
}
$broker_url should be in the format ssl://host:port.

Laravel Passport: 401 Unauthenticated, but encryption keys are the same

I have a weird problem with Passport authentication deploying my application to a new site, in spite of the fact that I haven't had a problem with this before.
I'm using Passport's password grant feature to log in to the application (Laravel 5.4). This happens by the user posting his username/password as JSON to the site, and from there it posts those credentials to OAuth/token to get the API key for the site. This has worked fine in the past, and my OAuth keys are committed to the repository.
The other day I managed to deploy the site to a new server (with the same OAuth keys), regenerated (I think) the app key, loaded my data that has the encrypted passwords, and authentication worked fine. Today, however, I did the same thing on a different branch and now logging in returns 401 Unauthorized.
I've done nearly every permutation I could think of: delete the OAuth keys, regenerate app key, reinstall passport, and yet the app refuses to log in. I don't believe it has anything to do with the source code because no changes I made on this branch would affect the login system.
I even tried copying the working site's app key and OAuth keys to the broken site still don't work.
What makes this even more puzzling to me is that I have another site with a different app key, different OAuth keys, but the same data, and the login system works fine.
I don't understand the league/oauth2 package enough to understand why this isn't working. What am I missing?
Thanks, Matt
Well, bad code doesn't pay.
What I didn't mention was that the site I'm trying to deploy is an attempt to move a domain over to a new server. Both the broken site and it's active sister site have the same domain name. This is the code that posts to my own domains /oauth/token endpoint:
$username = Input::json('username');
$password = Input::json('password');
$page = Input::get('page');
$client = Client::find(2);
$http = new GuzzleHttp\Client();
try {
$response = $http->post(url('oauth/token'), [ # <-- see the problem?
'form_params' => [
'grant_type' => 'password',
'client_id' => '2',
'client_secret' => $client->secret,
'username' => $username,
'password' => $password,
'scope' => '',
],
]);
#...
The problem is rather hilarious. Since the DNS hasn't switched for the site yet, the requests to my own site are going to the old site, not this one. Hence I'm getting back Unauthenticated. Because I'm not.
The solution was to edit the /etc/hosts file on the server to point the domain to itself. That way, any calls to its own api will actually go its own api. Problem solved.

FTPS with CURL PHP

I'm busy with a curl php library which needs to connect to an FTPS server.
I have this semi working... If I connect to ftp://domain.com then it does work. If I watch the comms on the server with tcpflow I see it logging in with AUTH TLS and and all the comms is encrypted. The file is uploaded so all's good..
What I'm unsure of is if its valid to try connecting instead to ftps://domain.com?
The reason I'm asking is because if I change the protocol from ftp to ftps in curl then the login fails and the server (watching tcpflow comms) says that the login has failed:
191.101.002.204.00021-088.099.012.154.51630: 530 Please login with USER and PASS.
Also, when I watch the comms when trying to connect to ftps:// I don't see the client issuing the AUTH TLS command as it does with plain ftp://
The problem I have is that it seems that my client's FTP server we have to ultimately connect to doesn't seem to allow connections without the ftps:// protocol.
If I connect using lftp I can do so using ftps:// but then I have to disable ssl:
set ftp://ssl-allow no
If I try the lftp connection using ftp:// it just hangs on the login command...
I'm not really that experienced with FTP or TLS / SSL so I don't know if its maybe because the client's server doesn't have the certificates set up correctly..
Here is a portion of my curl code which works with ftp:// but not ftps://
// Works
$url = "ftp://proxy.plettretreat.co.za/";
// Does not work
$url = "ftps://proxy.plettretreat.co.za/";
$port = 990;
$username = "ftpuser";
$password = "pass";
$filename = "/test.php";
$file = dirname(__FILE__)."/test.php";
$c = curl_init();
// check for successful connection
if ( ! $c)
throw new Exception( 'Could not initialize cURL.' );
$options = array(
CURLOPT_USERPWD => $username.':'.$password,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_BINARYTRANSFER => 1,
CURLOPT_FTP_SSL => CURLFTPSSL_ALL, // require SSL For both control and data connections
CURLOPT_FTPSSLAUTH => CURLFTPAUTH_TLS, // let cURL choose the FTP authentication method (either SSL or TLS)
CURLOPT_UPLOAD => true,
CURLOPT_PORT => $port,
CURLOPT_TIMEOUT => 30,
);
Another thing I'm unsure of is that my client has given me an IP address to connect to.. Can an IP address be used in ftps? I would have thought that certificates are mostly certifying a domain name?
tl;dr
1) Can I use ftps://domain.com to connect using CURL PHP?
2) If I can use ftps:// in curl, then how do I get curl to log in (issue auth tls command)?
3) Can an FTP server use SSL / TLS with only an IP address?
Thanks...
John
Many many hours of struggling led me to an eventual answer.
Part of the answer was that the client server and the FTP server had "overly" strict firewall rules blocking the passive ports.
I was getting the following error:
Error no: 35; Error: SSL connect error.
Error 35 was because of the firewall rules. Once those were relaxed that error went away, but as a note, you will also see this error if the client machine is NAT'ed. If it is you need to set the curl option:
curl_setopt($c, CURLOPT_FTPPORT, '1.2.4.5' ); // change to your actual IP.
This tells the FTP server where to open up its data channel (instead of trying to open it to the client server's internal address).
Anyway, once the firewall and FTPPORT options were set I got:
Error no: 30; Error: bind(port=0) failed: Cannot assign requested address
This one baffled me for quite a while as everything looked correct.
I eventually stumbled upon a few thread here and elsewhere which talk about an issue with older versions of Curl using NSS for its encryption. I checked and I was using libcurl version 7.19.7 (about 8 years old) and sure enough it uses NSS...
I updated my Curl using this guide: https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in-centos6.
That updated me to libcurl 7.52.1 which uses OpenSSL and lo and behold, my app started working...
So, if you're having issues connecting curl-ftp to a FTPS server, check the FTPPORT (passive IP) if you're NAT'ed, check your firewall, but most importantly, check your curl:
<?php
print print_r(curl_version());
?>
I hope this helps someone..

PHP and SSL CA Verification - OS Independent

Here is a simple PHP script that opens an SSL socket ready to send HTTP requests:
$contextOptions = array();
$socketUrl = 'ssl://google.com:443';
$streamContext = stream_context_create($contextOptions);
$socket = stream_socket_client($socketUrl, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $streamContext);
if (!$socket || $errno !== 0) {
var_dump($socket, $errstr);
exit;
}
var_dump($socket);
exit('Socket created.');
This works - I've just tested it - but there is no validation against a trusted CA store.
We can modify that script to use PHP's SSL Context options:
$contextOptions = array(
'ssl' => array(
'cafile' => 'C:\xampp\cacerts.pem',
'CN_match' => '*.google.com', // CN_match will only be checked if 'verify_peer' is set to TRUE. See https://bugs.php.net/bug.php?id=47030.
'verify_peer' => TRUE,
)
);
$socketUrl = 'ssl://google.com:443';
$streamContext = stream_context_create($contextOptions);
$socket = stream_socket_client($socketUrl, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $streamContext);
if (!$socket || $errno !== 0) {
var_dump($socket, $errstr);
exit;
}
var_dump($socket);
exit('Socket created.');
As long as the 'cafile' exists and has the correct CA then this example also works...
...but how can we do this without hard-coding a CA filename/filepath? We're trying to create something that verifies SSL certificates OS-independently without requiring separate configuration for each server that runs this script.
I know Linux has a directory for CAs that we could put as the 'capath'. What about Windows? Where does it store its trusted CAs? I searched and these unfortunately seemed to be in the registry, so is there no way we can access them from PHP? What about other OSs?
A losing battle ...
There is no way to conduct a secure encrypted transfer in PHP without manually setting the "cafile" and "CN_match" context options prior to PHP 5.6. And unfortunately, even when you do set these values correctly your transfers are still very likely to fail because pre-5.6 versions do not consult the increasingly popular SAN (subjectAltName) extension present in peer certificates when verifying host names. As a result, "secure" encryption via PHP's built-in stream wrappers is largely a misnomer. Your safest bet (literally) with older versions of PHP is the curl extension.
Regarding windows certs ...
Windows uses its own cert store and encodes its certificates in different format from OpenSSL. By comparison, openssl applications use the open .PEM format. Pre-5.6 versions of PHP are unable to interface with the windows cert store in any way. For this reason it's impossible to have reliable and safe encryption in a cross-OS way using the built-in stream wrapper functionality.
PHP 5.6 is a major step forward
New openssl.cafile and openssl.capath php.ini directives allow you to globally assign cert locations without having set them in every stream context
All encrypted streams verify peer certs and host names by default and the host name is automatically parsed from the URI if no "CN_match" context option is supplied.
Stream crypto operations now check peer cert SAN entries when verifying host names
If no CA file/path is specified in the stream context or php.ini directives PHP automatically falls back to the operating system's cert stores (in Windows, too!)
By way of example, this is all you'd need to do to connect securely to github.com in PHP-5.6:
<?php
$socket = stream_socket_client("tls://github.com:443");
Yes. That really is it. No fussing about with context settings and verification parameters. PHP now functions like your browser in this regard.
More reading on the subject
These PHP 5.6 changes are only the tip of the iceberg with regard to SSL/TLS improvements. We've worked hard to make 5.6 the most secure PHP release to date with regard to encrypted communications.
If you'd like to learn more about these new features there is a wealth of information available via the official channels
[RFC] Improved TLS Defaults
[RFC] TLS Peer Verification
5.6 NEWS file
5.6 UPGRADING file
A note on SAN matching in 5.4/5.5
We are working to backport at least the SAN matching to the 5.4 and 5.5 branches as it's extremely difficult to use the encryption wrappers in any meaningful way (as a client) without this functionality. Though this backporting work is largely dependent on my available free time as a volunteer, upvoting this answer might certainly help it happen sooner :)

Connecting to a web service with PHP given only username, password and certificate authority

I am successfully connecting, using Microsoft C#, to a Microsoft web service. I have to supply a username, password (in the C# code); and install a certificate (in .cer format) into the "Root Certificate Authorities" section of the system's certificates.
How can I connect to such a web service in PHP? The reason I ask is that all methods I have seen (such as wsdl2php, which creates a SoapClient subclass), seem to assume various things, such as SSL certificate, SSL key file and SSL key passphrase.
So it all confuses me. I'm not sure what should go where. I'm not sure where my "root certificate authority" (the .cer file) should go, and where the username and password should go. Any ideas?
all can be done whith soapclient and stream_context_create using ssl options
<?php
$context = stream_context_create(array(
'https' => array(
'cafile' => '/path to file',
'verify_peer' => true
)));
new soapclient("https://localhost/index.php?wsdl",array(
'login'=>'admin',
'password'=>'passss',
'stream_context'=> $context
));
it is not uncommon in soap to not use http auth but just an soap-call, the documnetation is essential
it can be rewarding to use soapclient whith classes using classmap to map soaptypes to php clases
Typically if you're calling a webservice using regular SSL your URL will look like:
https://username:password#myserver.com/mywebservice.php
Then there is the issue of the SSL certificate. I'm using something similar to read from an SSL protected SVN web front. I don't know of any other solution other than to log into the server as the user that is running your webserver (apache/IIS) and accepting the certificate manually. In the case of SVN you could make a checkout and it will ask you to accept the certificate. I'm not entirely sure how this would work for a plain HTTPS request but perhaps you can get the certificate by loading the webservice in a browser? (or using wget or something fancy if you're lucky enough to be running Linux)
Also, is your code the PHP code or the C# code? If it's C# you may need to do something else entirely.

Categories