📜 ⬆️ ⬇️

How not to continue passwords in Python scripts



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 lives 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 a key from a piece of paper. When two information systems interact, on the client side, in any case, the password should be stored in a form open for 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 it suddenly turns out that the script had already migrated from the dev-zone somewhere into production. And then 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 the password stored in an encrypted form right in the code. In this case, the decryption 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") cGFzc3dvcmQ= >>> print base64.b64decode("cGFzc3dvcmQ=") password 

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 with 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:


You can also 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, local storage can be accessed by any local program running on behalf of the current user, if it knows 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 def parse_arguments(): parser = argparse.ArgumentParser() parser.add_argument("-n", "--newpass", required=False, help="Set new password", action="store_true") arguments = parser.parse_args() return arguments def fake_db_connection(): # ,       -  db_name = 'very_important_db' db_host = '147.237.0.71' 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() #    ,    --newpass if 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)) #          fake_db_connection() 

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 type(age) >>output 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.

Usage example:

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

Source: https://habr.com/ru/post/435652/


All Articles