Securing Kitura

In this blog series, we explore how a developer can secure a Swift server application using Kitura web framework. Kitura-Credentials is the middleware which Kitura applications can leverage to integrate authentication with a protected resource or API. In this post, I will describe basic and digest authentication schemes, how they should be used and write an API which is protected via these schemes.

Modern web applications handle authentication in a variety of ways. Traditionally, users log in by providing a username and password. More recently and with the rise of social networking, OAuth and OpenID Connect framework have become popular authentication methods.

Kitura-Credentials follows the philosophy of Passport.js, where the framework serves a single purpose: to authenticate requests. It also recognizes that each application has unique authentication requirements. Authentication mechanisms can be packaged as individual plugins and the application can choose its plugin of choice. By integrating the plugin into Kitura-Credentials, support for the authentication mechanism can be easily and unobtrusively added to any application that supports Kitura.

If you are interested in how a Kitura application can be served on top of SSL/TLS and thus provide private and authenticated communication, check out Part one of the series. In Part three, we’ll be discussion how using OAuth-based authentication can overcome some of the shortcomings of basic and digest authentication.

Basic Authentication

HTTP basic authentication is a very old and simple protocol:

  1. Each user is given a username and password to access an API.
  2. For every API request the developer makes on behalf of the user, the developer will send along the username and the password to identify and authenticate user.
  3. Server validates the request by checking if the client username and password match the server’s version.

This is done by inserting ‘username:password’ in a standard field of the HTTP header. This type of access control to a resource is extremely simple and appealing because it does not require any handshakes, cookies or session identifiers.

Basic Authentication Protocol

HTTP basic authentication is implemented in Kitura-CredentialsHTTP plugin. The plugin requires a verifyPassword callback, which accepts the username (also called userId) and the credential, and returns in its callback, a user profile if the username and credential are a valid pair and nil otherwise.

public typealias VerifyPassword = (userId: String, password: String, 
                                    callback: (userProfile: UserProfile?) -> Void) -> Void

The basic authentication plugin also does not make any assumptions on how the application manages its user credential database or more specifically, how it stores the user passwords. It abstracts away this functionality in VerifyPassword. This is an important security feature because the framework does not dictate the security level of the stored credentials to the application; the application can implement security measures that match its own requirements.

As you will see below, these VerifyPassword calls end up being used within a credential plugin. These credential plugins, in turn, can be associated with endpoint routes. Once a route is associated with a credential plugin, the developer does not need to call any other credential methods. The middleware unobtrusively ensures that the route is subsequently protected with that authentication scheme.

Secure Storage of Credentials

Before we go on, we should note that proper protection of credentials is very important. A compromised credential or password not only allows an attacker to impersonate the user in the context of the compromised service, but potentially affects many other services because of user’s propensity for password re-use.

Protection of credentials should be implemented on multiple layers. The first, which is outside the scope of our blog series, protects the server that stores the credential information. This includes proper hardening of the server, isolation via firewalls, IPS/IDS and implementing access control. But guaranteeing the security of a server is a hard problem, and therefore it is important to assume that eventually theft of stored credentials will happen. This compromise can be through active attacks such as SQL Injection, or passive via combing through server logs, dumps or backups.

Another layer of defense is the protection of the stored credentials in the event that they have been compromised. It is this layer that can be affected by the authentication framework and the level of abstraction provided for credential verification. Two essential recommendations in storing credentials is use of (1) a salt, and (2) an infeasible protective/encoding function.

A salt is a fixed-length cryptographically-strong random value, which is unique for each credential and is used as input to the protective function. The salt serves two purposes. First it prevents the encoded form from revealing two identical credentials. Second, it increases the randomness of the credential without relying on credential complexity. This makes pre-computed lookup attacks (using rainbow tables) hard.

The function used to protect the stored credentials needs to balance the needs of the attacker and the application verification. The function needs to be fast and easy to compute (for the application) but hard (time and CPU intensive) to inverse (for the attacker). Example functions include one-way hash functions such as scrypt, bcrypt, PBKDF2 and Argon2 which recently won the password hashing competition and should be considered as the first choice when solid implementations are available.

In our example below, we use PBKDF2 to securely store the user credentials. This uses the crypto framework BlueCryptor, which is a swift cross-platform crypto library using CommonCrypto on macOS and libcrypto from OpenSSL on Linux.

