I need to safely store the username and password in Python, what are my options?

I am writing a small Python script that will periodically retrieve information from a third-party service using a combination of username and password. I don’t need to create something that is 100% bulletproof (does it even exist 100%?), But I would like to bring in a good security measure, so at least someone would need to break it.

This script will not have a graphical interface and will be run periodically using cron , so entering a password every time it is run for decryption will not work, and I will need to save the username and password in an encrypted file or encrypted in a SQLite database, which would be preferable since I will use SQLite anyway, and I may need to change the password at some point. In addition, I will probably wrap the entire program in EXE, as it happens exclusively for Windows at this point.

How can I safely store a username and password combination for periodic use with a cron job?

+59
python security encryption
Aug 10 '11 at 17:13
source share
8 answers

I recommend a strategy similar to ssh-agent . If you cannot use ssh-agent directly, you can implement something like this so that your password is stored only in RAM. A cron task could configure the credentials to receive the actual password from the agent each time it starts, use it once, and immediately delete it using the del statement.

The administrator still needs to enter the password to run ssh-agent, at boot time or something else, but this is a reasonable compromise that avoids using a plain text password stored anywhere on the disk.

+14
Aug 10 2018-11-18T00:
source share

The python key library integrates with the CryptProtectData API on Windows (along with the corresponding API on Mac and Linux), which encrypts the data with the login credentials.

Simple use:

 import keyring # the service is just a namespace for your app service_id = 'IM_YOUR_APP!' keyring.set_password(service_id, 'dustin', 'my secret password') password = keyring.get_password(service_id, 'dustin') # retrieve password 

Use if you want to save the username in the key fob:

 import keyring MAGIC_USERNAME_KEY = 'im_the_magic_username_key' # the service is just a namespace for your app service_id = 'IM_YOUR_APP!' username = 'dustin' # save password keyring.set_password(service_id, username, "password") # optionally, abuse 'set_password' to save username onto keyring # we're just using some known magic string in the username field keyring.set_password(service_id, MAGIC_USERNAME_KEY, username) 

Later, to receive information from the keyring

 # again, abusing 'get_password' to get the username. # after all, the keyring is just a key-value store username = keyring.get_password(service_id, MAGIC_USERNAME_KEY) password = keyring.get_password(service_id, username) 

Elements are encrypted with the credentials of the user's operating system, so other applications running in your user account will be able to access the password.

To hide this vulnerability a little, you could somehow encrypt / obfuscate the password before storing it in the key fob. Of course, anyone who targets your script will just be able to look at the source and figure out how to decrypt / find out the password, but at least you would have to prevent some application from clearing all passwords in the repository and receiving your ,

+28
Aug 07 '15 at 16:08
source share

Having looked at least the answers to this and related questions, I collected some code using several proposed methods of encryption and hiding secret data. This code is intended for cases where the script should be run without user intervention (if the user starts it manually, it is best that he enter the password and save it only in memory, as the answer to this question suggests). This method is not super safe; in principle, a script can access sensitive information, so anyone with full system access has a script and associated files and can access it. What this does is, id hides data from accidental inspection and leaves data files safe if they are considered separately or together without a script.

My motivation for this is a project that polls some of my bank accounts to monitor transactions - I need it to work in the background without changing passwords again every minute or two.

