How to make my CouchbaseMobile iOS app use SSL?

So rather than depend on AFNetworking looking up the credentials, I query by place and login name, then take the resulting credentials and pass it to the AFHTTPRequestOperationManager I am using. Now, ALL of my credential failures, but one, go away. I suppose that at some lower level the settings for credentials is being modified somehow. I also changed the type from Permanent to Session since I really didn’t want to have them hanging around in the keychain forever. Permanent means Permanent!

And the WebSocket calls stopped logging errors.

I now only have one credential issue, the initial replication is returning 401 errors on some documents with attachments. Not all, just a specific range of five of them. The logs show this sequence a five times for five different documents:

23:27:33.952‖ RemoteRequest: CBLMultipartDownloader[/ts1/map_icon_house.png_attachment]: Got response, status 401
23:27:33.952‖ CBLMultipartDownloader[/ts1/map_icon_house.png_attachment]: Got error Error Domain=CBLHTTP Code=401 "401 unauthorized" UserInfo=0x7fd9b300fb70 {NSURL=http://127.0.0.1:5984/ts1/map_icon_house.png_attachment?rev=2-0540b8b4ea438a1b1d98d756e01353f9&revs=true&attachments=true, NSLocalizedFailureReason=unauthorized, NSLocalizedDescription=401 unauthorized}
23:27:33.952‖ Sync: CBL_Puller[http://127.0.0.1:5984/ts1/] Progress: set error = 401 unauthorized
23:27:33.952‖ SyncVerbose: CBL_Puller[http://127.0.0.1:5984/ts1/]: postProgressChanged (201/275, active=1 (batch=0, net=25), online=1)
23:27:33.952‖ SyncVerbose: CBL_Puller[http://127.0.0.1:5984/ts1/]: postProgressChanged (202/275, active=1 (batch=0, net=25), online=1)
23:27:33.953| Sync: CBLReplication[from http://127.0.0.1:5984/ts1/]: active, progress = 202 / 275, err: 401 unauthorized

Each of these five have a single small png file attached to show as an icon. I can locate them using Futon with no problem after I login.

I’ve lost track of what you’re doing, to be honest — you started this thread about SSL, but now you say you’re not using it. And where does AFNetworking come in?

Is there a reason you need to use NSURLCredentialStorage directly instead of setting the authenticator property of the CBLReplication?

I finally got a chance to pull the framework out of my code and inset the project in place so that I can trace the code. In the CBL_Replicator class, where the cert is being evaluated, there is this:

    SecTrustResultType result;
    OSStatus err = SecTrustEvaluate(trust, &result);
    if (err) {
        Warn(@"%@: SecTrustEvaluate failed with err %d", self, (int)err);
        return NO;
    }
    if (result != kSecTrustResultProceed && result != kSecTrustResultUnspecified) {
        Warn(@"%@: SSL cert is not trustworthy (result=%d)", self, result);
        return NO;
    }

When I manually check the trust in the debugger the cert looks OK, well, the parts that are easy to compare. It looks like the correct cert is failing. The result variable is set to kSecTrustResultRecoverableTrustFailure, which thus returns NO for failure, even though AFNetworking succeeds using the same cert at the same host.

Digging into the docs a bit it looks like the problem may be that my self-signed cert is provisionally failing for lack of matching the correct host name, thus the kSecTrustResultRecoverableTrustFailure which indicates that narrowing the focus may succeed.

I can’t set a host name on the server as I may or may not ever have a host name. I need to tell the system to ignore the host name. Any ideas? And can I do this in the way I create the certificate? Or can I do in my app code?

I need to dig into this some more and see how AFNetworking handles this and specifically why it succeeds in the same circumstances.

Sorry, it is a muddle, but the goal is to get a secure connection. Should I abandon this tread?

What I said was that since SSL was not working during replication, that I fell back to working on the another part of a secure connection, authentication. I had hoped someone might have an idea, but none were forthcoming. I’ve now found time to start debugging the code myself and have found where the issue lies, seemingly, see below.

The only docs I found on authentication were the two words: @protocol CBLAuthenticatorProtocol and a reference to Facebook authentication. I’ll go scour around the docs later to see what I may have missed.

Sorry this is such a muddle, but the threading in this forum is confusing, messages jump around while scrolling and nothing is clearly related to something else unless you examine it in detail. Or maybe that is just in Safari. I’ve put weeks of time into tracking down these problems and thought to share what I’ve seen in the hopes that others could offer help, or at least that the next person who encounters this will have a few breadcrumbs to follow.

I appreciate you taking the time to thoroughly debug your issues, and to share your knowledge with everyone in the forum here. I see that you’re frustrated with the responses that you’ve received so far, but for those of us not familiar with your application’s codebase, it’s been a bit difficult to understand what your issue actually is. To a reader catching up on this thread, it appears that your topic changed over different parts of the thread (it’s good practice to keep each thread limited to a single topic and create new posts for new topics). Your last three posts seem to make your current issue clearer.

Just to make sure, can you confirm that the current issue in your app is the that certificate validation fails? If that’s the case, have you registered your self-signed certificate? You can see how we do this in our unit tests.

My frustration is limited to trying to work with this forum software. For some reason it does not display correctly with Safari for me. I just downloaded Chrome, and I can now actually see ALL of the messages! And they don’t jump around while viewing them! :slight_smile: So I will just switch over to Chrome when I am here. If I seem frustrated I’m sorry for giving that impression.

I should have titled the post “How do I now secure my working CBL app” instead of focusing on SSL since SSL without authentication is useless, at least in my case. The initial answers from paulharter and pasin cover those two areas. I seem to now have working authentication, but now that I’ve been told about the authenticator property on CBLReplication I will replace my code with an AuthenticatorFactory and see if I can determine how to make that work since it will probably be supported in future versions of CBL.

So, currently, in my app, the certificate is evaluated and the resulting kSecTrustResultRecoverableTrustFailure aborts the process, likely because the host does not match. As mentioned above, AFNetworking doesn’t abort on this state and I expected similar behavior. It would be nice if CBL would report on this result instead of silently failing. Perhaps a logger for these kinds of issues would be useful to add one day?

I am currently setting up my OpenSSL as a Certificate Authority in the hopes that I can construct a certificate with “X.509v3 extensions” that that doesn’t evaluate to kSecTrustResultRecoverableTrustFailure, but which passes the test. I hope that is my last hurdle.

And finally, to address your statement about “those of us not familiar with your application’s codebase”. I don’t see it that way, the issue is me understanding the CBL code base and how to use it. The ideal answer to my question would have been to direct me to a working sample that implements a secure environment. I can’t see any way around the need for good security solutions in the application space you folks seek to be targeting. Mobile devices live out in the wild and should not be sending data or credentials in the clear. Ever. At the very least you should have a whitepaper that covers this topic. If you don’t publish solutions for CBL, other people will, and who knows what they will suggest? If CBL didn’t claim to support some degree of security I would not have chosen to use it. Two weeks ago I knew nothing about SSL and authentication and since then I’ve read tons of incomplete and sometimes incoherent information on the web about how to secure mobile apps, some of it being outright wrong and potentially compromising security. If some frustration leaked out from my postings here, it comes from me expecting the people behind CBL to be very serious about security, but then, I’m a bit more paranoid about security that most devs, so it must be coming from that! :wink:

Cheers.

That’s odd; I haven’t had any trouble with Safari and Discourse on 10.9 or 10.10. Are you running an old OS? But glad you found a workaround.

If the hostname doesn’t match, the cert can’t be trusted, period. You’ll have encryption, but absolutely no authentication that you’re talking to the right server or that there isn’t a man-in-the-middle watching or modifying traffic.

I haven’t used AFNetworking so I can’t say why it wouldn’t raise an error for this. Maybe it has “opt-in” cert validation, which would be a very bad idea.

It would be nice if CBL would report on this result instead of silently failing

It should warn about that; we have unit tests for it. I just ran them and got this output when we purposely try to connect to a server with an untrusted cert:

WARNING: CBL_Puller[https://localhost:4994/public]: SSL cert is not trustworthy (result=5)
WARNING: CouchbaseLite: SSL server <localhost> not trusted; cert chain follows:
WARNING:     localhost: error = CSSMERR_TP_INVALID_ANCHOR_CERT

I am currently setting up my OpenSSL as a Certificate Authority in the hopes that I can construct a certificate

I wrote some directions a while ago on creating your own self-signed certs; maybe they’ll be useful.

The ideal answer to my question would have been to direct me to a working sample that implements a secure environment.

It really shouldn’t be as much trouble as you’re having; I’m not sure what’s getting in the way. A lot of it is probably that you’re using self-signed certs instead of getting a regular cert from a CA like Comodo. With a regular cert signed by a known CA, there’s no configuration needed on the client side.

There’s a whole bunch of stuff here, in the main article on the Replication class. I know our docs can be hard to navigate, but this particular page doesn’t seem difficult to find.

Right, if you know that authentication is part of the replication process. Since I didn’t know that it was, the location was a mystery. The entrance to the system seems to be Manager or Database, it only now makes sense that this is a good place to look.

My attempt to create a cert host that matches all host names with DNS:* doesn’t work, either and IP:* fails.

The essential problem is that my server or servers will pop up in the AWS (or equivalent) cloud and I don’t know what IP address any given instance might have or what DNS host name it might have, if any. Yes, I can determine the runtime IP address but that doesn’t help. The need to evaluate hosts may be desirable for many apps but works against my architecture. It isn’t even clear to me that SSL is the right solution, but I can’t hire an expert to write a custom solution unless/until I get the app funded, so I just want to get it bootstrapped at some acceptable level of risk for now.

I don’t feel a need to debate the pros and cons of this decision, my app is code signed and encrypted and the server is locked down. I will provide an external mechanism for pointing the app at the right IP address. I’m just not going to be able to provide it at complile time, and I don’t feel that generating certificates when an AWS instance starts up and moving keys around the internet is a viable option when this a really a security policy issue.

In essence, I need to provide a policy that ignores the host name. It is clear that kSecTrustResultRecoverableTrustFailure is not the same as an error or outright total failure. The notion of being recoverable is clear in the SecTrust.h file:

@constant kSecTrustResultRecoverableTrustFailure Indicates a trust policy
failure which can be overridden by the user.  This value may be returned
by the SecTrustEvaluate function but not stored as part of the user
trust settings.

Looking at the code, I don’t see a way to provide a policy or custom trust to the Replicator. Have I missed it? Is there another possibility I’m overlooking? I see, for example, that the pinned certificates take a different logic route that I have not investigated.

Hmm, I don’t think that is quite the whole story, but may be good enough to tell the kids to keep them out of trouble. I’m not sure if it is a good policy or not, it feels insufficient to me. The OpenSSL site lists SSL vulnerabilities and exploits and they already have about 20 this year, one of them marked critical, so I’d like the ability to customize and extend things.

My understanding is that an acceptable security pattern is to set up a certificate authority and create a root certificate and keep it super secure and safe. You then use that to sign an intermediate certificate. This “signing certificate” is used to sign server certificates. When an app sees the server certificate it can validate that the certificate was signed by the signing certificate. As far as I can tell, this is sufficient for the risk I’m willing to accept and is better than just matching host name strings. My goal is achieve this, but without host name matching and I’m not clear on what CBL or iOS is doing, so I need to delve into it a bit more.

You’re not the only person doing this — it’s got to be super common to set up SSL for servers hosted on AWS. I just don’t have any expertise with it, so I’m not the right person to help; but I would be very surprised if there weren’t a cut-and-dried solution.

You wouldn’t use your IP address. Instead you’d register a domain name for your server and then somehow get AWS to serve the DNS for it so it points to the current address of your server.

I don’t want to put you off, but this seems like something that you can find answers to in the AWS docs or in some other resource online, and it’s also unrelated to Couchbase Mobile. Once you get a domain name and an SSL cert for it, you just configure the server to use the cert. If it’s a traditional cert signed by a known CA, then you have zero work to do on the client besides changing “http:” to “https:” in the replication URL.

I don’t have any kind of formal certification, but I’ve been working with computer security and crypto for about ten years, so I’m pretty sure I know what I’m talking about here.

My understanding is that an acceptable security pattern is to set up a certificate authority and create a root certificate and keep it super secure and safe.

This is the sort of thing a large enterprise like Apple or Google would do. You yourself don’t want to do this; it’s too much work and a security risk. Besides, you won’t find any CA that will sell you a “fertile” root cert that you can use to derive other certs. That is exactly the sort of thing that got the Chinese cert authority in trouble last month, for instance — they sold one of those to an Egyptian(?) company that misused it and opened up a huge security hole.

Believe me, you should just be doing the regular thing: register a domain name, buy an SSL cert for it from a CA, and stick that cert in your server. Job done.

I have never setup SSL on AWS servers myself. I googled around and found this link might be helpful:

Thanks, pasin. But I have already built a number of systems on AWS and know about elastic ip addresses. There are a whole host of other issues in my architecture that I haven’t covered here concerning discovery and deployment that will still present me with the same issue concerning host names/ip addresses, so adapting the certificate to use an elastic ip addresses just avoids the issue that I have to solve in a short while anyway. But thanks, it is appreciated!

I should add that I’ve decided to fork the code and ad an X509 evaluation block that will allow me to deal with the kSecTrustResultRecoverableTrustFailure situation to see if that works the way I like.

Right, that’s the way I should do it, but I can’t for a whole host of reasons I won’t debate. Not the least of which is that I don’t have a team on my side to support all the work that needs to be done to do it correctly. I can’t do it now, so I need a reasonable solution with acceptable risk. I’m not worried about the host name matching issue at this point. If this project survives then someone with much more training will address it in depth. They may even decide that your approach is correct and rip out lots of code and redo my precious work!

I can live with that. :smile:

My training included basic security, and predated SSL, which is new to me, and worked from a focus on risk, and at this point in time the risk/benefit/cost situation doesn’t permit your solution because of what the app is doing. I see no way forward short of providing my own evaluation method as I will not know the host name or IP address during compile time.

Although I’m comfortable with my architecture and plans for security, it occurred to me that adding some extra security to my initial alpha test would not hurt, especially since registering a junk domain name and adding a few lines of code and creating another certificate would be of minimal cost. So I got a domain name and will set up my initial public facing server using the existing name matching for my app testers for any added security it might provide. The other non-public servers will use an additional evaluation block to verify the certificates are signed by my CA’s signing certificate. This gives me the best of both worlds since I can limit alpha users to the one domain and have the flexibility of spinning up multiple servers for other purposes with a sufficient security policy for my purposes that doesn’t need string matching nor lots of certificates.

If the app makes it to the funding stage we’ll get someone with deeper security skills than mine to do a security audit to tease out any problems and provide input for the inevitable re-architecting. So thanks for pushing the idea, it forced me to think through things to see if I could force name matching to occur without major effort and such that it will work for the initial phase. I’ve already tested an initial implementation and it works well. I need to do more testing, and no doubt clean up some loose ends and make sure I’m being efficient, but it all seems to be working now.

Hi, I have also the problem with SSL and self signed cert under iOS.
I did exactly as described here in topic, added cert for my site, self signed, because on our test server.

when I am executing replication it Couchbase Lite 1.1.0 says me
SSL cert is not trustworthy (result=5)
Result 5 is kSecTrustResultRecoverableTrustFailure which is actually ok for self signed certs.

I looked in your source code, and now this code accepted, not sure was that fix for self signed certs or not, but version 1.1.0 reverts this result code. (according to history on github)

Two questions:

  • would it be possible to have latest build and test
  • maybe it is possible to workaround this issue in current version 1.1.0