Sample Application

We assume you have at least Swift 3.0 toolchain installed in your environment. You can follow Kitura wiki to create a sample app called CredExample. We want to set up a Kitura private API called /private/basic/hello which is access controlled with basic authentication. A successful authentication will result in a page displaying a personalized message such as Greetings Ada Lovelace! You are logged in with Basic Authentication. A failed authentication will result in display of the message You are not authorized to view this page. You can also download the sample app from here.

Let’s get started with setting up our Kitura application.

First we modify Package.swift file to include Kitura-Credentials framework. For debugging purposes, we also include HeliumLogger, which is a lightweight Swift logging framework.

import PackageDescription

let package = Package(
    name: "CredExample",
    dependencies: [
      .Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 1, minor: 0),
      .Package(url: "https://github.com/IBM-Swift/Kitura-CredentialsHTTP.git", majorVersion: 1, minor: 1),
      .Package(url: "https://github.com/IBM-Swift/HeliumLogger.git", majorVersion: 1, minor: 0),
    ]
)

In main.swift, we set up the route and the logging. We create a function called setupBasicAuth to abstract away the setup of the route /private/basic/hello and associating the route with basic authentication.

import Kitura

let router = Router()

// sets up /private/basic/hello  and associates it with basic authentication
setupBasicAuth()

Kitura.addHTTPServer(onPort: 8090, with: router)
Kitura.run()

Next we create the function setupBasicAuth. For simplicity, we hardcode our dictionary of usernames and passwords, however this is not good practice and the user credential information should be stored in a database. We do encode our passwords using PBKDF2 in the EncodedPassword object, which we will define subsequently.

import Credentials
import CredentialsHTTP

import LoggerAPI
import HeliumLogger

func setupBasicAuth() {
    // Setup a dictionary of users. In general, these would be read in from a database
    // Encode salted passwords using a one way encoding such as PBKDF2 or bcrypt or etc.
    // For more information, see https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
    var userDB: [String: EncodedPassword] = try [
        "Ada" : EncodedPassword(withName: "Ada Lovelace", password: "kitura", encoding: .PBKDF2),
        "Grace" : EncodedPassword(withName: "Grace Hopper", password: "swift", encoding: .PBKDF2),
        "Adele" : EncodedPassword(withName: "Adele Goldberg", password: "server", encoding: .PBKDF2),
    ]

    // setup basic credentials
    // include a verifyPassword in the constructor that is the callback to be used when
    // checking the username, password combination. 
    // Callback returns a user profile if (username,password) is valid. Else, nil.
    let basicCredentials = CredentialsHTTPBasic( verifyPassword: { userId, password, callback in
        
        if let user = userDB[userId] {
            do {
                let result: Bool = try user.verifyPassword(withPassword: password)
                if ( result ) {
                    callback(UserProfile(id: userId, displayName: userId, provider: "HTTPBasic-Kitura"))
                }
            }
            catch {
                Log.error("VerifyPassword internal error")
            }
        }
        
        // if userID or password do not match or internal error
        callback(nil)
    }, realm: "Kitura-Realm")    

    // create credential object and register basic credential plugin
    let credentials = Credentials()
    credentials.register(plugin: basicCredentials)
    
    // register this middleware for all routes /private/basic
    router.all("/private/basic", middleware: credentials)
    
    // construct the page on the following GET path
    router.get("/private/basic/hello", handler:
        { request, response, next in
            response.headers["Content-Type"] = "text/html; charset=utf-8"
            
            do {
                // if userProfile exists, it means user logged in
                if let userProfile = request.userProfile  {
                    
                    // display customized page
                    try response.status(.OK).send(
                        "<!DOCTYPE html><html><body>" +
                            "Greetings " +  userProfile.displayName + "! You are logged in with " + userProfile.provider + ". This is private!<br>" +
                        "</body></html>\n\n").end()
                    next()
                    return
                }
                
                // if 401 returned
                try response.status(.unauthorized).send(
                    "<!DOCTYPE html><html><body>" +
                        "You are not authorized to view this page" +
                    "</body></html>\n\n").end()
            }
            catch {}
            next()
    })
}

We create our secure encoding of our passwords using the following simple class, using BlueCryptor.

import LoggerAPI
import Cryptor
import Foundation

public class EncodedPassword {
    
