Reputation: 394
I am trying to send some data that the user will select to a mysql database on a LAMP server that is on a different network. Recently I downloaded a server on my own machine and everything was fine. The action in the app sent to the php file which was in the server and then of course the php handled sending this to the mysql database. Recently I was given access to a server which we will need to use. Now when I try to do the same thing I get an error saying the certificate for this server is invalid. I know that previously I was dealing with http and now it will need to be https, but I am not clear on how I should change this to make it work properly. I see a lot of different responses here on how to do this, but they are often countered with comments such as "This is a workaround" or "the app could be rejected" and so forth.
Here is my current function:
func sendToServer(firstEntry: String, secondEntry: String, serverAddr: String){
let uid = firstEntry
let gender = secondEntry
let request = NSMutableURLRequest(url: NSURL(string: serverAddr)! as URL)
request.httpMethod = "POST"
let postString = "UID=\(uid)&Gender=\(gender)"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("responseString = \(responseString)")
}
task.resume()
}
I read things such as not using shared here to get it to work, but I am still not quite clear on this. All I want to do is send this data to a php located on the server. The data itself is not sensitive, it is just simply gender and yes or no answers that get sent to a database. However, I would need this to be secure enough where there are no attacks that effect the user experience and where it won't be rejected for this reason. Any help here would be much appreciated. Thanks
Upvotes: 2
Views: 3249
Reputation: 757
self.isSimulatingCertificateCorruption implementaion is missing.
Upvotes: 0
Reputation: 2200
You need to do two things:
Add the domain exception to your plist file, I will refer you to this stackoverflow post How do I load an HTTP URL with App Transport Security enabled in iOS 9?
You need to implement your URLSessionDelegate. This one method is the bare minimum for your https request. You will find an almost exact method on URLSessionTaskDelegate, that method is for task specific authentication, this one is for session wide authentication. As you can see from the code, the crucial part is URLCredential(trust: challenge.protectionSpace.serverTrust!) where you specify that you trust that server.
class RequestDelegate: NSObject, URLSessionDelegate {
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {
completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
}
}
Then you just make the URLSession(configuration: URLSessionConfiguration.default, delegate: RequestDelegate(), delegateQueue: nil)
with your delegate as an argument
Tested and working on IOS 10.
Hope this helps, this is actually my first post on stackoverflow, so Hello World.
Upvotes: 7
Reputation: 2811
I have all this (called SSL Pinning)...
// MARK: URL session delegate
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
//Implementation 1: VERY WEAK METHOD
/*if challenge.previousFailureCount > 0{
completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
}else{
completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:challenge.protectionSpace.serverTrust!))
}*/
//Implementation 2:
var disposition: URLSession.AuthChallengeDisposition = URLSession.AuthChallengeDisposition.performDefaultHandling
var credential:URLCredential?
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
//certificate-based server credentials are used when verifying the server’s identity
credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
if (credential != nil) {
disposition = URLSession.AuthChallengeDisposition.useCredential
}
else{
disposition = URLSession.AuthChallengeDisposition.performDefaultHandling
}
}
else{
disposition = URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge
}
print("==============",#file.getClass()," ", #function," disposition: ", disposition)
print("==============",#file.getClass()," ", #function," disposition: ", credential!)
//completionHandler(disposition, credential);
//Implementation 3:
let serverTrust = challenge.protectionSpace.serverTrust
let certificate = SecTrustGetCertificateAtIndex(serverTrust!, 0)
// Set SSL policies for domain name check
let policies = NSMutableArray();
policies.add(SecPolicyCreateSSL(true, (challenge.protectionSpace.host as CFString)))
SecTrustSetPolicies(serverTrust!, policies);
// Evaluate server certificate
var result = SecTrustResultType(rawValue: 0)!
SecTrustEvaluate(serverTrust!, &result)
let isServerTrusted:Bool = (result == SecTrustResultType.unspecified || result == SecTrustResultType.unspecified || result == SecTrustResultType.proceed)
print("==============",#file.getClass()," ", #function," isServerTrusted: ", isServerTrusted)
print("==============",#file.getClass()," ", #function," result: ", result.hashValue," SecTrustResultType.unspecified: ", SecTrustResultType.unspecified.hashValue," SecTrustResultType.proceed: ", SecTrustResultType.proceed.hashValue)
var certName = ""
if self.isSimulatingCertificateCorruption {
certName = corruptedCert
} else {
certName = cert
}
// Get local and remote cert data
let remoteCertificateData = SecCertificateCopyData(certificate!) as Data
let pathToCert = Bundle.main.path(forResource: certName, ofType: "der")
let localCertificate = try! Data(contentsOf: URL(fileURLWithPath: pathToCert!))
print(" remoteCertificateData: ", remoteCertificateData," localCertificate: ", localCertificate, " serverTrust: ", serverTrust.debugDescription )
if ( remoteCertificateData == localCertificate) { //TODO:- this is strictly for tesing puposes, to allow untrusted severs. REMOVE IN PRODUCTION.
let credential:URLCredential = URLCredential(trust: serverTrust!)
completionHandler(.useCredential, credential)
}else if (isServerTrusted && (remoteCertificateData == localCertificate)) {
let credential:URLCredential = URLCredential(trust: serverTrust!)
completionHandler(.useCredential, credential)
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
will explain more if necessary. The function is supposed to be in the Class with the URLSession. The class should extend URLSessionDelegate.
Upvotes: 1