NAME ==== KHPH.pm6 VERSION ======= 0.0.5 TITLE ===== Keep Honest People Honest SUBTITLE ======== Unsecure Reversible Obfuscated Character Storage & Retrieval Disclaimer ========== This module scrambles a string, which is vulnerable to unscrambling. On the surface, one might ask, "Why even bother providing a scrambling function?" It is helpful to have such a function because there are still cases that aren't well-addressed yet with today's systems and applications. What if you you need to run a program that absolutely requires you to include a *password* in its command line invocation? myuid@myserver> /usr/local/bin/someappmgr -id=U028441 -pass=pAsSwOrD57! START INSTANCE ABC If you run it interactively, your shell history will record the command line for posterity, including the exposed password. What if this needs to be executed through a job scheduler? 00 01 * * 7 /opt/SomeApp/bin/someappmgr -id=xyx259 -pass=PaSsWoRd22@! run_weekly_reports The password would be exposed in multiple places: crontab, logs, email, etc. You could put the secret characters in a directory/file and judiciously apply DAC controls to restrict access (chown/chgrp/chmod). *root* __would__ be able to look at your secret with a quick `cat` command, so your secret isn't really a secret anymore. This module offers you a way to reduce the likelihood of inadvertently exposing your secret to curious people who are just poking around. It does not purport to fully protect your secret information. The idea is that if you sufficiently scramble the secret string before storing it, reversing the obfuscation manually could induce just enough ennui to dissuade the amateur cracker sitting in the next cubicle. Anyone trying to unscramble a KHPH string would have to put in a little effort. They would be committing themselves to a deliberate __bad__ act to get at your secret. Sometimes they might even get noticed. Watch them gulp when you confront them as to why they are breaking into something that doesn't belong to them. Maybe point out the stated policy in your login herald about what is allowed and what isn't. Adding a little resistence sometimes pays off. Use this module if you are going to store your clear-text secret string in a vulnerable place due to pure necessity. It would be better than clear-text storage. Just remember, this module's obfuscation is relatively trivial. It simply provides a little more resistance to snoops. Use this as a last resort until you find something better. Think of it like the difference in playing hide-and-go-seek with a 2-year-old versus a 10-year-old. The 10-year-old stays out of sight and can be kind of clever about it. Description =========== This module will scramble a string, stash it wherever specified, then expose it to you whole again when you ask for it, interactively or in batch (I.e. CRON). *root* can’t expose it directly, unless *root* originally stored it. SU’ing into the owner’s account from a different account won’t expose it directly either. It’s not in the direct line of site by anyone other than the owner. Synopsis ======== use KHPH; my $userid = 'testid'; my KHPH $secret-string .= new( :herald('myapp credentials'), :prompt($userid ~ ' password'), :stash-path('/tmp/myapp/' ~ $userid ~ '/mysecret.khph'), :user-exclusive-at('/tmp/myapp/' ~ $userid); ); say $secret-string.expose; Methods ======= .new() ------ Generate a KHPH object #### :herald? Optional announcement used only when interactively stashing the secret. #### :prompt? Optional prompt string used only when interactively stashing the secret. #### :secret? Optionally send the constructor the secret string. No prompting will occur. #### :stash-path! Specify the path (directories/file) to create or find the stash file. Always use a subdirectory to store the secret, as KHPH will chmod the directory containing the stash file. #### :user-exclusive-at? Optionally specify a segment of the :stash-path to exclude all group & other access (0700). .expose() --------- Return the secret as a clear-text Str. Example I ========= The following stand-alone script will manage the password stash of `myapp` in the user's home directory. Run it interactively one time to stash your secret, then you (not someone else) can run it anytime to expose the secret. A script in the user's home directory named `myapp-pass.pl6` as follows: #!/opt/rakudobrew/bin/perl6 use KHPH; my KHPH $passwd .= new(:stash-path(%*ENV ~ '/.myapp/password.khph')).expose.print; Run ~/myapp-pass.pl6 once interactively to stash the secret. Then in your ancient application client: /usr/bin/dsmadmc -id=MYSELF -password=`~/myapp-pass.pl6` QUERY SESSION FORMAT=DETAILED This particular application client is smarter than most, in that it re-writes the process' args after the program launches so that `ps` will only display `-password=*******` instead of the actual password string. Not all vendors pay attention to such details, so beware -- `ps` could be displaying the secret despite your efforts to protect it! Example II ========== The following `acme-connect` script, which manages the fictitious ACME application, is implemented so that all passwords are stored in a common directory: /var/raduko/.credentials Ensure that all users can descend to that directory. It is ideal to set 1777 to the last directory in that path. Different users run the script to connect to instances of the ACME application on multiple hosts. | OS Login | Application Host | Application Userid | Application Password | | -------- | ----------------- | ------------------ | --------------------- | | userid_a | acme1.myco.com | ACMEUSERX | pAsSwOrDx | | userid_b | acme1.myco.com | ACMEUSERY | pAsSwOrDy | | userid_c | acme2.myco.com | ACMEUSERZ | pAsSwOrDz | The `acme-connect` script: #!/opt/rakudobrew/bin/perl6 use KHPH; sub MAIN ( :$acme-host is required, #= ACME Host :$acme-id is required, #= ACME UserId :$start-monthly-batch, #= Launch monthly batch processing ) { my KHPH $passwd .= new( :herald('ACME credentials'), :prompt($acme-id ~ '@' ~ $acme-host ~ ' password'), :stash-path('/var/raduko/.credentials/' ~ $*USER ~ '/ACME/' ~ $acme-host ~ '/' ~ $acme-id ~ '.khph'), :user-exclusive-at('/var/raduko/.credentials/' ~ $*USER), ); # Assemble the acme-manager command parts my @cmd = '/usr/bin/acme-manager', '--server=' ~ $acme-host, '--id=' ~ $acme-id, '--password=' ~ $passwd.expose; if $start-monthly-batch { @cmd.push: '--start-monthly-batch'; } else { @cmd.push: '--status'; } # Run ACME run @cmd; } The first time the `acme-connect` script is run interactively by userid_a: userid_a@linux> acme-connect --acme-host=acme1.myco.com --acme-userid=ACMEUSERX ACME credentials [1/2] ACMEUSERX@acme1.myco.com password> pAsSwOrDx [2/2] ACMEUSERX@acme1.myco.com password> pAsSwOrDx ACME Status Report: A-OK userid_a@linux> KHPH will produce the following hierarchy: * 1777 /var/raduko/.credentials/ userid_a 0700 /var/raduko/.credentials/userid_a userid_a 0700 /var/raduko/.credentials/userid_a/ACME userid_a 0700 /var/raduko/.credentials/userid_a/ACME/acme1.myco.com userid_a 0600 /var/raduko/.credentials/userid_a/ACME/acme1.myco.com/APPUSERX.khph The `acme-connect` script will not prompt userid_a for the application password again and any variation of this command invocation including the `--pass=` switch can be placed in a job scheduler for subsequent runs by userid_a (only) from this server (only). When the `acme-connect` script is run interactively by userid_c: userid_c@linux> acme-connect --acme-host=acme2.myco.com --acme-userid=ACMEUSERZ ACME credentials [1/2] ACMEUSERZ@acme2.myco.com password> pAsSwOrDz [2/2] ACMEUSERZ@acme2.myco.com password> pAsSwOrDz ACME Status Report: A-OK userid_a@linux> The following hierarchy will result: * 1777 /var/raduko/.credentials/ userid_a 0700 /var/raduko/.credentials/userid_a userid_a 0700 /var/raduko/.credentials/userid_a/ACME userid_a 0700 /var/raduko/.credentials/userid_a/ACME/acme1.myco.com userid_a 0600 /var/raduko/.credentials/userid_a/ACME/acme1.myco.com/APPUSERX.khph userid_c 0700 /var/raduko/.credentials/userid_c userid_c 0700 /var/raduko/.credentials/userid_c/ACME userid_c 0700 /var/raduko/.credentials/userid_c/ACME/acme2.myco.com userid_c 0600 /var/raduko/.credentials/userid_c/ACME/acme2.myco.com/APPUSERZ.khph The `acme-connect` script will not prompt userid_c for the application password again and any variation of this command invocation including the `--pass=` switch can be placed in a job scheduler for subsequent runs by userid_c (only) from this server (only). Limitations =========== Only developed on Linux. Roadmap ======= Add :email-address to notify owner of unsuccessful .expose() events Author === Mark Devine