    public enum SecureEncoding {
        case PBKDF2
        case scrypt
        case bcrypt
    }
    
    public enum EncodedPasswordError: Error {
        case invalidRecord
        case invalidEncoding
    }
    
    /// username
    private var userName: String
    /// salt used in the hashing
    private let salt: [UInt8]
    // length of salt and also encoded password
    private let saltLength: UInt
    // rounds of PBKDF2
    private let roundsOfPBKDF: UInt32
    // type of encoding
    private let encodingType: SecureEncoding
    /// hashed password
    private let encodedPassword: [UInt8]
 

    ///
    /// Initialize an EncodedPassword object
    ///
    /// Parameters:
    ///     userName:       username 
    ///     password:       password
    ///     saltFromUser:   salt (optional). If not included, function will generate a random salt. Recommended to not be passed
    ///     encoding:       encoding type used to encode the salted password
    ///
    public init(withName name: String, password: String, 
                usingSalt saltFromUser: [UInt8]? = nil, encoding: SecureEncoding) throws {
        
        // Define our constants
        let mySaltLength:UInt = 32
        let myRoundsOfPBKDF: UInt32 = 2
        
        let mySalt: [UInt8] = try saltFromUser ?? Random.generate(byteCount: Int(mySaltLength))
        
        salt = mySalt
        saltLength = mySaltLength
        roundsOfPBKDF = myRoundsOfPBKDF
        userName = name
        encodingType = encoding
        
        switch (encoding) {
            case .PBKDF2:
                encodedPassword = PBKDF.deriveKey(fromPassword: password, salt: mySalt, 
                                                  prf: .sha512, rounds: roundsOfPBKDF, 
                                                  derivedKeyLength: saltLength)
            default:
                throw EncodedPasswordError.invalidEncoding
        }
        
        Log.verbose("init: userId = \(userId), password = \(password), salt = \(mySalt), hash = \(encodedPassword)")
    }

    ///
    /// Verify the password passed with the input password
    /// this is done by encoding the input password and comparing the two encoded passwords.
    ///
    public func verifyPassword(withPassword testPassword: String) throws -> Bool{
        
        let testPassword_encoded: [UInt8]
        
        switch (encodingType) {
            case .PBKDF2:
                testPassword_encoded = PBKDF.deriveKey(fromPassword: testPassword, salt: salt, 
                                                       prf: .sha512, rounds: roundsOfPBKDF, 
                                                       derivedKeyLength: saltLength)
            default:
                throw EncodedPasswordError.invalidEncoding
        }

        return ( encodedPassword == testPassword_encoded)
    }
    
}

We can now compile our application using the Swift Package Manager (SwiftPM), which first fetches the dependencies and then builds our project. We do this by running swift build in our project directory. Once the project is built, we run the executable which was placed in the .build/debug directory. After the executable is running and listening for connections on localhost:8090, you can test out the application by opening a browser on:

http://localhost:8090/private/basic/hello

A login form should pop open with username and password input space. Try one of the hardcoded usernames to authenticate yourself.

Security of Basic Authentication

Basic authentication is very appealing because it is very simple, intuitive and does not require any session assumptions. However the scheme has weak security because of several important reasons.

First, the protocol does not provide any privacy or authentication for the transmitted credentials. The ‘username:password’ field in the HTTP header is neither encrypted or hashed, merely encoded with Base64. The username and password are readily available to anyone who intercept the HTTP requests, so this authentication scheme should only be used over an HTTPS connection. Using SSL/TLS would also protect the user and server against man-in-the-middle attacks.

Second, since the ‘username:password’ field needs to be sent over every request, the application needs to cache the credentials for a reasonable period of time to avoid constantly prompting the user for their credentials. Caching the credentials can also lead to attacks such as cross-site resource forgery (CSRF) attack where the credentials are silently used by other requests to the server.
Therefore, implementing or integrating a secure but usable caching policy is very important.

Third, by sending the password repeatedly for each request, we increase the size of the attack window. In addition, the server should implement a built-in account throttling/locking mechanism to protect against brute forcing the user password.

Finally, if a user credential is ever compromised, then the attacker can use that username and password pair to access that protected resource at any time (until the credential is revoked). This means that the application server needs to implement a robust and reliable credential-compromise detection and revocation mechanism. A better way to deal with this scenario is to use temporary tokens (such as OAuth access tokens) so that in the event that the temporary token is compromised and the compromise is not detected, the protected resource can only be accessed for a limited period of time.

Digest Authentication

Digest authentication is an extension of basic authentication that replaces the password in transit with a salted hash (MD5) of the password. A hash is one-way, meaning that although it is easy to compute, it is (very) difficult to inverse. It follows that hashing the password protects its confidentiality and so SSL is not required because of password leakage (although it is still required for other attacks such as man-in-the-middle attack and we strongly recommend that your application serves data on a secure communication channel).

Digest Authentication works as follows:

  1. Each user is given a username and password to access an API.
  2. A user sends a request to a server.
  3. Server responds with a randomly chosen value (called a nonce), another string representing the ‘realm’ and asks the user to authenticate.
  4. User responds with this nonce and a hash of the username, password and realm.
  5. Server validates the request by checking if the client hash matches their own hash of the username, password and realm.

Digest Authentication Protocol

Integrating the digest authentication plugin is very similar to that of basic authentication, except for the callback function. In digest authentication, since the password is hashed with a random nonce chosen by the server and then verified, the server needs access to the plaintext password. This is in contrast to basic authentication which could abstract away the password verification functionality in verifyPassword.

Digest authentication uses a callback function called userProfileLoader which accepts the username (also called userId), and returns in its callback, a user profile and the stored password.

public typealias UserProfileLoader = (userId: String, 
                                      callback: (userProfile: UserProfile?, password: String?) 
                                               -> Void) -> Void

We initialize the digest authentication constructor with three fields: a userProfileLoader, an opaque string and a realm.
The opaque field is a random string which is specified by the server and returned by the client. The field is used for the transportation of state information in a multi-server setting. The realm parameter defines a protection space which is a set of resources which share an authentication scheme and/or authorization database.

    let digestCredentials = CredentialsHTTPDigest( userProfileLoader: { userId, callback in
        if let storedPassword = users[userId] {
            callback( UserProfile(id: userId, displayName: userId, provider: "HTTPDigest"), storedPassword)
        }
        else {
            callback(nil, nil)
        }
        }, opaque: "J1lzGfs5Tihjs9", realm: "Kitura-users")
    
    apiCredentials.register(plugin: digestCredentials)

Concluding Remarks

Although digest authentication is designed to protect the confidentiality of the user credentials over an insecure channel, it still inherits the rest of the shortcomings of basic authentication, such as the credential caching insecurities and inability to limit the effects of compromised in-transit credentials.

In addition, digest authentication requires that userProfileLoader returns the plaintext of stored passwords. This very much limits how the passwords are stored on the server. As mentioned above, it is best practice to store user credentials as a salted hash, using a secure and slow hash function such as bcrypt that implements key stretching.

Finally, a fundamental challenge with both basic and digest authentication is that the framework assumes that the application developer is responsible for user and identity management. There are many complex challenges when dealing with identity management including credential management such as sign up and password reset, as well as security and compliance issues with storing user credentials. This is why many applications integrate Identity-as-a-Service (IdaaS) solutions such as the IBM Cloud Single Sign On (SSO) service. Alternatively, applications can leverage OAuth2/OIDC framework and existing OAuth2 providers such as Google, GitHub or Facebook to outsource some of the difficulties of identity management. In the next post in our security series Securing Kitura, we will cover the OAuth2 plugins of Kitura-Credentials. Note that additional plugins can be easily developed by the Swift community for custom authentication mechanism and developers can integrate these plugins with Kitura-Credentials.

In the meantime, learn more about Swift@IBM by visiting our DevCenter.

Swift On!

2 comments on"Securing Kitura Part 2: Basic Authentication"

  1. Why should we go for Kitura for Server Side Swift? As there are other frameworks like Vapor, perfect in the market. What makes Kitura different from others?

    • Hi Neeru,

      We actually think Vapor and Perfect are great projects that are important parts of a fast maturing Server Side Swift ecosystem. In fact, we’ve been working together on the Swift.org Server API workgroup. You can find out more about that here: https://swift.org/blog/server-api-workgroup/.

      For our part, Kitura is inspired by Express.js, the popular Node.js web framework – we can’t help but love the work that our colleagues at StrongLoop are doing. Regardless, we encourage you to give all the frameworks a try, and let us know if there’s anything we can do to improve.

Join The Discussion

Your email address will not be published. Required fields are marked *