24

I have a cronned Perl script that connects to our database and does various kinds of lookups and integrity checks. The original script was written by someone long ago. My job is to make some changes to it. But I really don't like staring at the username="foo", password="bar" parameters hard-coded in there for accessing the database.

There's got to be a more secure way of doing this. All I could think of to do for now is to comment out the cron job, delete the line in the script that had the password, and start brainstorming about how to make this more secure. But meanwhile the things the script does have to be done by hand.

Any ideas?

kalina
  • 3,354
  • 5
  • 20
  • 36
Luke Sheppard
  • 2,217
  • 3
  • 15
  • 21

7 Answers7

25

The standard way is to put the credentials into a config file, and attempt to protect the config file from being more readable than the perl file. This offers a moderate increase in security; for example, the code may be in source control and accessible to developers, the config file wouldn't be. The code needs to be in the web server's cgi root, and possibly downloadable under certain misconfigurations, and the config file needn't be.

The ambitious way is to reversibly encrypt the credentials and put them into a config file. Of course, anything reversibly encrypted can be decrypted. The BladeLogic application did this, and it was trivial (<1 day) for me to de-compile their Java enough to find out the function to decrypt credentials and use it to decrypt them to my satisfaction. Not a mark against them; that's just the name of the reversibly encrypted game.

Another option is to use OS-based authorization in concert with strictly limited database restrictions. For example, limit the database client user's access to a set of stored procedures to limit the potential for abuse, and allow that user to access the database without a password. This doesn't work if you're doing client-server over the network, which limits how often it's useful. Also, people tend to look more askance at "passwordless" OS-user access than they do at writing the password down willy-nilly. It is not completely logical, but there are standards that say all database users must have passwords, so that's that.

gowenfawr
  • 71,975
  • 17
  • 161
  • 198
  • Great answer. Thank you. One bit of good news is that there is no webserver involved in any of this. I like the idea of a combination of your first two methods: config file + encrypting the password. They are more specific versions of some fuzzy ideas I was thinking about. – Luke Sheppard Sep 20 '12 at 22:20
  • Given that we don't have very many database user accounts to manage, and given that my script only needs to run three different queries, I'm thinking that I'll create a database user that can only run three stored procedures, as you recommended. But I'll require that user to log in with a password. That, in concert with the config file and reversible encryption, will let me sleep at night. I much prefer layers of security, especially in systems that run automatically, so might get forgotten over time. Thank you again. – Luke Sheppard Oct 21 '12 at 19:21
8

I often pass the password to the script in an environment variable that way the script need not have access to the secure location that contains the password.

PASSWORD=`cat passwd_file`  perl_script.pl

then the script reads the password

my $password = $ENV{'PASSWORD'}

bonus points if the perl script drops privileges

Arthur Ulfeldt
  • 364
  • 1
  • 6
  • As long as /proc//environ (or equivalent) is not readable by anyone else (it usually isn't) – symcbean Sep 20 '12 at 23:40
  • 1
    This sounds OK, but I don't see how it is any different than just storing the password in a text file separate from the main script, and having the script read the password that way. In your method, the owner of both scripts is the same or at least has the same level of permissions on the two files, right? – Luke Sheppard Sep 21 '12 at 01:31
  • There's one nice difference here worth noting. This separates the user / group the script runs as from the user / group ownership of the password. For instance, `root` might be the only one who should have access to the password, but the script needs to run with the same permissions as the web app you're running on the machine. – Stephen Touset Sep 21 '12 at 01:53
  • 1
    OK. Then if root is the only user that can access the password file, then this line (that you typed) must be run by root, right? PASSWORD=`cat passwd_file` perl_script.pl But running the script as root is a bad idea. So I'm missing something here. – Luke Sheppard Sep 21 '12 at 21:59
  • It's better to put it in a user environment variable over a file as someone will invariably check that file into source control. – Neil McGuigan Oct 26 '13 at 08:56
  • This is 2015, and the advent of the microinstance. Everything should be in it's own container. – munchkin May 03 '15 at 08:07
4

As others have already said, put the credentials in a separate file which the script will load. Risks of having the password in the script include its being exposed to shoulder surfing, its being committed in revision control systems or configuration management systems and distributed far beyond what it should be, accidentally copying to another location when reusing part of the code, etc.

The credentials file should have the minimum permissions necessary. It should possibly not be included or treated differently by configuration management systems.

