How not to continue passwords in Python scripts

  • Tutorial

Storing passwords has always been a headache. In the classic version, you have a user who is trying very hard not to forget the terribly secret "qwerty123" and the information system that stores the hash from this password. A good system also carefully solves hashes in order to poison the life of bad people who can steal a database with hashed passwords. It's all clear. We store some passwords in the head, and we put some passwords in an encrypted form in keepass.

Everything changes when we remove from the scheme a person who diligently enters the key from a piece of paper. When two information systems interact, on the client side, in any case, the password must be stored in a form open to the system, so that it can be transferred and compared with the reference hash. And at this stage, the admins usually open the local branch of the bicycle plant and begin to carefully hide, obfuscate and bury the secret key in the script code. Many of these options are not just useless, but also dangerous. I will try to offer a convenient and safe solution to this problem for python. And a little touch powershell.

How not to do

Everyone knows the concept of a "temporary scriptika". That's literally just the data quickly parse from the database and delete. And then suddenly it turns out that the script has already migrated somewhere in the production from the dev-zone. And here unpleasant surprises from the initial “disposability” begin to emerge.

The most common variant in the style:

db_login = 'john.doe'
password = 'password!'

The problem is that here the password glows in the clear and can be easily detected among the deposits of old scripts by automatic search. A slightly more complicated option goes along the path of security through obscurity, with storing the password in an encrypted form right in the code. In this case, decoding back must be performed immediately, otherwise the client will not be able to present this password to the server side. This method will save the most from a random look, but any serious manual analysis of the code will allow you to easily remove the secret key. The code below will save only from such “shoulder surfers”:

>>> import base64
>>> print base64.b64encode("password")
>>> print base64.b64decode("cGFzc3dvcmQ=")

The most annoying scenario is the use of version control systems, such as git, for such files with sensitive information. Even if the author decides to clear all passwords, they will remain in the history of the repository. In fact, if you file a secret file in git, you can automatically consider it compromised and immediately begin the procedure for replacing all the affected credentials.

Using System Storage

There is the coolest keyring library . The basic principle of operation is based on the fact that each user of the OS has its own encrypted storage, access to which is possible only after the user logs into the system. It is cross-platform and will use the backend for storing passwords provided by the operating system:

  • KDE4 & KDE5 KWallet (dbus required)
  • Freedesktop Secret Service - many DEs, including GNOME (secretstorage is required)
  • Windows Credential Locker
  • macOS Keychain

Alternatively, you can use alternative backends or write your own if absolutely something strange is required.

Compare the complexity of the attack

When storing the password directly in the script you need :

  1. Steal the code itself (easy)
  2. De-fuss if necessary (easy)

When using a local keyring, an attacker needs to:

  1. Steal the code itself (easy)
  2. De-fuss if necessary (easy)
  3. To compromise a local machine by logging in under the attacked user (difficult)

Theoretically, any local program running on behalf of the current user can access the local storage if they know the parameters for accessing the secret password. However, this is not a problem, since in the event of an account being compromised, the attacker will be able to intercept all sensitive data. Other users and their software will not have access to the local keystore.

Usage example

import argparse
import getpass
import keyring
    parser = argparse.ArgumentParser()
    parser.add_argument("-n", "--newpass", required=False, help="Set new password", action="store_true")
    arguments = parser.parse_args()
    return arguments
deffake_db_connection():# Функция, имитирующая подключение к базе данных или что-то подобное
    db_name = 'very_important_db'
    db_host = ''
    passwd = keyring.get_password(systemname, username)
    print('Connecting to db: {}'.format(db_name))
    print('Using very secret password from vault: {}'.format(passwd))
    print('Doing something important...')
    print('Erasing the database...')
    print('Task completed')
# Объявляем дефолтные переменные
systemname = 'important_database'
username = 'meklon'
args = parse_arguments()
# Записываем в хранилище пароль, если активирован параметр --newpassif args.newpass:
    # Безопасно запрашиваем ввод пароля в CLI
    password = getpass.getpass(prompt="Enter secret password:")
    # Пишем полученный пароль в хранилище ключейtry:
        keyring.set_password(systemname, username, password)
    except Exception as error:
        print('Error: {}'.format(error))
# Подключаемся к базе с помощью пароля из системного хранилища

Secure password entry

Another common leakage option for secret passwords is the command line history. Using standard input is not allowed here:

age = input("What is your age? ")
print"Your age is: ", age
What is your age? 100
Your age is:  100
type 'int'>

In the example above, I already mentioned the getpass library :

# Безопасно запрашиваем ввод пароля в CLI
password = getpass.getpass(prompt="Enter secret password:")

Entering data when using it is similar to the classic * nix approach when entering the system. No data is written to any system logs or displayed on the screen.

Something about Powershell

For Powershell, the correct option is to use a regular Windows Credential Locker.
This is implemented by the CredentialManager module .

Example of use:

Install-Module CredentialManager -force
New-StoredCredential -Target $url -Username $ENV:Username -Pass ....
Get-StoredCredential -Target .... 

Also popular now: