Dima Mnushkin Bronze Collection
Assigning permissions to tables does nothing to protect your sensitive data from anyone with a word processor. If you find Access security too cumbersome to use, Dima Mnushkin provides you with a simple solution for encrypting sensitive data. Along the way, you’ll be introduced to the costs and benefits of using Access encryption.

I don’t know what most people think about Access security, but I find it a royal pain in the neck. If all I want to do is restrict access to a form or module, I have to implement Access security and start popping aspirins. Ideally, I’d be able to secure a completed database once and not worry about it ever again. That never happens for me. Somehow there’s always one more form to tweak, bug to fix, report column to widen. Once I make that change, I have to adjust my permissions once again.
Largely because of my bad experiences with applying Access security, I was absolutely thrilled to find out that Access 97 could create MDE databases. What a godsend! No more messing around with permissions and database ownership–just choose Tools | Database Utilities | Make MDE, and presto! My database would be secure from tampering by adventurous users who think your form name should be more descriptive. But after some additional investigation, I was disappointed to find that all of my tables’ data was still completely open to anyone with a copy of Access. It looked like assigning permissions was still the only way of ensuring that unauthorized personnel couldn’t get at sensitive data. My next step was to find out what the Encrypt/Decrypt Database would do for me.
Access encryption
Some developers believe that implementing Access security will keep people out of sensitive information like salaries or passwords. This is simply not true. As a simple experiment, create an Access database with a one-field table. Enter the value "this is a test" into that field, set a database password, and close the database. Open the database with your favorite word processor and do a search for "this is a test." You should find it easily. Now imagine that had been your boss’s name and the field right next to it contained his salary. Now imagine looking for a new job.
What this little experiment has demonstrated is that data within an Access database is easily accessible to anyone with a word processor, regardless of what permissions you’ve assigned to your objects or passwords you’ve set on the database. To make your database truly secure, it’s necessary to assign appropriate permissions and/or passwords and encrypt it. For this purpose, Access 97 provides the Encrypt/Decrypt Database function under the Tools | Security menu. I couldn’t find out precisely what type of encryption algorithm Access uses to protect each database, but it appears to be a form of symmetric encryption, likely DES.
The problem with database encryption is that it must be used in combination with regular Access security. An encrypted database without any database security can be decrypted by anyone with a copy of Access. Additionally, an encrypted database will run slower because of the extra overhead of encrypting and decrypting information all the time. Since Access isn’t the gazelle of development tools, slowing it down seems foolish.
Encrypting a whole database is often overkill. The average database doesn’t contain very much confidential information. Even a sensitive table like Employee has, at most, only one or two fields that must be protected. Thus the question becomes: Why encrypt (and therefore slow down) my entire database and enforce cumbersome Access security when all I want is to protect the values of two fields?
Encryption functions
A few months back, I was asked to protect employee passwords in an Access database. My client wanted the passwords hidden by more than just the "Password" input mask on the table field. The rest of the database hardly needed more protection than distributing it as an MDE. Under the circumstances, I was loath to implement both Access security and encryption (and I was all out of aspirin).
The only solution I could come up with was to implement my own encryption function that would reside in the MDE-protected module. I was faced with the difficult decision of selecting an encryption algorithm that was both easy to implement and tough to crack. Since I didn’t know much about encryption, I had to do some research on the Internet. I was overwhelmed by how much stuff was out there (see the sidebar "Encryption" for a brief synopsis of what I found).
Microsoft actually provides an API that performs encryption/decryption using the CryptEncrypt function. However, rather than re-using that perfectly good Microsoft code that had been well debugged and that included advanced encryption techniques, I decided to write my own. I was pretty sure I could write a reasonably secure function in less time than it would take to figure out how to use the API. As you can imagine, it took longer than I expected, but I learned a lot in the process.
The algorithm
One goal of my encryption function was to produce a different encrypted result for a given text value each time the function was run. In other words, when passed data, my function would produce an encrypted version of the data. If the process was repeated, a different version of the encrypted data would be produced. If you don’t do this, it’s possible for a hacker to take some known data, encrypt it, and compare the results to your encrypted data. Where your encrypted data and the hacker’s encrypted data match, the hacker knows what’s in your database. Another goal was to prevent users from getting a sense of the encrypted information by seeing clear word separation that could result from spaces being encoded to the same value each time.
I chose to implement a simple symmetric encryption algorithm that uses a single, developer-defined key to encrypt and decrypt information. In this algorithm, each byte in the data would have a byte added to it and so produce an encrypted byte. The byte to be added would be drawn from the developer-defined key. My function would draw the bytes one by one from the provided key. Once the last value in a key is used, the function will cycle back to the beginning of the key and continue encrypting until it runs out of data. This would ensure that even a common character, like a space, would rarely look like another.
A tougher challenge was to produce a different encrypted result each time the same value was passed through the function. To achieve this, it was necessary to begin at a randomly chosen position within the key each time. This would ensure that "baby" would encrypt to "_©"´È" the first time, "Ö™m£Ì" the second time, and so on. The more bytes in the key, the tougher it becomes to guess the key because there’s less repetition in the encrypted results.
While this isn’t an especially difficult algorithm to implement, it does pose a problem when decrypting the string. In order to decrypt the data, I have to know what starting position within the key was used to encrypt the data in the first place.
To solve this final problem, I had to store the starting position that was used to encrypt the data in the encrypted result (you might have noticed that the encrypted values for "baby" were one character longer than "baby"). Just to make things more interesting, I also encrypted the starting position (using the first key value) and inserted it into the middle of the result. With the function incorporated into my code and the appropriate data in the tables encrypted, my database was ready to be made into an MDE and distributed to my users.
The encryption function
You can find the encryption function in the sample database that I’ve included in the accompanying Download file. Here, I’ll walk you through some of the more interesting parts.
The first step in the encryption process is to establish the encryption key. It doesn’t matter what it is as long as it’s not longer than 255 letters and not empty. Here’s an example:
Const PASSWORD_KEY = "asd q98uiqw4t8ASEF9p513l"
When the function is encrypting data, the routine begins by generating a random number between 0 and the length of the encryption key. This number is the start position in the key:
Randomize
intPassKeyPos = _
Int(Len(PASSWORD_KEY) * Rnd + 1)
Once I’ve selected the start position, I encrypt it by adding the first byte in the encryption key to it:
intTemp = intPassKeyPos + _
Asc(left(PASSWORD_KEY, 1))
The core of the routine is a loop that finds the next position in both the encryption key and the data. If the next position for the encryption key is past its end, the routine cycles back to position 1. The routine uses the Mid function to extract the characters at the current positions in the data and the key, converts them to numbers with the Asc function, and then adds them together to get the encrypted result.
If the result is a number greater than 255 (the largest value that can be converted into a character), I subtract 255 from it to get a number that I can convert to a character. The Chr function converts the encrypted result into a character, which I add to the end of the encrypted result. During this process, I check to see whether I’m halfway through the string and add the key’s start position:
For intInputPos = 1 To Len(strFull)
intPassKeyPos = intPassKeyPos + 1
If intPassKeyPos > Len(PASSWORD_KEY) Then
intPassKeyPos = 1
End If
intTemp = Asc(Mid(strFull, intInputPos, 1) _
) + Asc(Mid(PASSWORD_KEY, _
intPassKeyPos, 1))
If intTemp > 255 Then
intTemp = intTemp — 255
End If
If CInt(Len(strFull) / 2) + 1 = intInputPos _
Then
strOutput =strOutput & strStartingPos
End If
strOutput = strOutput & Chr(intTemp)
Next intInputPos
Not surprisingly, the decryption process is similar to the encryption process. The first step is to extract the start position for the encryption key:
intPassKeyPos = Asc(Mid(strFull, _
CInt((Len(strFull) - 1) / 2) + 1, 1)) - _
Asc(left(PASSWORD_KEY, 1))
The next step is to subtract the value of the encryption key from the value of the encrypted data. If this gives a result less than zero, it indicates that I had subtracted 255 during the encryption process. I compensate by adding 255 before converting the result to a character:
intTemp = Asc(Mid(strFull, intInputPos,1))- _
Asc(Mid(PASSWORD_KEY, intPassKeyPos, 1))
If intTemp <= 0 Then
intTemp = intTemp + 255
End If
The only other trick is to jump over the middle character because it contains the start position for the encryption key.
The function accepts two values: the string to encrypt as its first parameter and a Boolean value as its second parameter. If the second parameter is True, the function encrypts the data; if the second parameter is False, the function decrypts the data passed to it. You can call this function from a variety of places. The first line in the following code accepts a text box on the form and stores the result in a field in a Recordset. The second line reverses the process:
rec("name") = strEncrypt(Me.txtName, True)
Me.txtName = strEncrypt(rec("name"), False)
Because of the integration of VBA and SQL, you can use the function in a SQL statement:
Select strEncrypt(rec("name"), False)
From SalaryTable
Known limitations
If you do use this function, you should be aware that it currently doesn’t handle numeric encryption. If numbers such as a salary must be encrypted, you must first convert them to strings and then store the encrypted result as a string. Also, it’s currently impossible to have a key longer than 255 bytes because the position is stored in a single byte whose largest possible value is 255.
Although Access encryption, in combination with Access security, allows for a truly secured database, it’s not always the best solution to protecting sensitive data. When deciding what method to use, consider how many fields need to be protected, the complexity introduced by adding Access security, and the performance hit taken by encrypting the database. Implementing a simple encryption function and distributing the database as an MDE will often be the most efficient way to ensure a reasonable level of security.
Your download file is called DECRYPT.ZIP in the file SA1999-10down.zip
This is found in the Bronze Collection at http://www.vb123.com/smart/
Sidebar: Encryption
Symmetric encryption
Asymmetric (public-key) encryption
Digital signatures
Certificates and certificate authorities