What is the most secure method of distributing databases? (1 Viewer)

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
This is a follow on from a discussion in this thread:
https://www.access-programmers.co.uk...d.php?t=297683

The OP (bruno 1970) wanted to secure his database applications against piracy by linking the app to the computer hard disk number.

I realise this has come up before & I apologise if a definitive answer has already been done elsewhere

Anyway, I'm having a similar issue and rather than hijack that thread further, I hope this new post will trigger some new ideas

If anyone knows a better way of doing what I'm about to describe, please let me know

My current approach to my apps is as follows:
I distribute my databases via my website as EXE files created by a professional installer program (SamLogic Visual Installer).

All current apps are sold as 32-bit/64-bit ACCDE files (except where code is purchased as an extra). They are fully locked down with no ribbon or nav pane & the shift key bypass disabled.

Until now, most of my databases were sold with site licenses which means I hadn't needed to worry about the number of times it is installed.

I can easily prevent the installed database being COPIED to another PC by checking against registry data created during a proper installation.
If that's missing or there's a mismatch, the app won't run.
So far so good.

The problem for me is that I now want to distribute apps where the licence is limited to a specified number of PCs e.g. 5.

One recommended solution is indeed to 'tie' the app to the hard drive it is installed on.
Attached are two apps (taken from earlier threads) which are designed to do exactly this. I'm sorry but I don't know the author of either of these.
If anyone knows the authors or a link to the original threads please let me know

Both of these apps can check:
a) logical hard drive serial numbers (which change when the drive is reformatted)
b) physical hard drive number as issued by manufacturer (i.e. fixed)
c) motherboard & CPU numbers
So if an app is COPIED to another PC, this is detected & the app is prevented from running

However, this approach has a MAJOR flaw which I think renders the solution useless.

If you just download multiple copies of a db containing hard disk checking code of this type, each will be valid on the computer it first runs on.
Similarly that method won't prevent multiple installations if you install using an EXE file as I do.

At the moment therefore, I believe the only realistic way to prevent users installing more copies of a database than the number of licenses they own is to use the standard activation method. Hated by all but widely used by large software suppliers because it works

i.e. allow unlimited number of installs but have the program run in evaluation mode for e.g. 30 days during which time the user can get the app activated by the app supplier (in this case - me).

When a user sends an activation request, it will be 'silently accompanied by' info about that computer hard drive/CPU etc. I then check the number of licenses in use don't exceed the number purchased & issue activation code (or not) as appropriate

If the app hasn't been activated by the end of that evaluation period, it is automatically permanently disabled.

The issue with this is I have to keep a record of the number of activated PCs.
Also users changing PCs or hard disks have to go through the hassle of getting activation redone.

Of course, I'm well aware that even this approach can be hacked by those in the know - billions of examples of activation hacks online.

No system will ever be absolutely foolproof - however it should be possible to prevent casual piracy.
 

Attachments

  • DB_PropertyDemo.accdb
    464 KB · Views: 342
  • Security.accdb
    536 KB · Views: 335
Last edited:

moke123

AWF VIP
Local time
Today, 01:43
Joined
Jan 11, 2013
Messages
3,912
Re: load variable to a record

hey colin,
I wonder if you could adapt what I had. Put a 30 day trial period on the db. At the end of 30 days they provide you the unlock code. You can probably obscure it further by adding to the begining or end of it. I doubt any casual users would figure out the source of the number. You then just ?Hex(the number) and you have an unlock code.
 

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
Re: load variable to a record

Hi moke

Can you see why I think the HD check isn't that useful after all?

As an experiment, I did something similar to your suggestion in one of my databases a couple of years ago.http://www.mendipdatasystems.co.uk/exam-timer/4594138123

It worked fine. However that was for a site licence.

I can't work out a simple method that will allow users to purchase licenses for e.g. 5 computers, allow that number of installs and no more ...UNLESS each install is independently activated
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 00:43
Joined
Feb 28, 2001
Messages
27,140
Re: load variable to a record

The only methods I have seen that are really hard to hack involve writing some code that isn't VBA, but perhaps VB6. You then have to compile that to binary code (no embedded or associated source) so that it becomes harder to reverse-engineer. Make a .DLL out of it so that your VBA can activate it from your DB. Then you tell the user, "I'll send you the code you need to enable the program." Since your DLL is not easily reverse-engineered, key word being "easily," you can then use some of the Windows Crypto-API code to generate a hash key based on the disk or CPU serial number and some other value that you send in to the program by assembling odd bits of it and then converting it to a number that can be the 2nd component of the hash. You probably need a little side-app (or make it part of your main app) to take the key and perform a "register" step. This code, you CAN store in the database if you wish.