If available, look into using an operating system feature that limits the access to the credential file further than permissions. For example, AppArmor or SELinux under Linux can restrict the access to the credential file to the one script that needs it. This only protects against an attacker who cannot take control of the legitimate script, so make sure the legitimate script is not owned by the user who runs it (this is a general recommendation: don't have executable programs owned by the user that runs them).

Gilles 'SO- stop being evil'
  • 50,912
  • 13
  • 120
  • 179
  • This sounds like a good idea: "this is a general recommendation: don't have executable programs owned by the user that runs them", but I'm not clear on *why* it is a good idea. What attacks does this prevent? – Luke Sheppard Sep 21 '12 at 21:40
  • 1
    @LukeSheppard This is especially a warning against setuid programs on unix: if you can compromise a setuid program then you can write to the program executable and turn it into a trojan to infect the users who run the program. Example: [the Debian policy is to make games that write a system-wide high score file setgid, not setuid](http://www.debian.org/doc/debian-policy/ch-customized-programs.html#s11.11), so that at most a security hole allows the user to cheat. But more generally, it's a way to turn a vulnerability that allows writing to a file into one that allows arbitrary code execution. – Gilles 'SO- stop being evil' Sep 21 '12 at 21:42
2

The consensus seems to be:

  1. The password should be in a separate file, not part of version control, and protected with permissions more restrictive than that of the script.
  2. Encrypting the stored password is a waste of time since the script needs to be able to decrypt the password anyway.

But I'm thinking of adding a third control on top of those: Time Based (temporal) Access Control might help out a bit here too. Limiting the amount of time that the cleartext password is available to lower privileged users would add another (albeit small) layer of access control between the password and any local attacker.

My idea is to have root own the password file, set to mode 0400. And then have a root cronjob that does a chmod 0404 on the password file just before the lower-privileged cronjob that runs the script. At a specified interval later (hopefully after the script is done running), a second root cronjob does a chmod 0400 on the password file. This all assumes that the script only needs the password for some period of time less than always. Your thoughts on this scheme?

Luke Sheppard
  • 2,217
  • 3
  • 15
  • 21
  • By the way, I can't find a URL pointing to any general, authoritative reference on the web for Time Based Access Control. Cisco routers still support it, and it used to be used in large dial-in modem pools for corporate and government systems. But I think it is generally a phenomenon of 20th century mainframe security systems. I think that the concept back then was something like, "Legitimate users would never dial in to the mainframe outside of normal business hours". But I don't think I've ever seen it used for controlling intra-system file access. – Luke Sheppard Sep 21 '12 at 01:42
  • 1
    Time-based access control would only help in rather contrived circumstances (e.g. if the attacker can only read files and not execute arbitrary commands, and the vulnerability is against a scheduled task that runs at a time the credentials file is inaccessible, and the attacker cannot delay the task). Adding this complexity for no real gain in security would be counterproductive. – Gilles 'SO- stop being evil' Sep 21 '12 at 10:13
  • 1
    It provides a limited amount of additional security in exchange for excessive complexity. I won't go so far as @Gilles and say it's counterproductive, but it is clearly a makeshift bandaid that will end up biting you on the day the script can't read the file (you need to run it by hand, for example). What you really want is some form of MAC; for example, a tool that only permits your script to read that file and not other programs. Cisco Security Agent used to offer this sort of functionality, but is now defunct. I would love to find a good product that picked up that mantle. – gowenfawr Sep 21 '12 at 11:26
  • Thanks gowenfawr. Excellent point about the added complexity. I've always believed that security controls are less effective as system complexity increases. – Luke Sheppard Sep 21 '12 at 21:43
0

This is what I am planning doing for a script which connects to a remote database. It requires 2 servers in order to store the key separately from the encrypted value.

  1. On Server 2 (where the key will be stored), create a script that will accept a key parameter and store it locally.
  2. On Server 1 (where the script is stored), Create a script which will accept the connection string (host, db_name, username, password) as a parameter and generate a random key to encrypt the connection string. We store the encrypted string locally, hash the key and store that locally, then send the key (over SSL) to server 2. This script will need to be run manually with the actual connection string to initialize it and can be re-run to reset the key if the second server somehow becomes compromised.
  3. On Server 1 Set up the original script to accept a key parameter, validate the key against the stored hash, then use the key to decrypt the encrypted connection string.
  4. On Server 2 Set up a cURL call to the script on Server 1 (over SSL) which sends the key and runs the script. This can be set up on cron or however you want it to run.
knsheely
  • 101
0

Would this be an appropriate solution:

  1. Password is stored in a compiled C/C++ program that is obfuscated like so: char password[100]; password[0] = 'a'; password[1] = 'b'; password[2] = 'c'; password[3] = '\0';

Or some other way of obfuscating the password. (password should not be available by running "strings" on the executable)

  1. The C/C++ program only returns the password to trusted, registered scripts: A. Get ppid and check /proc//cmdline, /proc//comm, etc. B. Get inode of caller script and compare with registered identity, to ensure the script was not changed.
  • 2
    While your solution will indeed hide the password from `strings` and such, it will not survive any further reverse engineering. For example, disassembly or decompilation could reveal the character assignment, or attaching a debugger to the running program and reading the stack will certainly show the password as a whole. – multithr3at3d Jan 05 '18 at 18:25
0

I like putting configs in ~/config/appname and having a generic config for my local mysql (or any other) connection. If you don't want to give your home a X permission and run the app as a different user have a standard config folder someplace. I do this so i don't accidentally package or give any passwords or usernames to other programmers. I dont think its a big deal but my boss is a security freak and its a good habit especially when publishing anything to the web.

It'd be wise to write a standard oneliner or twoliner for your config files and copy/paste them into any new scripts you write