Welcome!

PowerBuilder Authors: Chris Pollach, Yeshim Deniz, Jayaram Krishnaswamy, Kevin Benedict, Avi Rosenthal

Related Topics: PowerBuilder

PowerBuilder: Article

Secure Your Passwords

Secure Your Passwords

As architects of enterprise systems, we know that security should always be a concern when we transfer or store sensitive data. In the past, you might have cut corners because you assumed the corporate LAN provided some implicit level of protection. You might have rationalized that because only people with access to the LAN could see data being transferred across the wire - and surely no one at your company would try to steal sensitive data - your data was secure. Likewise, your internal applications might not have been passing sensitive information and you leveraged password protection on the operating system.

Although corporate LANs, corporate WANs, and intranets might provide adequate security for some applications, many applications are now deployed across wide-area public networks such as the Internet. The danger is that along with the ease of accessibility to these public networks comes, potentially, lots of prying eyes. Protecting sensitive data in this environment isn't an option - it's a requirement. If you expose someone's password or credit card information to an unauthorized source, you could incur legal action and lose customers and business.

The solution is for us to use cryptography. Cryptography, the science of information security, can address many of the issues associated with applications that handle sensitive data. Namely, it can prevent unauthorized persons from reading the data. There are many topics related to cryptography - enough to fill several books.

In this article, I'll examine three useful cryptographic techniques: hashing, encryption, and decryption. I'll discuss how each technique can improve application security and how to implement each technique in PowerBuilder.

The Simple Password Model
The simple password model illustrates the fundamental concepts behind password-based security. A password entered by a user is compared with a password stored on a system or in an executable file. If the passwords match, a secured operation is permitted.

Implementing the most popular approach to secured functionality - requiring the user to enter a password and enabling functionality only if the password is valid - is straightforward in PowerBuilder. I called this function of_comparepwd, and it takes an argument that's the password entered by the user. Depending on the value of the parameter, we could enable different functionality in our application (see Listing 1).

Although it's obviously a poor approach from a security perspective and very easy to break (you might have used the IDA Pro disassembler already to look at string references from dlls or executables, but here even Notepad [see Figure 1] would be enough to break the "security"), you might have seen this approach in older applications like shareware or demo software; you also might have seen that passwords have been read from a file or database (a scenario common in applications that support multiple users, such as databases or e-mail clients) and have been sent back to the client.