Then you have three entry points in the DLL (at least three, more as needed for procedural simplification): (1) Generate the hash from two inputs; (2) Retrieve the hash from a registry key that will probably be in the HKLM or HKCU registry hives; (2) STORE the hash to the appropriate hive if and ONLY if it is not already there. You never store the actual unlock code. What you store is the hash code and when you restart the app, you COMPARE your current reading of that hash to the stored version. But you NEVER allow anyone to see that computed hash because you never keep it in the database. And of course, the DB's FE file must be stored as a .MDE so that it is even hard for someone to reverse engineer how you use that DLL.

There is a third value that is part of many hash algorithms, and that is the "seed" value. When you have a "seed" number, it is typically small (like, single LONG size), and that would be embedded in the .DLL file, not an explicit input. The trick, of course, is that if you FOUND the hashed key and had both the serial number and the original value you gave to the customer, that hashing algorithm can be EXTREMELY difficult to reverse-engineer in order to determine the seed. And therefore, without the seed, even if your user FOUND the extra key, they would not be able to regen a new hash. And of course, if you use something like the MD5 hash (which is old news) you have a 256-bit key which would be tedious to manually edit. Beyond the skills of most casual hackers who are only interested in "theft of service" (i.e. piracy-level) hacking.

As has been pointed out, even this is breakable because it depends on your ability to keep at least some of the hash inputs obscured. Fail in obscuration and you fail in prevention of code piracy.
 

Mark_

Longboard on the internet
Local time
Yesterday, 22:43
Joined
Sep 12, 2017
Messages
2,111
@Ridders,

One way to limit the number of concurrent users is to resort to some truly horrible programming methods. Rather than using two fields for some of your tables (sales person AND order number, User name AND other field), you have the field be an array with <User number> elements. For some files this is not practical, but of others (such as checking records to print, date of sale, what have you) the element to use can be passed from a config file each user has on their local install.

This does take a bit of extra coding to also make it make sense in reports or otherwise dealing with the field, especially on computers other than the one where it was originally entered.

What will happen when the client tries to use another seat is they run into LOTS of issues where basic functions of the program do not work properly as two people are now using the same "Single user" fields.

This WONT stop a user from doing this but is enough of a headache for them to NOT want to put up with it. If your clients are businesses the problems should be sufficient to keep them from doing this.

NOTE: This only works if you use a split database (rather obviously) and if you have tables that would otherwise be in the front end instead stored in the back end using arrays.

Really not a good idea, but something to consider depending on your particular needs. Only upside is you will quickly find out who's exceeding their seat count when they start calling with problems ONLY caused by excess seat usage.
 

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
Hi Mark

Thanks for your reply
That's an interesting approach that I hadn't considered.
Bits of it could possibly be incorporated but overall the database that I'm preparing to release next doesn't really match that idea
See this page on my website : http://www.mendipdatasystems.co.uk/uk-postal-address-finder/4594138311

It is a split db (with 2 separate BE files) but the data will be available to all users.

I'm not bothered about the number of users - just the workstations
Having said that I could add a login feature as in my schools databases where user logins & workstations are tracked.
I could then surreptitiously track the logins ... but perhaps life is too short (or I need to get a life) !

The release date already over a month late due to other projects & this current issue.
I thought I had that sussed until I thought in depth about the hard drive issue described in the earlier post.

I'm still thinking about Doc's last reply as well
 
Last edited:

Mark_

Longboard on the internet
Local time
Yesterday, 22:43
Joined
Sep 12, 2017
Messages
2,111
@Ridders,

As I said not a great idea but one that can definitely do what your asking for. Only time I ever had to deal with something like this was a very long time ago when one of the other programmers (not long with the company) did something like this without realizing the headache he would cause others. Think about having to go into a module where someone else thought "Oh there will never be more than three of these at one time, best put it in an array" was a good idea.

Took me two days before I realized I had to junk all of it and start over.
 

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
@Ridders,

As I said not a great idea but one that can definitely do what your asking for. Only time I ever had to deal with something like this was a very long time ago when one of the other programmers (not long with the company) did something like this without realizing the headache he would cause others. Think about having to go into a module where someone else thought "Oh there will never be more than three of these at one time, best put it in an array" was a good idea.

