i'm trying to figure out if the following case makes any sense.
Background info
I'm building a PHP platform that makes a lot of REST API calls via Guzzle. I really need to make sure that connections are valid so apart from API authorization, NAT firewalls and valid HTTPS/SSL connections i also want to build some checks in our application. For instance i am also validating that connections come from the valid IP subnets provided by the API service.
The problem
Then i started looking at the SSL certificates provided by the API services we're using. Offcourse the security is based on public/private keys and i cannot see the private keys used by this service but what i could do is make sure the public certificate(s) are still the same as the ones i could store locally.
Now i don't know if that makes any sense or not? My presumption here is that certificates for these organisations will only change once every two years. And i know that it doesn't validate anything, because i don't own the private key, but still, it is an extra check.
Practical
Then there is the practical side, is it even possible, using the openssl s_client i am able to get the certificates used by the API. But the verify option that Guzzle is providing is based on a .PEM file and that uses private keys right?
// Use a custom SSL certificate on disk.
$client->request('GET', '/', ['verify' => '/path/to/cert.pem']);
So is it even possible in Guzzle or basic CURL and would it make any sense or would that just be a false sense of security?
The service provider's public key allows you to do two things:
Send encrypted data that only the service provider can decrypt and no one else.
Once the encrypted connection is established, you know for sure that the messages sent back by the service provider originates from no one else than the service provider.
No one will ever provide their private key. That would defeat the purpose of a key being private. However, the whole idea of a certificate is to provide a public key signed by a trusted root certification authorities such as Verisign. Such trusted root authorities come pre-installed on your O.S. or are added by the system administrator.
If someone manipulates DNS so as to misguide you to connect to a fake host, the encrypted connection will not be established because the fake server will not be able to decrypt your messages as he does not have the proper private key.
You do not need to store anything. Upon connection, being presented with a certificate, you can verify the validity on the fly: as previously explained, if the certificate has been issued by a trusted root certification authority stored and trusted on your machine, you are good to go.
Related
I have a SSL enabled eCommerce website which uses cURL for payment processing. Everything is running well but recently I learned about "CA Public Certification Bundle for cUrl" that its a good idea to use it for cURL connections.
If this is true than can someone tell me how or how is it better/different than using the standard SSL?
Doesn't the SSL already provide some kind of certification for all connections?
Any HTTPS client connected to an HTTPS server will get its certificate (in fact, it can be a certificate chain). This server certificate must then verified by the client to authenticate the server.
This is normally done by using a number of CA certificates that are configured on the client as trust anchors (i.e. this is what you trust in advance, before encountering the server certificate). The client tries build a chain between the last element of the server chain and one of the CA certificates in its trust anchors. If there is such a valid chain the server certificate is trusted.
A "CA certificate bundle" would be a set of trust anchors. You can build your own by looking for CAs you're willing to trust, or you can use an existing bundle. Most OSes or browser come with an existing bundle. cURL in itself doesn't but it can rely on a pre-defined location (set at compile time) or it also suggests to use the Firefox bundle (via a conversion mechanism). (You can override default setting via extra options, on the command line or via the API.)
Certificate Pinning (which you also mention) has nothing to do with a CA cert bundle. In fact, it's almost the opposite. Instead of relying on 3rd party trust anchors (the certification authorities), you explicitly "pin" a set of server certificates you know as directly trusted. They're not used to verify other certificates, instead, you compare the certificate you get with the exact certificate you're expecting for that host (or at least you compare public keys). This is more like having a reference mapping from server name to certificate (or to public key) and comparing what you get from that host with the reference you have. Of course, this can only work for a reasonably small set of certificates in practice, unlike the CA (PKI) approach which is designed to let you authenticate parties you have never encountered before (via a 3rd party: the CA).
How is it better/different than using the standard SSL?
Doesn't the SSL already provide some kind of certification for all connections?
Using a CA certificate bundle isn't different than using "standard SSL", it is what's commonly used for SSL/TLS connections. You often don't see it because that CA bundle is often supplied with your client (or with the OS).
Note that strictly speaking, this is orthogonal to SSL/TLS itself, which mainly just says you should authenticate the server. Certificate verification (the PKI way, via CA certificates) is defined in a different specification, also complemented by a specification on how to verify the name in the certificate (and the HTTPS specification of course).
Found a great answer here. The comment above really helped. The exact keyword I was looking for was "Certificate Pinning".
Is it possible to authenticate a web browser using an ssl certificate.
Say i store a private key in my application, is there any way to read a key from a browser and try to authenticate based on that?
You can authenticate a browser/user using SSL/TLS client-certificate authentication.
The client certificate must be requested by the server, so you'd need access to the server configuration (not just installing some PHP code on a shared server). This is done at the SSL/TLS layer (in fact, the mechanism is not specific to HTTPS): the server requests the client-certificate during the SSL/TLS handshake (sometimes via a renegotiated handshake). In Apache Httpd, this is typically done via SSLVerifyClient (although you'll need to specify other options too).
The server will then verify the certificate against the CAs you've configured it with (possibly your own, and possibly independent of the CAs used for the server certificate itself). (Alternatively, you could disable certificate verification at the server level in some cases, and have the PHP application do it, but this is a bit more advanced and you'd need to know what you're doing.)
You can access the client certificate from your application and obtains its Subject DN (or alternative names), to identify the client.
It's not clear whether you're after identifying a browser or a user. In the end, everything goes through the browser anyway, but client certificates tend to be allocated to users. Users would have to install that certificate into their browser.
EDIT: For further details, it would help if you could clarify your question and what you intend to do with this.
Is it possible to authenticate a web browser using an ssl certificate.
Say i store a private key in my application, is there any way to read
a key from a browser and try to authenticate based on that?
Firstly, strictly speaking, there's no such thing as an "SSL certificate", since multiple types of certificates can be used for SSL/TLS, and some of these same certificates can also be used for other purposes than SSL/TLS. Typically, "SSL certificate" means "X.509 certificate in the context of SSL/TLS".
Therefore, authenticating a web browser using an SSL certificate implies doing it at the SSL/TLS layer. (There have been attempts to implement message-level security using X.509 certificates at the HTTP layer, but they're not widely supported by browsers.)
Secondly, the private key is held by the remote party that you authenticate. The local party that authenticates the remote party doesn't see any private key. If you (as a server) want to authenticate a web browser, it's the browser that needs to have the private key, not your (presumably PHP) application. In this context, it's not quite clear why your (PHP?) application would have/need a private key if it's the browser that you want to authenticate.
What your verifying application may need (if it's not done by the server itself) is a CA certificate to be able to verify the client certificate it is presented with (or at least some form of trust anchors with which to verify the client certificate). There's no private key required here, just public keys and certificates, unless you want your application to be a CA too.
Indeed, you could have your application be a mini CA. It could make the browser generate a key-pair and send a certificate request to the server (there are mechanisms to have a web page make the browser do all that). Then the server would generate the certificate and make the browser import it back against its private key. Subsequently, the browser could use this certificate for authentication with that server (or other servers that would recognise these certificates).
No, you cannot do that.
There is some development going on, and a few day ago W3C has made a proposal for a encryption standard.
You can however put a key in a cookie and use that to identify. This is the default PHP session id behavior.
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 written apps that run on both iPhone and Android. They make calls (HTTP POST requests) to a PHP script on my Ubuntu server that retrieves queries from MySQL server there, etc.
Basically I want to encrypt these messages going back and forth. I have the additional constraint that I am on shared hosting and so do not have root (or sudoer) access on my server.
What is the best way to implement this? I don't want something where I have to pay verisign for a cert (I know very little about security). I am thinking it will somehow involve a public/private key scheme, but I am not sure what is the best method for this.
any thoughts? thanks.
If you are on a shared hosting server, it is likely a bit of a challenge (and, depending on the provider's setup, impossible) to secure your keys from other users. Plus, reinventing the encryption wheel is generally a bad idea. So I wouldn't go that route, especially if you aren't knowledgable about security.
The easiest solution is to use SSL. If you really don't want to get a cert from Verisign (or Comodo or whomever), use your provider's default SSL cert. If they have things set up so that your SSL server will use a cert for https://www.YourHostingProvider.com/, then you should be able to make that work, perhaps with a bit of homework. (MediaTemple is set up that way, where you get their cert if you don't have your own. Not sure about other hosting providers.)
Do try to educate yourself about the elevated risks involved in doing this on the cheap. For example, depending on the setup, it's possible that anyone else on the shared hosting service could impersonate your site if they are sufficiently skilled and determined, since they may have access to the same SSL private key you're using.
If you're securing game high scores or something, that will probably be OK. If you're securing credit card numbers or people's personal health information, this is definitely not the way to go. You don't want to store that kind of information on a shared hosting service, and you definitely don't want to be so resource starved that you're reluctant to buy an SSL cert.
What I would like to achieve is being able to send signed emails so that users' email clients show such mails as trusted (e.g. in Thunderbird sometimes such emails are marked with a closed envelope icon).
I found a PHPMailer library which is supposed to send signed emails, but I think it just wraps the openssl_pkcs7_sign PHP function. I am not sure what are and how to get and use keys and certificates which are function's arguments. There are to parameters: $signcert and $privkey. From what I understand the $signcert should be publicly known certificate used to verify that the message was properly signed. The $privkey is something coupled with the certificate, should be kept in secret and is used to sign emails. Am I correct so far?
My problem is how to get proper certificates. I think OpenSSL allows me to generate my own certs, but are such certs sufficient enough for my needs? If they are, how do I generate them properly? I mean there might be certificates of different length and other options which might be important and I am unaware of. And by the way, I noticed that "https" self signed certs cause warnings in browsers. Does same thing happen with mails? In that case probably I should buy certificate from trusted cert center. Can you give me an advice which centers I should consider?
I think OpenSSL allows me to generate my own certs, but are such certs sufficient enough for my needs?
We don't know what your needs are.
A certificate on its own is useless for signing of messages. The value comes where that certificate is itself (verifiably) authenticated by a third party acceptable to both the the sender and resipient. This 3rd party is the Certification Authority (e.g. Thawte, Verisign etc) although within an organisation it may be sufficient to provide your own CA. The OpenSSL documents explain in detail how to create your own CA certificaet which you can use to sign other certificates.
You've got a long learning journey ahead of you before you'll be able to implement any code - you could do worse than start with the openssl howto and x509 cerificates howto.
Here [1] it is implied that the certificate used to sign emails must be issued by Certification Authority (so the email client won't give any warnings). The list of some Authorities which issue free certificates can be found here: [2]. The one I have chosen gives the certificate by installing it directly to a web browser. So to do something more, it should be exported. Popular format is *.p12, which can store both public and private key. But it seems that using PHP function openssl_pkcs7_sign() and pointing it's arguments to the file with both certificates doesn't work - OpenSSL returns ambigous error error:0906D06C:PEM routines:PEM_read_bio:no start line. After separating keys (using OpenSSL) signing works fine.
By the way, even though the PHPMailer is supposed to be capable of singing emails, it's current version doesn't work. Mail is being signed, stored in a temporary file, the file is deleted and after that the content of the file is send (so the message is empty).
[1] - http://kb.mozillazine.org/Message_security
[2] - http://kb.mozillazine.org/Getting_an_SMIME_certificate