Using NSURLRequest, I am trying to access a web site that has an expired certificate. When I send the request, my connection:didFailWithError delegate method is invoked with the following info:
-1203, NSURLErrorDomain, bad server certificate
My searches have only turned up one solution: a hidden class method in NSURLRequest:
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:myHost];
However, I don't want to use private APIs in a production app for obvious reasons.
Any suggestions on what to do? Do I need to use CFNetwork APIs, and if so, two questions:
Any sample code I can use to get started? I haven't found any online.
If I use CFNetwork for this, do I have to ditch NSURL entirely?
EDIT:
iPhone OS 3.0 introduced a supported method for doing this. More details here: How to use NSURLConnection to connect with SSL for an untrusted cert?
The supported way of doing this requires using CFNetwork. You have to do is attach a kCFStreamPropertySSLSettings to the stream that specifies kCFStreamSSLValidatesCertificateChain == kCFBooleanFalse. Below is some quick code that does it, minus checking for valid results add cleaning up. Once you have done this You can use CFReadStreamRead() to get the data.
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://www.apple.com"), NULL);
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), myURL, kCFHTTPVersion1_1);
CFReadStreamRef myStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest);
CFMutableDictionaryRef myDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(myDict, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
CFReadStreamSetProperty(myStream, kCFStreamPropertySSLSettings, myDict);
CFReadStreamOpen(myStream);
If it's for an internal server for testing purposes, why not just import the test server's certificate into the KeyChain and set custom trust settings?
iPhone OS 3.0 introduced a supported way of doing this that doesn't require the lower-level CFNetwork APIs. More details here:
How to use NSURLConnection to connect with SSL for an untrusted cert?
I've hit the same issue - I was developing a SOAP client, and the dev server has a "homegrown" certificate. I wasn't able to solve the issue even using that method, since I wasn't using NSURL, but the (poorly documented and apparently abandoned) WS methods, and decided for the time being to (internally) just use a non-SSL connection.
Having said that, however, the question that springs to mind is, if you aren't willing to use a private API in a production app, should you be allowing access to a site with a dodgy certificate?
I'll quote Jens Alfke:
That's not just a theoretical security problem. Something
like 25% of public DNS servers have been compromised, according to
recent reports, and can direct users to phishing/malware/ad sites even
if they enter the domain name properly. The only thing protecting you
from that is SSL certificate checking.
Can you create a self signed certificate and add your custom certificate authority to the trusted CAs? I'm not quite sure how this would work on the iPhone, but I'd assume on Mac OS X you would add these to the Keychain.
You may also be interested in this post Re: How to handle bad certificate error in NSURLDownload
Another option would be to use an alternate connection library.
I am a huge fan of AsyncSocket and it has support for self signed certs
http://code.google.com/p/cocoaasyncsocket/
Take a look, I think it is way more robust then the standard NSURLRequests.
Related
I am using NSURLSession to implement an osx application that perform REST requests, and has the ability of manually or automatically configure proxy settings for app requests.
I was able to do this using session configuration, and is working for simple contexts.
However when it comes to proxies that require authentication I encounter some issues.
Due to some project requirements, when a request fails due to proxy authentication error(status code 407), I need to display my own custom dialog.
From what I've read so far, I did not find any way of achieving this using NSURLSession. There are some delegate methods related to authentication challenge, but are called after the challenge was already displayed to the user.
Is there a way to skip showing an authentication challenge for a NSURLSession request and fail with 407 status code instead?
As far as I know, neither NSURLConnection nor NSURLSession ask the app to handle 407 responses before displaying that dialog, and there's not a way to change that, short of filing a bug with Apple and convincing them to add that functionality in a future version of iOS or OS X.
For now, I think the best you can probably do is use libcurl to make a "junk" request first, see if it returns a 407, and if so, ask the user to provide new credentials. It isn't pretty, but it will technically meet your requirements. :-)
But honestly, the requirements are dubious. OS X and iOS users don't expect to control proxy settings within an app. They're normally systemwide behaviors. As such, trying to control them in the app is questionable. So if you have any say-so in the matter, you should try to get the requirements changed.
I was reading some topics about security and how hackers can look at the request you send to the backend to figure out how your system works but I did not find any good solution to avoid this.
So I was wondering what would you do in your app (here an iphone app) to make sure that hackers cannot see the content of the request your sending to the backend.
example http://myserver.com/api.php/login&pwd=test&username=pwd,
how to hide this so that no one can see the content.
I was thinking of different solutions:
1) encrypt the pwd and the username (not ideal as hacker can still see the post function of the server you are sending the request to)
2) use SSL request (I think this is only interesting to secure the connection to the server, meaning if the hacker is using his phone to connect to the server he should be able to see the request he is sending and so see the full URL)
3) change my backend so that all the request are sent to the same post function with an encrypted message. Finally the backend would decrypt and dispatch the message to the right function. This could work as the hacker would only see the url I am sending my request but would not have any information on what I am sending.
example: http://myserver.com/api.php/receiver&message=415gre6168sg4rg4e61g6r8g
"415gre6168sg4rg4e61g6r8g" could be decrypted as:
"login#pwd#username" and so I would be able to send this to the right function
But I am sure some of you have encounter similar issues and have better suggestions, would be interested to see what you would do,
Thanks
Any encryption you add is an extra effort for the hackers.
But in terms of what concerns to Apple use SSL connection should be enought based on iOS and the new IT - Security.
You can read more about the security polices used/recommended by Apple on iOS Security Guide
My problem:
I need, basic authentication over HTTP (client can't afford HTTPS). So I don't worry if communication is not encrypted. I just want to prevent some user from sniffing and using the password (site only used to upload photos and those photos are public.).
Toolbox of what I have at my disposal:
Javascript
PHP
Sha512.js
The SHA algorithm is the same in both PHP and JS:
The proof:(?)
<?php
$password= "password";
echo hash('sha512',$password);
//outputs: b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
?>
While in JS (all my files are encoded in utf8)
document.getElementById("hiddenField").value
= JS.sha512("password");
//outputs b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
However I cannot simply send the same hash on every connection, else anyone can sniff it and send it to connect.
So my idea was to use password_hash() function as salt generator.
The salt is public, the hash of (password+salt) is public, but password is private and never sent as clear text: the server will compute (hopefully) the same hash as the one in JS from the client and verify that both hashes match.
The problem is that regardless of what I do, I'm unable to get the same output when I hash the output of that function(password_hash). It seems to be something related to encoding.
I want to use password_hash() because it already keeps into account a lot of security stuff:
Javascript:
document.getElementById("hiddenField").value
= JS.sha512("password" + document.getElementById("publicToken").value);
I put the "password_hash" content into another hidden form field that I call "publicToken". Regardless of what I do I'm unable to get the hash match:
<?php
$salt = ut8_encode(password_hash("another_password")); //doesn't work either
In the end, what do I have to do to get a correctly encoded salt?
<?php
$salt = //... one time usage salt.. but what to put here?
I realize your client cannot afford a standard HTTPS certificate, but honestly, even a free SSL certificate is likely to be far better than what ever you can concoct here.
In this situation, all you are doing is making the browser-side hash the user's password, and all one has to do to get in is send a matching hash. If you decide to do this, you definitely need to hash the password again on the server side, but it is still no replacement for SSL.
More on it here: https://crackstation.net/hashing-security.htm
Without public key cryptography and a way to verify the identity of the server (in other words, HTTPS), the unfortunate truth is that there is simply no way to secure the communication to an acceptable level. I would not even advise trying, for fear of getting a false sense of security. No matter how much you hash and salt, it will only be minimally better than sending the plaintext password and trivial to break.
If your client cannot afford a certificate, I would recommend taking a look at StartSSL. Their basic level certificates are completely free; I believe they're valid for 1 year with unlimited renewal.
Another project worth looking at is Let's Encrypt. They've trying to make the process of getting a certificate much simpler and more accessible, so they've developed a way to completely automate the process of issuing (free) certificates. The service is not live yet, unfortunately; they plan to start issuing certificates this summer. Quoting their page:
Anyone who has gone through the trouble of setting up a secure website
knows what a hassle getting a certificate can be. Let’s Encrypt
automates away all this pain and lets site operators turn on HTTPS
with a single click or shell command.
When Let’s Encrypt launches in mid-2015, enabling HTTPS for your site
will be as easy as installing a small piece of certificate management
software on the server:
$ sudo apt-get install lets-encrypt
$ lets-encrypt example.com
That’s all there is to it! https://example.com is immediately live.
The Let’s Encrypt management software will:
Automatically prove to the Let’s Encrypt CA that you control the website
Obtain a browser-trusted certificate and set it up on your web server
Keep track of when your certificate is going to expire, and automatically renew it
Help you revoke the certificate if that ever becomes necessary.
No validation emails, no complicated configuration editing, no expired
certificates breaking your website. And of course, because Let’s
Encrypt provides certificates for free, no need to arrange payment.
I am writing a client app that need talk to Active Directory server and one of requirements is to support LDAPS/StartTLS.
I already figure out there is one option need to set:
if (ldap_set_option(pLdap, LDAP_OPT_SERVER_CERTIFICATE, &my_cert_check_func) != LDAP_SUCCESS) {
std::cerr << "ldap set cert check callback failed" <<std::endl;
return NULL;
}
and my_cert_check_func is over-naive and not safe at all:
static BOOLEAN my_cert_check_func(PLDAP connection, PCCERT_CONTEXT server_cert)
{
return TRUE;
}
And I also did a lot of googling and read quite a lot msdn, but still no clue. I have never handle such security-related coding before so any thing related to cert check are welcome.
And because I write this app using Winldap API, so the code should use Windows specific APIs.
And I am also thinking do such check using openssl api (this api is a dependency of my app, so it is fine to use that).
Could you show me some sample code for doing real checking of server certs against client security store or what ever client has?
Thank you very much!
You don't need to verify the entire certificate chain etc. for validity. LDAPS should already have done that. You only need to check the subjectDN of the certificate against what you think it should be when talking to that server.
For some legacy and internal reasons, I need to retrieve the certificate of the server (and ones from the chain as well), and read its fingerprint.
I understand this is easy to implement using AFNetworking or NSURLConnection since I only need to implement it in - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
But is it possible to do the same thing when using ASIHTTPRequest, which is just just basically a wrapper of CFNetworking?
Once again, I don't have a client certificate and my goal is to get the certificate from the server to read its fingerprint of issuer.
I will need to validate the fingerprint of server certificate in client and continue the request if it matches the one I expected, or cancel the request / throw error otherwise. Is this possible using ASIHTTPRequest?
I verified that there's no way to do this using CFNetworking (what ASIHTTPRequest is using), see https://devforums.apple.com/thread/87346. For those who don't have ios Developers account (I am not sure why you are reading this though), "No. After thinking about this in depth I don't think it's possible. NSURLConnection, which does this, use a private hook into CFHTTPStream, and you won't be able to use that."
Btw, we rewrote our network layer using NSURLConnection and solved this problem