Took me two days before I realized I had to junk all of it and start over.

I will confess that I have an instinctive dislike of arrays and only use them where there is no better way.
Thinking about that point makes my recent focus on importing JSON files to Access rather incongruous...!

The tracking system I mentioned in my schools databases meant we knew exactly which features were accessed by each user, which features were most / least used & could plan development time accordingly
I also built on that to create a full Access error reporting system.
This was done to solve the issue of errors either not being reported or being explained so badly it was hard to identify the cause & fix.
The way that worked was when an error was triggered, an email was sent automatically & silently to me with details of who/when/what & details of the computer used.
I had a couple of frantic weeks where I regretted the whole thing but after fixing those errors, the emails dried up almost to nothing.

There are definitely benefits to a big brother approach ....
 

Mark_

Longboard on the internet
Local time
Yesterday, 22:43
Joined
Sep 12, 2017
Messages
2,111


Big Brother isn't all bad...
 

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
That's brilliant.

Alexa - how can I secure my databases?
 

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
Re: load variable to a record

The only methods I have seen that are really hard to hack involve writing some code that isn't VBA, but perhaps VB6. You then have to compile that to binary code (no embedded or associated source) so that it becomes harder to reverse-engineer. Make a .DLL out of it so that your VBA can activate it from your DB. Then you tell the user, "I'll send you the code you need to enable the program." Since your DLL is not easily reverse-engineered, key word being "easily," you can then use some of the Windows Crypto-API code to generate a hash key based on the disk or CPU serial number and some other value that you send in to the program by assembling odd bits of it and then converting it to a number that can be the 2nd component of the hash. You probably need a little side-app (or make it part of your main app) to take the key and perform a "register" step. This code, you CAN store in the database if you wish.

Then you have three entry points in the DLL (at least three, more as needed for procedural simplification): (1) Generate the hash from two inputs; (2) Retrieve the hash from a registry key that will probably be in the HKLM or HKCU registry hives; (2) STORE the hash to the appropriate hive if and ONLY if it is not already there. You never store the actual unlock code. What you store is the hash code and when you restart the app, you COMPARE your current reading of that hash to the stored version. But you NEVER allow anyone to see that computed hash because you never keep it in the database. And of course, the DB's FE file must be stored as a .MDE so that it is even hard for someone to reverse engineer how you use that DLL.

There is a third value that is part of many hash algorithms, and that is the "seed" value. When you have a "seed" number, it is typically small (like, single LONG size), and that would be embedded in the .DLL file, not an explicit input. The trick, of course, is that if you FOUND the hashed key and had both the serial number and the original value you gave to the customer, that hashing algorithm can be EXTREMELY difficult to reverse-engineer in order to determine the seed. And therefore, without the seed, even if your user FOUND the extra key, they would not be able to regen a new hash. And of course, if you use something like the MD5 hash (which is old news) you have a 256-bit key which would be tedious to manually edit. Beyond the skills of most casual hackers who are only interested in "theft of service" (i.e. piracy-level) hacking.

As has been pointed out, even this is breakable because it depends on your ability to keep at least some of the hash inputs obscured. Fail in obscuration and you fail in prevention of code piracy.

Hi Doc

Apologies for the delay in responding.
I was away from my computer most of today.

I think I follow all of this but I fall at the first hurdle.
I've never created a DLL and don't know how to do so. Visual Studio perhaps?
The encryption & registry retrieval aspects aren't an issue and indeed were already included as part of my solution.

My current plan (subject to further refinement) is:
a) Create ACCDE file with nav pane / ribbon hidden & shift key bypass disabled. Also hide tables so they can't be viewed externally
b) Package to EXE file for installation
c) Licence key needed for installation
d) User/program data saved to registry
e) Program opens in evaluation mode, checks computer info such as HD/CPU/motherboard serial numbers, saves data as properties & possibly to registry (encrypted)
f) User has e.g. 30 days to activate after which program disabled
g) Activation process will silently send email to me with all details from b/c/d above. I check and issue activation code (matched to above data for that computer) but only if licence limit not exceeded

If user copies front end or installs a fresh copy on a different computer, this re-triggers evaluation mode & e/f/g repeated

However site license option will also be available.
This will have a different licence key pattern
If so, activation process (e/f/g) not required