Just paste this code at the top of your script, change saltSeed, and then use store () retrieve () and require () in your code if necessary:

 from getpass import getpass from pbkdf2 import PBKDF2 from Crypto.Cipher import AES import os import base64 import pickle ### Settings ### saltSeed = 'mkhgts465wef4fwtdd' # MAKE THIS YOUR OWN RANDOM STRING PASSPHRASE_FILE = './secret.p' SECRETSDB_FILE = './secrets' PASSPHRASE_SIZE = 64 # 512-bit passphrase KEY_SIZE = 32 # 256-bit key BLOCK_SIZE = 16 # 16-bit blocks IV_SIZE = 16 # 128-bits to initialise SALT_SIZE = 8 # 64-bits of salt ### System Functions ### def getSaltForKey(key): return PBKDF2(key, saltSeed).read(SALT_SIZE) # Salt is generated as the hash of the key with it own salt acting like a seed value def encrypt(plaintext, salt): ''' Pad plaintext, then encrypt it with a new, randomly initialised cipher. Will not preserve trailing whitespace in plaintext!''' # Initialise Cipher Randomly initVector = os.urandom(IV_SIZE) # Prepare cipher key: key = PBKDF2(passphrase, salt).read(KEY_SIZE) cipher = AES.new(key, AES.MODE_CBC, initVector) # Create cipher return initVector + cipher.encrypt(plaintext + ' '*(BLOCK_SIZE - (len(plaintext) % BLOCK_SIZE))) # Pad and encrypt def decrypt(ciphertext, salt): ''' Reconstruct the cipher object and decrypt. Will not preserve trailing whitespace in the retrieved value!''' # Prepare cipher key: key = PBKDF2(passphrase, salt).read(KEY_SIZE) # Extract IV: initVector = ciphertext[:IV_SIZE] ciphertext = ciphertext[IV_SIZE:] cipher = AES.new(key, AES.MODE_CBC, initVector) # Reconstruct cipher (IV isn't needed for edecryption so is set to zeros) return cipher.decrypt(ciphertext).rstrip(' ') # Decrypt and depad ### User Functions ### def store(key, value): ''' Sore key-value pair safely and save to disk.''' global db db[key] = encrypt(value, getSaltForKey(key)) with open(SECRETSDB_FILE, 'w') as f: pickle.dump(db, f) def retrieve(key): ''' Fetch key-value pair.''' return decrypt(db[key], getSaltForKey(key)) def require(key): ''' Test if key is stored, if not, prompt the user for it while hiding their input from shoulder-surfers.''' if not key in db: store(key, getpass('Please enter a value for "%s":' % key)) ### Setup ### # Aquire passphrase: try: with open(PASSPHRASE_FILE) as f: passphrase = f.read() if len(passphrase) == 0: raise IOError except IOError: with open(PASSPHRASE_FILE, 'w') as f: passphrase = os.urandom(PASSPHRASE_SIZE) # Random passphrase f.write(base64.b64encode(passphrase)) try: os.remove(SECRETSDB_FILE) # If the passphrase has to be regenerated, then the old secrets file is irretrievable and should be removed except: pass else: passphrase = base64.b64decode(passphrase) # Decode if loaded from already extant file # Load or create secrets database: try: with open(SECRETSDB_FILE) as f: db = pickle.load(f) if db == {}: raise IOError except (IOError, EOFError): db = {} with open(SECRETSDB_FILE, 'w') as f: pickle.dump(db, f) ### Test (put your code here) ### require('id') require('password1') require('password2') print print 'Stored Data:' for key in db: print key, retrieve(key) # decode values on demand to avoid exposing the whole database in memory # DO STUFF 

The security of this method will be greatly improved if os permissions are set in the secret files to allow the script itself to read them, and the script itself was compiled and marked as an executable file (not readable). Some of them can be automated, but I did not bother. To do this, you probably need to configure the user for the script and run the script as this user (and establish the ownership of the script files for this user).

I like any suggestions, criticism or other vulnerabilities that anyone might think of. I am new to writing cryptocode, so what I did can almost certainly be improved.

+22
Jan 24 '13 at 3:54 on
source share

I think that best of all you can protect the script file and the system it runs on.

Basically follow these steps:

  • Use file system permissions (chmod 400)
  • Strong password for the owner account in the system
  • Reduce the chance of hacking the system (firewall, disable unnecessary services, etc.)
  • Remove administrator privileges / root / sudo for those who do not need it
+6
Aug 10 2018-11-11T00:
source share

It makes no sense to try to encrypt the password: the person from whom you hide it has a Python script that will have a code to decrypt it. The fastest way to get a password would be to add a print statement to the Python script just before using the password using a third-party service.

So, save the password as a string in the script, and base64 encode it so that just reading the file is not enough, and then call it day.

+4
Aug 10 '11 at 17:18
source share

There are several options for storing passwords and other secrets that Python should use, especially a program that should run in the background, where it cannot just ask the user to enter a password.

Problems to avoid:

  1. Password verification in the version control system, where other developers or even the public can see it.
  2. Other users on the same server read the password from the configuration file or source code.
  3. Having a password in the source file, where others can see it over your shoulder while you are editing it.