This brings us to the next problem: transmitting a password in clear text. Eavesdropping on a TCP/IP network can be carried out very easily and effectively against protocols that transmit passwords in the clear. To defeat this simple sniffing attack (sometimes I use my iPaq with CENiffer running and check around our network to see if there's anything wrong with our security in-house), we have to look at something different.

The Crypto API
The Microsoft Crypto API (CAPI) is a core OS service that's been shipping with Windows since Windows 95 OSR2. It's the API used by Microsoft to develop applications using cryptography, and any third party can use it on Windows for free. The documentation and libraries for CAPI are available as part of the Microsoft Developer Network (MSDN) as well as on their Web site: http://msdn.microsoft.com/library/ default.asp?url=/library/en-us/security/security/ cryptography_functions.asp.

Among the benefits to CAPI are:

  • Algorithm independence: An application can change cryptographic algorithms without having to rewrite the code.
  • Device independence: Applications written to CAPI can use software- or hardware-based crypto tokens without having to rewrite code.
  • Single store for certificates and software keys: This allows a user to use the same certificate and keys in a variety of applications without forcing the user to export and import them between applications or toolkits.
  • Built-in support for revocation checking and complex certificate chain building: All the logic is built into the API; applications need only request revocation services or chain building and the OS does the work.

    The Microsoft Crypto API can be accessed as a C language API, which makes it easy for us to access it. We simply have to declare the constants and external functions we'd like to use (see Listing 2).

    Start Hashing Your Data
    One of the first things we have to understand if we want to use the crypto API is hashing. A hash is simply a "summary," or "tag," that's generated from a digital document (a binary string) using a mathematical rule or algorithm. It's designed so that a small change in the document would produce a big change in the hash. As an example I created a hash for two different strings:

    Hash for the String ? testhash1':
    5616C9510E126363E8859F6B1D8E1334
    Hash for the String ? testhash2':
    F20C24485F0D57FAE87253C45E34846C

    Keep in mind that you could create a hash value from a string or document, but that you cannot get back (in a reasonable amount of time) the string or document from a hash value. A hash is often smaller than the original string (an exception is a small word like the one we used in our example) and, as we can see, it's generally unreadable by humans. Hashes are also used to check the integrity of files and documents, and are also often used in digital signature algorithms. We could use the hashed value to compare it against the value the user entered (which is hashed through the same algorithm). You might think of writing your own hashing algorithm, but before you start consider what a good hashing algorithm should provide:

  • A small change in the document should produce a large change in the hash; notice in our previous example where a tiny change in the string produced two totally different hashes.
  • Hashes should not be predictable. In other words, I should not be able to guess that changing the binary string will have a specific effect on the hash.
  • Hashes should not collide, and it should be computationally difficult to find collisions. In other words, two binary strings should have an infinitesimal chance of having the same hash, and it should be virtually impossible to find a document that has the same hash as a known document.
  • Hashing should be fast. Even if we enhance our application to use hashing, there shouldn't be a negative impact on our application.

    Why not use something that already exists? There are already some good hashing algorithms out there: MD2, SHA, SHA-1, and MD5 are well known (just to name a few). We'll probably move to SHA-1, which is the US. government-approved hashing algorithm, over the next year or two.

    At first we call the CryptAcquireContext function, which is used to acquire a handle to a particular key container within a particular cryptographic service provider (CSP) (see Listing 3). This returned handle is used in calls to CryptoAPI functions that use the selected CSP.

    The CryptCreateHash function initiates the hashing of a stream of data. We pass as a parameter the hashing algorithm we'd like to use. In Listing 3 we use MD5, but feel free to give another algorithm a try. For example, you might use SHA-1. The function creates a handle to a CSP hash object and returns it to the calling application. This handle is used in calls to CryptHashData to hash session keys and other streams of data.

    The CryptGetHashParam function retrieves data that governs the operations of a hash object. We use it to read back the hash value. At the end we convert the hashed password to a hex string, which we display in the MessageBox. We could use this string to verify an entered password.

    Remote Authentication
    The two examples you've seen so far involve local management and verification of passwords. This is fine, if only your application is involved in checking the password, but this is not often the case in the enterprise. Think of a bigger application where a lot of different back-end systems are involved, every one having its own authentication. Even if we move to more EAServer development in our enterprise where we integrate a lot of different back-end systems, we have to authenticate ourselves against the EAServer components. Even if we use the hashed password approach I described earlier, the remote password model suffers from several new security flaws, unless you can somehow guarantee the security of the connection between the local and remote systems. These flaws are that it transmits the password and the username to the remote object in plain text, and/or it returns the authentication result in plain text.

    Let's think a bit differently. How would it be if clients could authenticate without sending a password? I think it's certainly hard to steal a password that's never transmitted, isn't it?

    How do you go about verifying a password without seeing it?

    Encrypt/Decrypt Your Data
    To tell you the truth, this is nothing new. The approach we'll be looking at is the same one Windows uses to authenticate users on domains, or Sybase Enterprise Portal uses to authenticate its users. The CryptDeriveKey function from the cryptographic API generates cryptographic session keys derived from a base data. This function guarantees that when the same cryptographic service provider and algorithms are used, the keys generated from the same base data are identical. The base data can be a password or any other (random) user data. This means that if we use this function with exactly the same base data, we would get exactly the same hashed value on the server side and the client side. On the server side this hashed value could be stored in a database, a password file, or any other system (LDAP).

    If a user enters a password, we generate the hashed version and then both the local and remote systems would have exactly the same hashed version of the password.

    Now we need a way to compare the two passwords without actually sending the password between the two systems: the client generates a random block of data that it encrypts using a key derived from the hashed password (see Figure 2). The client sends the random block, along with user identification, to the remote verification computer. That machine retrieves the known hashed password for the specified user and encrypts the random block of data (the same random data that the client used) using a key derived from that hashed password. It then sends the encrypted data back to the client application.

    The client application now has two encrypted blocks of data. If the two passwords (the one the client entered and the one that was stored on the server) match, the two encrypted data blocks will match as well, meaning the password is correct and the operation can proceed. Keep in mind that the used random data has to be the same on both the client and the server side. You could use EAServer to generate this random set of data and, just before you start hashing, get this value from a component.

    The source code to encrypt a string would look like:

    ls_encrypted = lnv_crypt.EncryptData
    ("my sensitive data" , "SecretKey")

    where ls_encrypted is the encrypted data we would pass to the server. To decrypt a string on the remote computer we would use:

    ls_decrypted = lnv_crypt.DecryptData
    (is_crypted , "SecretKey")

    where ls_decrypted holds the original date.

    The functions could be implemented in one function (see Listing 4). The only difference is, if we want to encrypt or decrypt:

    of_encryptdecrypt(String data, String password, boolean encrypt)

    Unless you're using an application that requires a lot of bandwidth, most computers can easily handle encrypting all data transferred over the network without causing major performance problems. If you're using a high-bandwidth application, you might want to consider purchasing a crypto accelerator card for your machine to allow it to encrypt all of its data, for example.

    You can download a complete user object for PowerBuilder where all the encrypt and decrypt functions are implemented from www.sys-con.com/pbdj/sourcec.cfm, www.rgagnon.com/pbdetails/pb-0170.html, or from www.pocketpb.com.

    Conclusion
    In its simplest form, password protection is highly insecure because it introduces fairly easy ways for unauthorized users to obtain valid passwords. Cryptography is the extra ingredient you need to secure functionality in your applications. We've seen how to add basic password protection to PowerBuilder applications and how to use Microsoft's Crypto API to make that protection secure, whether you implement local or remote password authentication.

    The challenge/response password model improves security dramatically by eliminating the need to transmit the password in any form between the local and remote systems. Instead, your program uses the password to derive an encryption key that's used to encrypt a random block of data. If both the client and server obtain the same result when encrypting the random data with the password they have, the passwords must match.

  • More Stories By Berndt Hamboeck

    Berndt Hamboeck is a senior consultant for BHITCON (www.bhitcon.net). He's a CSI, SCAPC8, EASAC, SCJP2, and started his Sybase development using PB5. You can reach him under [email protected]

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.