SIMPLES !!!
If you can see any flaws in the above or suggest further improvements, do let me know
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 00:43
Joined
Feb 28, 2001
Messages
27,140
Step G fails for some registry settings and for some Internet Security packages because you cannot "silently" send e-mail. That is a viral behavior and is therefore trapped by any good A/V package with any level of behavior checking or heuristics checking. It is perhaps just my opinion, but you can't tell someone "You have to let my product silently send mail." And not all A/V products can accept a list of apps that are allowed to send mail silently. Now, you can SEND it - but for example, Outlook will warn you based on Windows and Office policy settings.

As to creating a .DLL, there is always the "great Google brain" as a reference. I've never done this in Windows. My mainframe, however, had a license management service.

Thinking about it, I knew that MS had such a service - by implication. So it come to mind that there had to be a service available for other use. Have you looked at the SLAPI?

https://msdn.microsoft.com/en-us/library/windows/desktop/cc296101(v=vs.85).aspx

Supposedly this worked under Vista. Don't have an idea as to what, if anything, has changed for Win 7, 8.x, or 10. But if there is something checking for a "genuine Windows installation" then it has to be managed by a service. And since MS has Office products on a license as well, I know they won't reinvent the wheel for a cash product. So it is a matter of finding the license interfaces. If you dig around from the starting point, I'm sure you can find something useful.
 

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
Thanks for your reply

Step G fails for some registry settings and for some Internet Security packages because you cannot "silently" send e-mail. That is a viral behavior and is therefore trapped by any good A/V package with any level of behavior checking or heuristics checking. It is perhaps just my opinion, but you can't tell someone "You have to let my product silently send mail." And not all A/V products can accept a list of apps that are allowed to send mail silently. Now, you can SEND it - but for example, Outlook will warn you based on Windows and Office policy settings.

I have used this approach successfully for several years with various apps to send details of user info / error messages 'home'.
The email is sent direct from the app using CDO so no email app required.
However, the word 'silently' may have been misleading - automatically may be a better descriptor
Never had an issue with being blocked though I expect you will respond "well you wouldn't know if your message was blocked".
Its not furtive - users are told that an email will be sent automatically & why
However I have always made it clear that if this can't be done, they would need to send an equivalent email themselves. So far, that's never been needed with any of my clients - but then I don't have contracts with US Defence Dept

As to creating a .DLL, there is always the "great Google brain" as a reference. I've never done this in Windows. My mainframe, however, had a license management service.

All the articles I found all related to C++ which I don't have/use
For example, see: https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp
That's why I referred to Visual Studio in my previous answer

Thinking about it, I knew that MS had such a service - by implication. So it come to mind that there had to be a service available for other use. Have you looked at the SLAPI?

https://msdn.microsoft.com/en-us/library/windows/desktop/cc296101(v=vs.85).aspx

Supposedly this worked under Vista. Don't have an idea as to what, if anything, has changed for Win 7, 8.x, or 10. But if there is something checking for a "genuine Windows installation" then it has to be managed by a service. And since MS has Office products on a license as well, I know they won't reinvent the wheel for a cash product. So it is a matter of finding the license interfaces. If you dig around from the starting point, I'm sure you can find something useful.

Ah ... the much loathed "Genuine Windows" feature.
I haven't looked into it but I seem to recall how unreliable it was, lots of 'false negatives'.
I think its no longer used as you no longer see similar messages
 
Last edited:

gemma-the-husky

Super Moderator
Staff member
Local time
Today, 06:43
Joined
Sep 12, 2006
Messages
15,634
I hope this idea helps.

A client can have as many copies of the databases as they want, but only a certain number can work concurrently.

To manage the number of concurrent users, I have a user table with a fixed maximum number of rows. Once this is full, then no more users can connect.

The users slot holds details of the active user, and anything else you want to store.

If a user is disconnected for some reason without the table slot being emptied other users have the facility of taking over that slot. This is similar to the way Sage appears to manages multi-user access, I believe.

Forcing users to log back in each time they "take over" a session makes it impractical for an excessive number of active users to "fool" the system by continually taking over each other's sessions.

I can use a registration string to manage the maximum number of concurrent users allowed, so if the number of users grows from 5 to 10, the new limit of 10 is built into the general licence set up. Alternatively you can encrypt the maximum value allowed and store it in a table. As long as they can't reverse engineer the encryption, the client can't easily change a licence for 5 users to a licence for 10, and you can send them the encrypted value for the number of users.
 
Last edited:

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
Hi Dave

You are an absolute star!