Option 1: SSH

This is not always an option, but probably the best. Your private key is never transmitted over the network, SSH just does the math to prove that you have the right key.

For this to work, you need the following:

  • The database or whatever you are accessing must be accessible via SSH. Try to find "SSH" plus any service you are accessing. For example, "ssh postgresql" . If this is not a function in your database, go to the next option.
  • Create an account to start the service that will make calls to the database, and generate the SSH key .
  • Either add the public key to the service you are about to call, or create a local account on this server and set the public key there.

Option 2: environment variables

This one is the easiest, so it can be a good start. This is well described in the Twelve Factors appendix. The basic idea is that your source code simply extracts a password or other secrets from environment variables, and then you configure these environment variables on each system in which you run the program. It can also be nice if you use defaults that will work for most developers. You must balance this in order to make your software "secure by default."

Here is an example that retrieves the server username and password from environment variables.

 import os server = os.getenv('MY_APP_DB_SERVER', 'localhost') user = os.getenv('MY_APP_DB_USER', 'myapp') password = os.getenv('MY_APP_DB_PASSWORD', '') db_connect(server, user, password) 

See how to set environment variables on your operating system, and think about starting the service under your own account. Thus, you do not have sensitive data in environment variables when you run programs under your account. When you configure these environment variables, be especially careful that other users cannot read them. Check file permissions, for example. Of course, any user with root privileges can read them, but there is nothing to be done about it.

Option 3: configuration files

This is very similar to environment variables, but you are reading secrets from a text file. I still find environment variables more flexible for things like deployment tools and continuous integration servers. If you decide to use a configuration file, Python supports several formats in the standard library, such as JSON , INI , netrc, and XML . You can also find external packages such as PyYAML and TOML . Personally, I think JSON and YAML are the easiest to use, and YAML lets you comment.

Three things to consider with configuration files:

  1. Where is the file? A default location is possible, for example ~/.my_app , and a command-line option to use a different location.
  2. Make sure that other users cannot read the file.
  3. Obviously, do not commit the configuration file in the source code. You might want to commit a template that users can copy to their home directory.

Option 4: Python module

Some projects simply put their secrets directly into the Python module.

 # settings.py db_server = 'dbhost1' db_user = 'my_app' db_password = 'correcthorsebatterystaple' 

Then import this module to get the values.

 # my_app.py from settings import db_server, db_user, db_password db_connect(db_server, db_user, db_password) 

One project that uses this technique is Django . Obviously, you should not commit settings.py to the version control system, although you might want to commit a file called settings_template.py that users can copy and modify.

I see several problems with this technique:

  1. Developers may accidentally transfer a file to a version control system. Adding it to .gitignore reduces this risk.
  2. Part of your code is not under source control. If you are disciplined and enter only lines and numbers here, this will not be a problem. If you start writing log filter classes here, stop!

If your project already uses this technique, it's easy to switch to environment variables. Just move all the settings to environment variables and change the Python module to read from these environment variables.

+2
Oct 28 '18 at 0:00
source share
Operating Systems

often support user data support. in the case of windows, it looks like http://msdn.microsoft.com/en-us/library/aa380261.aspx

you can call win32 apis from python using http://vermeulen.ca/python-win32api.html

as I understand it, this will save the data so that it can be accessed only from the account used to store it. if you want to edit the data, you can do this by writing code to extract, modify, and save the value.

+1
Aug 10 '11 at 17:38
source share

I used Cryptography because I had problems installing (compiling) other frequently mentioned libraries on my system, (Win7 x64, Python 3.5)

 from cryptography.fernet import Fernet key = Fernet.generate_key() cipher_suite = Fernet(key) cipher_text = cipher_suite.encrypt(b"password = scarybunny") plain_text = cipher_suite.decrypt(cipher_text) 

My script runs in a physically secure system / room. I encrypt the credentials using the "encrypter script" in the configuration file. And then decrypt when I need to use them. "Encrypter script" is not in the real system, only the encrypted configuration file is used. Someone who analyzes the code can easily break the encryption by analyzing the code, but you can compile it into EXE if necessary.

+1
Jan 27 '17 at 7:49 on
source share



All Articles