I've been looking around for a way to check if a user has connected to my website using the right (my server's) SSL certificate in PHP. I guess my question is similar to this one,
except that I'm not implying the MITM.
I've looked around but didn't find a way to get the info from the cert used in the current connection and compare it to the servers cert. This to prevent users connecting with other certs.
How would one go about doing this?
Short Answer
You don't.
Longer Answer
This is only possible with client certificates.
Your webserver identifies you to the client, not PHP. PHP never sees this and no environment variables are sent/passed to PHP aside from HTTPS=on. You don't verify yourself to yourself as it would always evaluate true. The question you linked to already has the answer, and man in the middle doesn't mean much. You are effectively asking for a solution to any and all SSL Man in the Middle attacks with a definitive PHP script.
There is no such thing as being able to connect to your host with another SSL certificate, unless something Diginotar-esque happens to your Certificate Authority.
If you want assurance that they really meant to connect to your site, simply just check the host the client connected to. If they hit your IP, and requested a certificate from it, and then verified it with your CA, they will continue with the connection, and connect to $_SERVER["SERVER_NAME"] and $_SERVER["HTTPS"] == "on"
But, honestly, why are you worried about an incorrect SSL certificate? It will be flagged by the user's PC. I'd be more worried about session decrypting.
I have no idea what you mean by "to prevent users connecting with other certs" which sounds impossible to begin with, but:
SSL happens at a layer in the protocol stack to which PHP has no access or control.
The security of your clients' connections [in the context of running a website] is beyond your control.
Related
I'm using multiple services to accept mobile payments for stuff like virtual currency.
Many companies will include an MD5 signature in the POST or GET callback which I can calculate to verify that the request is authentic and then reward the user with the purchased credits.
This method is very secure as it's nearly impossible to guess the signature.
Other companies will not provide a signature and just tell me to check if the call is from their server IP like the following code:
<?php
if(!in_array($_SERVER['REMOTE_ADDR'],array('xxx.xx.x.xx'))) {
header('HTTP/1.0 403 Forbidden');
die('Error: Unknown IP');
}
?>
Is this IP check secure enough?? Isn't it now very easy to spoof an IP address and make a GET or a POST request using that IP?
The other answers are incorrect. So I'll write my own.
With the exception of exceedingly rare situations, REMOTE_ADDR is 100% trust worthy. It comes from the TCP connection to the server, so it's practically impossible to forge without actually compromising something on the network (like the router the IP belongs to) or without having your server misconfigured (severely, Apache doesn't even let you misconfigure it like that).
So, there are two questions that I can see:
Is it safe to trust REMOTE_ADDR
Yes.
If the REMOTE_ADDR variable in PHP indicates the request came from their server, then it came from their server.
If you're using a remote proxy, then X-HTTP-FORWARDED-FOR is not to be trusted. That's where you can get into problems if you're not careful.
Is MD5() of the request safer than REMOTE_ADDR verification
NO!!!
It's a lot easier to forge an MD5 signature than it is to forge an IP address (which requires you to breach specific network hardware). And if the attacker breaches the network hardware, the game is over anyway.
What's The Best Solution?
The best solution is three fold:
Use HTTPS with Certificate Pinning
On your app, store the public key of their server. Then force the peer verification to use that certificate. That means that an attacker would need to steal the certificate of the remote server to be able to connect.
Verify IP Addresses
Using REMOTE_ADDR
Sign requests using HMAC+SHA2
Use HMAC with SHA-256 or SHA-512.
But yes, the IP check alone is quite secure.
To go deeper, we'd need to go into what types of attacks you're defending from.
Relying on server remote address is not a secure way since IP spoofing can breach the security.
But yes there are some ways by which you can prevent it like key exchange between the machines but still there is no assurance.
Better you should not rely on IP based security.
I was looking at this question and, to try to find the mistake, went to the PHP manual where I seen those 2 options :
CURLOPT_SSH_PRIVATE_KEYFILE The file name for your private key. If
not used, libcurl defaults to $HOME/.ssh/id_dsa if the HOME
environment variable is set, and just "id_dsa" in the current
directory if HOME is not set. If the file is password-protected, set
the password with CURLOPT_KEYPASSWD.
CURLOPT_SSLKEY The name of a file containing a private SSL key.
OP of that question uses a CURLOPT_SSH_PUBLIC_KEYFILE so I guess it should uses a CURLOPT_SSH_PRIVATE_KEYFILE instead of a CURLOPT_SSLKEY, but I don't really know the difference between those options.
So here comes my question :
What is the difference between CURLOPT_SSLKEY and
CURLOPT_SSH_PRIVATE_KEYFILE ?
Well, I found the difference between SSH and SSL in this IT Security question.
Thomas Pornin answered :
SSL and SSH both provide the cryptographic elements to build a tunnel
for confidential data transport with checked integrity. For that part,
they use similar techniques, and may suffer from the same kind of
attacks, so they should provide similar security (i.e. good security)
assuming they are both properly implemented. That both exist is a kind
of NIH syndrome: the SSH developers should have reused SSL for the
tunnel part (the SSL protocol is flexible enough to accommodate many
variations, including not using certificates).
They differ on the things which are around the tunnel. SSL
traditionally uses X.509 certificates for announcing server and client
public keys; SSH has its own format. Also, SSH comes with a set of
protocols for what goes inside the tunnel (multiplexing several
transfers, performing password-based authentication within the tunnel,
terminal management...) while there is no such thing in SSL, or, more
accurately, when such things are used in SSL they are not considered
to be part of SSL (for instance, when doing password-based HTTP
authentication in a SSL tunnel, we say that it is part of "HTTPS", but
it really works in a way similar to what happens with SSH).
Conceptually, you could take SSH and replace the tunnel part with the
one from SSL. You could also take HTTPS and replace the SSL thing with
SSH-with-data-transport and a hook to extract the server public key
from its certificate. There is no scientific impossibility and, if
done properly, security would remain the same. However, there is no
widespread set of conventions or existing tools for that.
So we do not use SSL and SSH for the same things, but that's because
of what tools historically came with the implementations of those
protocols, not due to a security related difference. And whoever
implements SSL or SSH would be well advised to look at what kind of
attacks were tried on both protocols.
I am now able to answer the question without guessing :-)
Currently I'm developing an REST API
my API access are only between my server and my client server(B2B, business to business). example : myserverapi.com(My REST API Server) and myclientserver.com(My Client Server who access My API) *no 3rd connection/application
we are implementing api_key(of course it a must), and domain name(so the client specific the domain name that he will access the api, so my server api will only accept from that)
for myserverapi.com, how to only receive connection from myclientserver.com ? is only using $_SERVER['REMOTE_ADDR'] ? Is that enough ? but after reading from a few place, that i can't use that because the IP may be wrong if it under proxy or loadbalancer farm. What's the solution ?
and how if i installed SSL Certificate ? is i must change my code ? or just buy and install on the server side that automatically my api will be secure ?
is openssl-verify function only the secure way to realy get know that the access are from specific server or not ? http://php.net/manual/en/function.openssl-verify.php Is that mean i must change my code to encrypt and decrypt data like in this link http://3stepsbeyond.co.uk/2010/12/openssl-and-php-tutorial-part-two/
so basically i just want to make sure for myserverapi.com to only get access from myclientserver.com. and the myclientserver.com only accept data from myserverapi.com. how to do that ?
I hope some one give me a good explain.
Thank You
There's a series of things you can do. The most code agnostic is HTTPS client certificates. If you don't care about who the user exactly is, but just want to make sure that is an allowed one let apache handle that. It's only a few lines in the config file, and you won't have to touch your code.
Your second option is ACLs, which you can handle again within Apache even at the path level. And of course at an OS level you can apply them as well to IPs and ports. Same applies to a firewall in or in front of your server/servers.
If you don't want to deal with managing certificates or IP/port firewalling and ACLs you can implement 2 legged Oauth. The clients can use a library since they exists in virtually any language already, and the code for the server is not too complicated.
For the SSL Certificate, you don't have to alter your code. You may, however, want to check the server port and throw an exception if not SSL:
if ($_SERVER['SERVER_PORT'] != 443) {
header('405 Method Not Allowed');
exit();
}
Of course, you could accomplish similar restrictions using Apache, by simply not serving out on port 80.
For the IP restrictions, $_SERVER['REMOTE_ADDR'] should work under most conditions. Unfortunately, I don't think there's a way around proxies. However, an API key should be sufficient for most security requirements.
A little late for an answer, but for checking remote_addr for requests that might come via proxy you can check if
X-forwarded-for header is set, and use that if it exists.
http://en.wikipedia.org/wiki/X-Forwarded-For
I have a web site written in PHP and I want to be sure that my visitors use my SSL certificate, I mean I want to be sure that there is no man in the middle. How I'll do it?
Edit: Any trick to send certificate name from POST or GET?
Edit: Or I'll send a hash to a user computer, the user computer will hash the cert name with a javascript, and compare both of them whether they mach or not. Not best solution but better than nothing.
You cannot do that: MitM attack is based on the fact that the person between server and valid client already has all the valid certificates. So for your server he behaves like any other valid client.
Assuming that there is a man in the middle, all information that "your visitor" provides (which you might somehow use to identify what certificate they are using) would actually be information that the man in the middle provides. That means you cannot trust it (which is a good rule of thumb really even when there is no MITM).
In other words, this is not possible.
Another way you could reach this conclusion is this: if this were somehow possible, "man in the middle" would not be a term we all know today.
As #Jon and #zerkms have already said, it's the client's responsibility to check the server certificate.
One way you could make sure, as a server, that the client is using a connection that has presented your server certificate is to request client-certificate authentication. Indeed, during the handshake with a client certificate, the CertificateVerify TLS message contains the signature of a digest of all the handhsake messages that have been exchanged so far, including the server certificate. If the TLS handhsake succeeds, the client will have sent the correct signature, verifiable against its certificate.
Of course, from the server point of view, this only works if you trust the client certificate.
This wouldn't completely solve the problem, in particular because it's not advisable for a client to accept to authenticate using its certificate against a server it cannot verify (even if the private key wouldn't be leaked, the identity of the certificate would be sent to the rogue party).
Again, at the end of the day, it's still the user's responsibility to decide whether it trusts the identity of the server.
I have a program written in PHP, and I'd like to make sure that login pages etc. are all served over SSL. Is there any good start to finish tutorial for doing so?
Also, does this affect my code in any way, or is it just a matter of getting a SSL cert, and setting up a server correctly?
If your html code contains absolute urls ("http://my-domain.com/...") to:
stylesheets
images
javascripts
Browsers will complain "This page contains both secure and non-secure items".
Use relative urls if you can, or link to "https://my-domain.com/..." urls.
Use free certificates
You don't have to spend money to get valid SSL certificate:
Let’s Encrypt
Let’s Encrypt is a free, automated, and open Certificate Authority.
It depends on the hosting how easy this is to setup, it could be just a checkbox.
The process is well documented on https://letsencrypt.org/
StartSSL
For more traditional certificates, you can get a "StartSSL™ Free" from StartCom.
The site also contains information on installing the certificate.
Firstly a word of warning. if you are considering using SSL its because you have something to protect. Therefore take the time to understand what you are doing every step of the way. Security (not just SSL) is a minefield even for the experienced.
I don't know of any tutorials, but there are plenty of gotcha's you have to be aware of.
Rolling your own ssl cert for testing purposes is free, but you will need to install it on your server.
Most of the time your code does not need to be any different for an ssl page or non ssl as the code itself is ssl agnostic, but as Bob says you must be careful of things like images.
Also redirects can cause popups to warn the user of redirections.
To test if the code is being called from a browser using SSL check for the SSL flag $_SERVER['HTTPS'] this should be a non empty value if SSL is being used.
$ssl_is_on = $_SERVER['HTTPS'] ? true:false;
Personally I prefer to keep my SSL code in a separate folder altogether and use apache to direct all SSL connections to that folder. that way I can be confident a script that should be protected by SSL is not called from a non SSL connection.
If you are logging them in under SSL and then redirecting them to non SSL pages you may need to account for domains and cookies
for example I always use a different domain for ssl normally https;//secure.blah.com and then redirect them to the non secure domain http;//www.blah.com so your cookie domain will need to be blah.com the default is the full domain name which means cookies for secure.blah.com won't be sent to www.blah.com and therefore your users will never be logged in.
Don't use this technique if you use a shared domain name otherwise you could have a problem with cookie information being leaked.
DC
It should not affect your code. Add modrewrite rules to your Apache config. Yes, just obtain an SSL cert (you'll need to pay to have it signed by Verisign or another certificate authority).