What a brilliantly simple idea which had never occurred to me....
If I can implement that, I think I can abandon the whole idea of activation.

There is of course the risk that users will get annoyed when the limit has been reached & they can't logon ... but if so, hopefully they would buy more licenses from me.

I'm definitely going to think this through in detail

Going back to my list in post #11, my initial thoughts is this would probably make steps e, f & g redundant

I can use the userID & workstation ID in the users table together with date/time on & date/time off - I already have this setup in another db. I could also add hard disk info but that's probably not necessary with this approach.
I also have code to log users out after a set period of inactivity which would free up the slot

My license keys also are used to indicate the number of licenses
However, if a site license is purchased, this approach could be disabled allowing unlimited concurrent users

If I have any questions, is it OK if I contact you by PM or email?
 

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
A belated follow up to this thread

I've now completed the licensing / activation code for this part of my latest project using the ideas suggested by Dave (Gemma the husky) & it works well. Thanks again Dave

Other security items include those in post 11
However there's a fly in the ointment with this statement of mine from that post...

Also hide tables so they can't be viewed externally
Unfortunately, they CAN still be viewed externally as the 'show hidden objects' property resides with the current access application, not the 'target' db.

So if a user opens a new db, sets the navigation properties to show hidden objects then links to the db with hidden tables, they are visible & could be imported & opened in the usual way .....

The same applies to system tables so there's no point prefixing with USys either.
Does anyone know of a solution for this issue?

Whilst the database will be password protected it could be good to have that extra layer of security.
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 00:43
Joined
Feb 28, 2001
Messages
27,140
First, Colin, I don't have good answers for how to do this. It will always depend on how much you might wish to pay for the solution (commercially or in the hours you spend to build it).

However, in modern security methods, at least part of the problem isn't whether you can hide something or protect it. It is HOW LONG can you hide or protect it? Implication - HOW LONG before someone figures out how to bypass your safeguards? Modern encryption and security methods are merely speed-bumps along the road. Even within the U.S. Dept. of Defense we knew it was all a matter of slowing down the bad guys enough to give us time to catch them and track them down, after which someone could arrest them (domestic) or drone them (foreign, enemy) or infect them (foreign, not clearly enemy).

Here is the central dilemma you face. Access, being an intended small-system solution, is going to have small-system safeguards built in to it. You can do things to sign the database and encrypt it. You can put a password on it - but it is a one-password-fits-all situation. You can make decompilation impossible so as to prevent hacks of your code. But the bottom line is that the data has to be accessible to YOU if you hold the right keys, whatever they are. And that is the central conundrum to ALL security solutions. It has to work for you but not for the bad guy and yet you both are using the same basic tool set.

Access is not SET UP to do things like encrypt its data using 256-bit CBC or one of the more modern cypher series. It just isn't set up that way. You have to make your plans with a limited tool kit. Which is why some folks use active back-end systems that can do a better job of hiding the content if you don't have the right keys. I'm specifically thinking of the ability to open a BE and import the data, which is possible with an Access BE but not so much when you are talking something that uses encrypted SQL channels on a server that won't open terminal-services (TLS/SSL) ports or file-services (SMB) ports for you.

Given that MicroSoft itself supports external BE servers (SQL Server immediately comes to mind) to provide this kind of protection, I think that as long as we are talking an all-Access solution, we will see only very limited offerings and abilities for this problem. It is certainly NOT in MicroSoft's best interest to make Access more secure when self-contained vs. providing another product (for more money, of course) if you are that worried about security issues.

I don't know that I helped you with your direct question, but I think it is important to keep a good perspective on WHY you have what you have at the moment.
 

isladogs

MVP / VIP
Local time
Today, 06:43
Joined
Jan 14, 2017
Messages
18,209
Thanks for the reply Doc

I think it is important to keep a good perspective on WHY you have what you have at the moment.

A very good point. Sometimes its too easy to get bogged down in detail and lose the overall view.

I had considered and rejected using SQL server for the BE databases (plural) for this app as that wouldn't suit the likely clients.
In addition, SQL Server whilst much more secure overall wouldn't allow me to hide the data from those with access to SQL Server itself.
Hence back to Access for both FE & BE databases.

As i said at the start of this thread:
No system will ever be absolutely foolproof - however it should be possible to prevent casual piracy.

My reason for raising this was the realisation that something so simple could so easily be 'overwritten'
 

Users who are viewing this thread

Top Bottom