By adding to the kCFStreamPropertySSLSettings dictionary the usual kCFStreamSSLValidatesCertificateChain, kCFStreamSSLAllowsAnyRoot, etc - I can make server & client authentication work.
However I fail to see how I can get this more fine grained; i.e.
detect a cert of server/CA never seen - and escalate acceptance up to the user.
validate a cert of the server I am connected to against a narrow trust list (e.g. the cert learned about during a previous connect).
I.e. what I am looking for is 1) something like the SSLSetTrustedRoots() on MacOSX and 2) something like the error/domain callbacks in kCFStreamErrorDomainSSL (e.g. errSSLPeerCertUnknown) - none which seems defined/accesible on the ipad/iphone 4.2.1.
Or am I missing something ? Or do I need to do something explicit on the callback ? Examples appreciated (the AdvancedURLConnections example is not quite applicable - I have a raw (IRC) socket).
Thanks,
Dw.
Set kCFStreamSSLValidatesCertificateChain to kBooleanFalse and manually verify the cert with the APIs.
Specifically, use
SecTrustCreateWithCertificates
with the certificates you get from
CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLPeerCertificates);
Then, you can use
SecTrustSetAnchorCertificates
and finally call
SecTrustEvaluate
Related
I run an self-hosted instance of BigBlueButton and signed up for Xirsys TURN server services because we need to serve clients behind (pretty restrictive) firewalls. Before I had been running my own instance of coturn, but as this led to problems recently, I thought I will got someone who does this for a living a try.
Now the configuration in BBB is explained here:
https://docs.bigbluebutton.org/2.2/setup-turn-server.html
Yet so far I completely failed to match the parameters I receive from Xirsys with what I have to put into the /usr/share/bbb-web/WEB-INF/classes/spring/turn-stun-servers.xml file in the place of the <turn.example.com> and <secret_value>.
Did anyone ever make this work? I did try and find a tutorial but also failed.
bbb_web, is returning this the turn uris. passwords to the html5 client, that the client is using in sip.js
so you can either get bbb-web to send valid username/passwords is same method is used, or modify the html5 client to make a Xirsys api call, to get access to the turn candidates.
Would need to look at api docs. twilio has a similar service.
regards,
Stephen
not the most elegant solution but the easiest one for me:
modify the final bbb js bundle to load the stunturn info from a fixed url in
e.g.
/usr/share/meteor/bundle/programs/web.browser/f30716b2b57e2862c4db2325 b7aac63f4622842b.js
the minified part should then look somewhat like:
const r=Meteor.settings.public.media,i='https://<yourbbburl>/html5client/stunturn.json',a=r.cacheStunTurnServers,s=r.fallbackStunServer;
and put either the static credentials or generated ones in a file stunturn.json besides the js bundle.
I've been writing client certificate code for iOS using many of the resources here: iOS Client Certificates and Mobile Device Management and I've broken out the process to these steps:
Get the Cert via email or AppConfig
Store the Cert (securely)
Extract Identity and Trust from the Cert.
Intercept failed web requests, create NSURLConnection to manually handle auth responses as per Eskimo's advice.
Turn Identity and Trust into the auth response challenge.
My problem is step 2. SecPKCS12Import function appears to automatically add Identity to the keychain as well as return all Identities and Trusts from the certificates, thus eliminating the need for the convenience function often given ExtractIdentityAndTrust().
But on my 2nd run, I will need the Identity and Trust, not just Identity. My current plan is to store the entire cert raw using SecItemAdd, test for duplicates and use it, but I feel like I should be able to just use SecPKCS12Import then later grab it without also using SecItemAdd.
The documentation that is most confusing is SecPKCS12Import, and I would like a clearer understanding of what it does vs secItemAdd, and if secItemCopyMatching() is the same in the end just to grab the certificate. Is Trust not needed or am I just being literal and it's stored with the identity?
The general save, use, store, grab is working, but I'm using NSData and would prefer to store it correctly
I eventually became more familiar with the KeyChain and Identity vs Trust and learned this:
The Trust is a Cert stored in a place that determines who your custom Certificate Authorities are. It only needs to be tested once, which is why it isn't stored.
Storing the Identity is also a certificate, but needed for later. The keychain considers Certificates/Identities to be a Special/Unique thing so it is stored as its own thing, which is why all the keychain code looks different than just securing a password.
Basically, storing the Trust is unnecessary for future reference, but should be checked for good practice. I personally think an expiration might be handy
I need to open a CFStream socket connection to a server that has an untrusted CA root. I have the certificate of the server, and I can create a SecCertificateRef structure from it. The problem is how to set up the properties of the stream.
I think I should set the kCFStreamPropertySSLSettings property to a CFDictionary that in turn contains a kCFStreamSSLCertificates key. This key should hold a "a CFArray of SecCertificateRefs except for the first element in the array, which is a SecIdentityRef" according to the docs. Now I can create the SecCertificateRef from the server's certificate that I'll ship with the app, but how to get the SecIdentityRef? I guess it should be the client identity but I absolutely don't want client side authentication for now. And I can't find a way how to feed CFStream only with the server certificate.
Note, I don't want to add the untrusted certificate to the keychain, neither disable kCFStreamSSLValidatesCertificateChain in the settings. I need to accept the server authentication only if it is based on my own server certificate data loaded from the disk, and only on this CFStream.
I do not have the direct answer to your question, but perhaps few guidelines:
Why do you need to use the CFStream API and not the more intuitive NSURLConnection ?
From what I could find in the documentation, it seams like not everything that is available for Mac OS X, regarding CFStream API, is available for iOS. So think about it, and see if you can switch to NSURLConnection :-)
For NSURLConnection, you can use the NSURLConnectionDelegate methods to get the SSL challenge and validate the certificate on your own. You can check the wsdl2objc project, where I have implemented these features:
The project: http://code.google.com/p/wsdl2objc/
Some guidelines on how to get the certificate references: http://code.google.com/p/wsdl2objc/wiki/AdvancedOptions
Now about your questions :-)
I don't see how you can set a custom (untrusted) CA in kCFStreamPropertySSLSettings. I'm not sure if it can be done by using kCFStreamSSLCertificates since it is meant to be used for setting client-side certificates (thus the requirement of having the SecIdentityRef on index 0, which basically provides the private key).
When you say you don't want to add the certificate to the keychain, do you mean manually or programmatically ? I guess you don't like the users of your app to have to do it manually, but you can use the Security API to import the certificate programatically. In this case your certificate will be imported in a sandboxed keychain which is only available for your application. (again, not sure if this will work but worths the try)
In my applications I use NSURLConnectionDelegate to manually validate untrusted certificates.
Regards,
Pece
Basically you have to:
disable default trust evaluation using kCFStreamSSLValidatesCertificateChain
get the trust object (kCFStreamPropertySSLPeerTrust) once connected to the stream (but before sending any data, i.e, on kCFStreamEventCanAcceptBytes or kCFStreamEventHasBytesAvailable events)
set your self-signed root certicate as a trusted anchor for that trust object
optionally, you may add custom SSL policies to the trust object (e.g, hostname doesn't match certificate CN), but if you do that it's important that you do it before setting the trusted anchor or you may get kSecTrustResultRecoverableTrustFailure result
evaluate the trust object (SecTrustEvaluate) and check result is either kSecTrustResultProceed or kSecTrustResultUnspecified
If I have an app that connects to Amazon's S3 service, is it worth my time to hide/obfuscate the connection strings and API keys? I'm guessing that most hackers won't care all that much, but it would be financially painful if someone found this information and was able to upload data to my account!
For instance, if I store a username/password (or Twitter/Facebook API key and secret), these may be easily found using "strings". A hacker could see the functionality, grab the secrets and use them for nefarious purposes. I've seen people suggest using a simple Rot13, or storing the strings backwards or something like that in the app binary. Are these useful?
Has anyone done this or have any ideas/patterns/code to share?
-dan
You can hide your secrets in a webserver you have full control over, and then having this server relay the query to Amazon. You can then use whatever encryption/validation method you like, since you are not relying on what is supported by Amazon.
Once you have validated that the request is from your own application, you then rewrite the query including your secrets and then forward this to Amazon. The result from Amazon could then be relayed directly back to the application.
In php this could for instance be done using something similar to this snippet (not showing your url rewrite):
$fp = fopen($amazon_url,'r',false);
fpassthru($fp);
fclose($fp);
You dont really need to hide them...what you should do is have an extra key such as a secret, that one IS hidden and is only present in the signature of the call (which can be an MD5 hash or sha (or whatever)) without that secret key people wont be able to just make calls since the signatures created by the server and the offender wont match since they dont know the secret key used...
I'm guessing that most hackers won't
care all that much
It just takes one who's bored enough.
Has anyone done this or have any
ideas/patterns/code to share?
This is what SSL is for. You can encrypt all your transmissions or just the login process (which would return a session id that can be used for subsequent requests during the session).
I am writing a very simple web service for my iPhone app. Let's say this is a http page that returns a random number at http://mysite/getRand. How do I ensure that this page can only be accessed from my iPhone app and not from other clients? I've thought of doing some simple password mechanism but that can easily be sniffed by capturing what my app sends out.
The reason for this is to lower the load of my server by only allowing legitimate requests.
You can't really do this. Your application can be disassembled and whatever secret is in the binary can be replicated in a malicious application.
Another attack you should be aware of is people settings the hosts file to a location they control and then installing a root certificate that allows them to provide a signature for that domain. Your application would do the post with the secret, and they'd just be able to read out the secret. They could extract the password from any complicated encryption system within the binary in this way.
Most of the ideas in this thread are vulnerable to this attack.
That said, the likelihood of somebody caring enough to disassemble your application is probably fairly remote.
I'd just keep it simple. Have a password that's hardcoded in to your application. To prevent someone just looking at the resources and trying every string, make it the XOR of two strings or the result of an AES decrypt of a particular fixed string.
Obviously, you should do the request over SSL otherwise an attacker can just sniff the traffic.
Yes, a determined attacker will circumvent the scheme but like any DRM scheme, that's always been the case. The trick is to make it too much effort to be worth it.
To follow up on Simon's idea, you could very easily have a key string in your application, then send the device ID, and then the DeviceID XOR'ed (or some other simple algorithm for string encryption) with your key string.
Since you know the key value to use, it's trivial for you to "decrypt" this string on the sever side and verify that the values match.
This way, the password is different for each user's device, and the "key" string is never sent over the wires of the great unwashed internets. :-)
Yes, this would by no means be impossible to figure out, but like others have said, the idea is not to make it impossible. The idea is to make it more trouble than it is worth.
I would use the https protocol with client-side keys too. You can use one client key for everyone or you can even generate a different key for each client and "register" them at your server.
I suppose that it's a lot of work for small project, but it sounds like the appropriate thing to do if you need authentication.
You should check that keys aren't seen easily by mobile phone owner. And remember that somebody will be able to hack it in any case.
Here's one thought - send up the device ID along with requests from your app.
Monitor the device ID's used - if you see a ton of requests from different IP's near or at the same time, that device is probably being used as a fixed key in the requests sent to you - block it.
For those that actually send the real device ID from other apps (not yours), you can monitor usage trends to see if the calls match the pattern of how your app performs - like one call being used by a device before some initialization call you would normally expect, and so on - block those too.
Basically by being able to shift rules around patterns of use, you can better adjust to someone trying to use your service by making sure it's not a fixed target like some random use key would be.
You may also want to use a simple use key as well as a first line of defense, and then layer on the traffic analysis approach. Also custom http header values you look for are another simple way to trip up a naive attacker.
I am assuming you don't want to use SSL? If you do then you can open HTTPS session and then pass some secret key in the request.
If you don't want SSL your options are limited: to have pseudo security I suggest both authentication and authorization methods and a third to reduce overall traffic:
Authentication: Generator in client application that creates secret keys by combining with a key file. The keyfile can be updated every so often for greater security: lets say you update the key file once a week. To re-cap: Generator combines in app secret with out of app key file to generate a 3rd key for transmission used in authentication. The server would then be able to authenticate.
Authorization: Of course you also want to lock out rogue applications. Here it would be best to have authorization mechanism with the site. Don't replace keyfiles for unless the client logs in. Track key files to users. etc.
Traffic reduction:
If you are receiving obscene amount of traffic or if you suspect someone trying to DOS your server, you can also have both the server and clients sync to request/response on a procedurally generated URL that can change often. It is wasteful to open/close so many HTTPS sessions if someone is just flooding you with requests.
I'm not sure what web technology you are using, but if you are using Ruby on Rails, it uses a secret authentication token in all of its controllers to make sure malicious code isn't accessing destructive methods (via PUSH, POST, or DELETE). You would need to send that authentication token to the server in your request body to allow it to execute. That should achieve what I think you are looking for.
If you're not using Ruby on Rails, that method of code authentication might be a good one to research and implement yourself in whatever technology you are using.
Take a look at the Rails Security Guide, specifically section 3.1 (CSRF Countermeasures).
You could do something like encrypting the current time and IP address from the iPhone, and then decrypt it on the server. The downside is that you need the iPhone app to know the "secret" key so that only it can generate valid access tokens... and once the key is in the wild, it will only be a matter of time before it's hacked if your app is really worth the effort.
You could encrypt the response using some random portion of the application which is meant to be using it, specifying the location of the binary in an unencrypted bit of the response. Then at least only clients with access to your binary would be able to decrypt it... but again, that's hardly 100% secure.
Ultimately you need to ask yourself how much effort you want to put into securing the service vs how much effort you think hackers will put into abusing it.
I'm not an Cocoa Touch developer, but I think HTTP Authentication over SSL would be easy to implement and it's probably exactly what you're looking for.
All you need to do is setup HTTP Authentication on the server side (you haven't mentioned what you're using on the server side) and create a self-signed SSL cert on your webserver. Done. :)
Tell us more about your setup and we will be able to help you further.
As some of the answers have stated, closing your web service off to everyone else will be a major hassle. The best you can hope for is to make it easier for the hackers to use another web service, than to use yours...
Another suggestion to do this, is to generate random numbers from a random seed on both the server and the client. The server would need to track where in the sequence of random numbers all of the clients are, and match that number to the one sent by the client.
The client would also have to register to be given access to the server. This would also serve as a authentication mechanism.
So:
//Client code:
$sequence = file_get_contents('sequence.txt');
$seed = file_get_contents('seed.txt');
$sequence++;
//Generate the $sequence-th random number
srand($seed);
for ($i = 0; $i <= $sequence; $i++) {
$num = rand();
}
//custom fetch function
get_info($main_url . '?num=' . $num . '&id' = $my_id);
This will generate a request similiar to this:
http://webservice.com/get_info.php?num=3489347&id=3
//Server Code: (I'm used to PHP)
//Get the ID and the random number
$id = (int)$_REQUEST['id'];
$rand = (int)$_REQUEST['num'];
$stmt = $db->prepare('SELECT `sequence`, `seed` FROM `client_list` WHERE `id` = :id');
if ($stmt->execute(array(':id' => $id)) {
list($sequence, $seed) = $stmt->fetch(PDO::FETCH_ASSOC);
}
$sequence++;
//Generate the $sequence-th random number
srand($seed);
for ($i = 0; $i <= $sequence; $i++) {
$num = rand();
}
if ($num == $rand) {
//Allow Access
} else {
//Deny Access
}
By using a a different seed for each client, you ensure that hackers can't predict a random number by tracking previous numbers used.