RSon1234
RSon1234

Reputation: 394

Sending a post request with swift 3 (self-signed certificate issues)

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

Answers (3)

Vigneshwaran Murugesan
Vigneshwaran Murugesan

Reputation: 757

self.isSimulatingCertificateCorruption implementaion is missing.

Upvotes: 0

Sasa Blagojevic
Sasa Blagojevic

Reputation: 2200

You need to do two things:

  1. 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?

  2. 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

nyxee
nyxee

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

Related Questions