Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I need to securely store a username and password in Python, what are my options? [closed]

I'm writing a small Python script which will periodically pull information from a 3rd party service using a username and password combo. I don't need to create something that is 100% bulletproof (does 100% even exist?), but I would like to involve a good measure of security so at the very least it would take a long time for someone to break it.

This script won't have a GUI and will be run periodically by cron, so entering a password each time it's run to decrypt things won't really work, and I'll have to store the username and password in either an encrypted file or encrypted in a SQLite database, which would be preferable as I'll be using SQLite anyway, and I might need to edit the password at some point. In addition, I'll probably be wrapping the whole program in an EXE, as it's exclusively for Windows at this point.

How can I securely store the username and password combo to be used periodically via a cron job?

like image 452
Naftuli Kay Avatar asked Aug 10 '11 17:08

Naftuli Kay


People also ask

Is it safe to store password in session variable?

Yes. You can choose to store your derived key in the session knowing it might be compromised if the server is compromised, but at least the users's password is still safe. That way your security failure doesn't become a much bigger problem for users who use the same password elsewhere.

Can you password protect a python file?

Then we call encrypt() function to encrypt it. Replace 'password' with the password you would like to use for encryption. You can also encrypt the input file as-is without creating a separate password-protected file. By default, it uses 128-bit encryption.


2 Answers

The python keyring library integrates with the CryptProtectData API on Windows (along with relevant API's on Mac and Linux) which encrypts data with the user's logon credentials.

Simple usage:

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 

Usage if you want to store the username on the keyring:

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 get your info 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)   

Items are encrypted with the user's operating system credentials, thus other applications running in your user account would be able to access the password.

To obscure that vulnerability a bit you could encrypt/obfuscate the password in some manner before storing it on the keyring. Of course, anyone who was targeting your script would just be able to look at the source and figure out how to unencrypt/unobfuscate the password, but you'd at least prevent some application vacuuming up all passwords in the vault and getting yours as well.

like image 176
Dustin Wyatt Avatar answered Oct 21 '22 21:10

Dustin Wyatt


There are a few options for storing passwords and other secrets that a Python program needs to use, particularly a program that needs to run in the background where it can't just ask the user to type in the password.

Problems to avoid:

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

Option 1: SSH

This isn't always an option, but it's probably the best. Your private key is never transmitted over the network, SSH just runs mathematical calculations to prove that you have the right key.

In order to make it work, you need the following:

  • The database or whatever you are accessing needs to be accessible by SSH. Try searching for "SSH" plus whatever service you are accessing. For example, "ssh postgresql". If this isn't a feature on your database, move on to the next option.
  • Create an account to run the service that will make calls to the database, and generate an SSH key.
  • Either add the public key to the service you're going to call, or create a local account on that server, and install the public key there.

Option 2: Environment Variables

This one is the simplest, so it might be a good place to start. It's described well in the Twelve Factor App. The basic idea is that your source code just pulls the password or other secrets from environment variables, and then you configure those environment variables on each system where you run the program. It might also be a nice touch if you use default values that will work for most developers. You have to balance that against making your software "secure by default".

Here's an example that pulls the server, user name, 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) 

Look up how to set environment variables in your operating system, and consider running the service under its own account. That way you don't have sensitive data in environment variables when you run programs in your own account. When you do set up those environment variables, take extra care that other users can't read them. Check file permissions, for example. Of course any users with root permission will be able to read them, but that can't be helped. If you're using systemd, look at the service unit, and be careful to use EnvironmentFile instead of Environment for any secrets. Environment values can be viewed by any user with systemctl show.

Option 3: Configuration Files

This is very similar to the environment variables, but you read the secrets from a text file. I still find the 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, like JSON, INI, netrc, and XML. You can also find external packages like PyYAML and TOML. Personally, I find JSON and YAML the simplest to use, and YAML allows comments.

Three things to consider with configuration files:

  1. Where is the file? Maybe a default location like ~/.my_app, and a command-line option to use a different location.
  2. Make sure other users can't read the file.
  3. Obviously, don't commit the configuration file to source code. You might want to commit a template that users can copy to their home directory.

Option 4: Python Module

Some projects just put their secrets right into a Python module.

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

Then import that 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 shouldn't commit settings.py to source control, although you might want to commit a file called settings_template.py that users can copy and modify.

I see a few problems with this technique:

  1. Developers might accidentally commit the file to source control. Adding it to .gitignore reduces that risk.
  2. Some of your code is not under source control. If you're disciplined and only put strings and numbers in here, that won't be a problem. If you start writing logging filter classes in here, stop!

If your project already uses this technique, it's easy to transition to environment variables. Just move all the setting values to environment variables, and change the Python module to read from those environment variables.

like image 26
Don Kirkby Avatar answered Oct 21 '22 23:10

Don Kirkby