Ocultando uma senha em um script python (apenas ofuscação insegura)


127

Eu tenho um script python que está criando uma conexão ODBC. A conexão ODBC é gerada com uma cadeia de conexão. Nesta cadeia de conexão, tenho que incluir o nome de usuário e a senha para esta conexão.

Existe uma maneira fácil de ocultar essa senha no arquivo (apenas para que ninguém possa ler a senha quando estou editando o arquivo)?


16
Lembre-se de que os usuários que executam esse arquivo terão pelo menos acesso de leitura e podem facilmente pegar as senhas. Se você só pode ler o livro e você está preocupado com as pessoas que o veem por cima do ombro, mas seja avisado enquanto o observador comum não consegue memorizar as coisas com rapidez suficiente para obter uma senha, qualquer pessoa com acesso ao script e um um pouco de conhecimento técnico e uma pequena quantidade de ambição poderão capturar suas senhas. Sempre pense na segurança com muito cuidado, é importante.
usar o seguinte código

Respostas:


117

A codificação Base64 está na biblioteca padrão e servirá para parar os surfistas:

>>> import base64
>>>  print(base64.b64encode("password".encode("utf-8")))
cGFzc3dvcmQ=
>>> print(base64.b64decode("cGFzc3dvcmQ=").decode("utf-8"))
password

5
Concordo. A senha codificada em base64 parece muito mais misteriosa.
Ed Haber

4
Mas não ajuda o fato de que o script deve ser legível pelo usuário que está executando e a senha não.
266 Martin

79
Não acho que isso base64seja mais ofuscante do que rot13neste contexto. Pelo contrário, base64tem suas características típicas (sinal de igual, ...) e, portanto, é mais fácil de detectar do que outras abordagens. Qualquer ofuscação não tem benefício prático, no entanto. Muito ruim que essa resposta seja altamente cotada. Ele apenas dá uma falsa sensação de segurança ...
schlamar

12
Se você estiver gravando a senha para que possa ser usada pelo script, qualquer pessoa com acesso ao script poderá obter a senha, independentemente do método de criptografia usado. O requisito aqui era apenas ocultar a senha de alguém que estivesse olhando o script enquanto ele estava aberto. Nesse caso, base64é preferível rot13que seja na biblioteca padrão do Python.
Dave Webb

17
base64 NÃO é criptografia. é ofuscação na melhor das hipóteses.
precisa saber é

52

O Douglas F Shearer's é a solução geralmente aprovada no Unix quando você precisa especificar uma senha para um login remoto.
Você adiciona uma opção --password-from-file para especificar o caminho e ler texto sem formatação de um arquivo.
O arquivo pode estar na área do usuário protegida pelo sistema operacional. Ele também permite que diferentes usuários escolham automaticamente seu próprio arquivo.

Para senhas que o usuário do script não tem permissão de conhecer - você pode executar o script com permissão reduzida e ter o arquivo de senha de propriedade desse usuário root / admin.


1
Como exatamente você executa o script com permissões elevadas sem fornecer uma senha root ou de administrador? Está relacionado à configuração de bits UID?
usar o seguinte código

4
Não importa, eu descobri. Para qualquer outra pessoa que se importe: Se um script tiver um bit setuid, o SO passará o bit setuid para o intérprete. Infelizmente, existem enormes falhas de segurança, de modo que a maioria das distribuições modernas desativa o setuid para scripts.
Youarefunny

Não consigo encontrar nenhuma informação na opção --password-from-file. Você tem algum exemplo? Obrigado!
Pythonface

@pyramidface - Eu quis dizer que você codificar um recurso como este e adicionar a capacidade de ler um passwd de um arquivo
Martin Beckett

@MartinBeckett, mas como Youarefunny disse, você teria que aumentar o setuid em python para dar ao script root acesso ao arquivo de senhas?
Pythonface

51

Aqui está um método simples:

  1. Crie um módulo python - vamos chamá-lo de peekaboo.py.
  2. Em peekaboo.py, inclua a senha e qualquer código que precise dessa senha
  3. Crie uma versão compilada - peekaboo.pyc - importando este módulo (via linha de comando python, etc ...).
  4. Agora, exclua peekaboo.py.
  5. Agora você pode importar felizmente o peekaboo, contando apenas com o peekaboo.pyc. Como o peekaboo.pyc é compilado em bytes, não é legível para o usuário casual.

Isso deve ser um pouco mais seguro que a decodificação base64 - embora seja vulnerável a um descompilador py_to_pyc.


2
Isso ainda tem algumas falhas, mas na verdade é muito próximo do que eu quero. Isso permitirá que eu demonstre scripts python que incluem conexões de usuário / senha sem revelar a senha na tela ou sem digitá-la no prompt de comando. Depois de importar peekaboo import peekaboa senha está disponível como peekaboo.password(se contido peekaboo.py password='secret')
Dannid

8
Se você quiser levar essa idéia um passo adiante, poderá usar o Cython para compilar qualquer arquivo .py no C e gerar um binário específico da plataforma (por exemplo: .pyd para windows, .so para macOS, etc) ... Por cythonizingseu script e compartilhando o binário gerado, você obterá o benefício desta resposta + adicionará outra camada de ofuscação, porque agora você tem o código C descompilado para obter a senha. Isso não é 100% seguro, mas será necessário muito trabalho para acessar os dados confidenciais que você deseja ocultar.
Fnord

cythonizing, perfeito
Arno

29

Se você estiver trabalhando em um sistema Unix, aproveite o módulo netrc na biblioteca padrão do Python. Ele lê senhas de um arquivo de texto separado (.netrc), cujo formato é descrito aqui .

Aqui está um pequeno exemplo de uso:

import netrc

# Define which host in the .netrc file to use
HOST = 'mailcluster.loopia.se'

# Read from the .netrc file in your home directory
secrets = netrc.netrc()
username, account, password = secrets.authenticators( HOST )

print username, password

19

A melhor solução, supondo que o nome de usuário e a senha não possam ser fornecidos em tempo de execução pelo usuário, é provavelmente um arquivo de origem separado contendo apenas a inicialização variável do nome de usuário e senha importados para o código principal. Esse arquivo precisaria ser editado apenas quando as credenciais forem alteradas. Caso contrário, se você estiver preocupado apenas com os surfistas com memória média, a codificação da base 64 é provavelmente a solução mais fácil. O ROT13 é muito fácil de decodificar manualmente, não diferencia maiúsculas de minúsculas e mantém muito significado em seu estado criptografado. Codifique sua senha e ID do usuário fora do script python. Ele decodificou o script em tempo de execução para uso.

Dar credenciais de scripts para tarefas automatizadas é sempre uma proposta arriscada. Seu script deve ter suas próprias credenciais e a conta que ele usa não deve ter acesso além do que é exatamente necessário. Pelo menos a senha deve ser longa e bastante aleatória.


Resposta muito boa - obrigado. Para os pequenos scripts que estou escrevendo (que são scripts de manutenção de qualquer maneira - a codificação BASE64 será suficiente)
bernhardrusch

2
Parece bom, mas você pode dar um exemplo de implementação? No momento, é apenas uma descrição de uma prática geral e não é tão útil para alguém que nunca fez isso antes.
Dannid 21/10

18

Que tal importar o nome de usuário e a senha de um arquivo externo ao script? Dessa forma, mesmo que alguém se apossasse do script, não obteria a senha automaticamente.


15

base64 é o caminho a percorrer para suas necessidades simples. Não há necessidade de importar nada:

>>> 'your string'.encode('base64')
'eW91ciBzdHJpbmc=\n'
>>> _.decode('base64')
'your string'

2
O que exatamente é bobo ?! A resposta inteira ou a parte não importada?
Tzot 01/10/08

18
Base64 apenas adiciona a ilusão de segurança.
FlySwat 01/10/08

13
Jonathan, parece que você não leu a pergunta. É sobre obscuridade (e muito temporária), não sobre segurança , por isso não entendo por que você considera minha resposta não útil.
Tzot 01/10/08

1
Eu não sabia que você poderia fazer isso em vez de precisar usar o módulo base64. E há uma série de codificações também como zlib também ... diversão :)
Kiv

1
@Dennis Usar o módulo base64 é a maneira preferida atualmente. O último não funciona mais nas versões mais recentes do Python.
Jeyekomon

5

para ofuscação python3 usando base64é feito de maneira diferente:

import base64
base64.b64encode(b'PasswordStringAsStreamOfBytes')

o que resulta em

b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM='

observe a representação informal da string, a string real está entre aspas

e decodificando de volta para a string original

base64.b64decode(b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM=')
b'PasswordStringAsStreamOfBytes'

para usar este resultado onde objetos de string são necessários, o objeto de bytes pode ser convertido

repr = base64.b64decode(b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM=')
secret = repr.decode('utf-8')
print(secret)

para obter mais informações sobre como python3 lida com bytes (e seqüências de caracteres de acordo), consulte a documentação oficial .


4

Este é um problema bastante comum. Normalmente, o melhor que você pode fazer é

A) crie algum tipo de função de codificação ceasar para codificar / decodificar (apenas não rot13) ou

B) o método preferido é usar uma chave de criptografia, ao alcance do seu programa, codificar / decodificar a senha. Na qual você pode usar a proteção de arquivos para proteger o acesso à chave.

Nesse sentido, se o aplicativo for executado como um serviço / daemon (como um servidor da web), você poderá colocar sua chave em um keystore protegido por senha com a entrada de senha como parte da inicialização do serviço. Um administrador precisará reiniciar o aplicativo, mas você terá uma boa pretensão para as senhas de configuração.


2

Seu sistema operacional provavelmente fornece recursos para criptografar dados com segurança. Por exemplo, no Windows, há DPAPI (API de proteção de dados). Por que não pedir suas credenciais ao usuário na primeira vez em que você executa e depois esquivá-las criptografadas para execuções subsequentes?


2

Aproximação mais caseira do que converter autenticação / senhas / nome de usuário em detalhes criptografados. FTPLIB é apenas o exemplo. " pass.csv " é o nome do arquivo csv

Salve a senha no CSV como abaixo:

nome do usuário

senha do usuário

(Sem cabeçalho da coluna)

Lendo o CSV e salvando-o em uma lista.

Usando os elementos da lista como detalhes da autenticação.

Código completo.

import os
import ftplib
import csv 
cred_detail = []
os.chdir("Folder where the csv file is stored")
for row in csv.reader(open("pass.csv","rb")):       
        cred_detail.append(row)
ftp = ftplib.FTP('server_name',cred_detail[0][0],cred_detail[1][0])

2

Aqui está o meu trecho para tal coisa. Você basicamente importa ou copia a função para o seu código. O getCredentials criará o arquivo criptografado se ele não existir e retornará uma descrição, e o updateCredential será atualizado.

import os

def getCredentials():
    import base64

    splitter='<PC+,DFS/-SHQ.R'
    directory='C:\\PCT'

    if not os.path.exists(directory):
        os.makedirs(directory)

    try:
        with open(directory+'\\Credentials.txt', 'r') as file:
            cred = file.read()
            file.close()
    except:
        print('I could not file the credentials file. \nSo I dont keep asking you for your email and password everytime you run me, I will be saving an encrypted file at {}.\n'.format(directory))

        lanid = base64.b64encode(bytes(input('   LanID: '), encoding='utf-8')).decode('utf-8')  
        email = base64.b64encode(bytes(input('   eMail: '), encoding='utf-8')).decode('utf-8')
        password = base64.b64encode(bytes(input('   PassW: '), encoding='utf-8')).decode('utf-8')
        cred = lanid+splitter+email+splitter+password
        with open(directory+'\\Credentials.txt','w+') as file:
            file.write(cred)
            file.close()

    return {'lanid':base64.b64decode(bytes(cred.split(splitter)[0], encoding='utf-8')).decode('utf-8'),
            'email':base64.b64decode(bytes(cred.split(splitter)[1], encoding='utf-8')).decode('utf-8'),
            'password':base64.b64decode(bytes(cred.split(splitter)[2], encoding='utf-8')).decode('utf-8')}

def updateCredentials():
    import base64

    splitter='<PC+,DFS/-SHQ.R'
    directory='C:\\PCT'

    if not os.path.exists(directory):
        os.makedirs(directory)

    print('I will be saving an encrypted file at {}.\n'.format(directory))

    lanid = base64.b64encode(bytes(input('   LanID: '), encoding='utf-8')).decode('utf-8')  
    email = base64.b64encode(bytes(input('   eMail: '), encoding='utf-8')).decode('utf-8')
    password = base64.b64encode(bytes(input('   PassW: '), encoding='utf-8')).decode('utf-8')
    cred = lanid+splitter+email+splitter+password
    with open(directory+'\\Credentials.txt','w+') as file:
        file.write(cred)
        file.close()

cred = getCredentials()

updateCredentials()

1

Coloque as informações de configuração em um arquivo de configuração criptografado. Consulte essas informações no seu código usando uma chave. Coloque essa chave em um arquivo separado por ambiente e não a armazene com seu código.


1

Você conhece pit?

https://pypi.python.org/pypi/pit (somente py2 (versão 0.3))

https://github.com/yoshiori/pit (funcionará no py3 (versão atual 0.4))

test.py

from pit import Pit

config = Pit.get('section-name', {'require': {
    'username': 'DEFAULT STRING',
    'password': 'DEFAULT STRING',
    }})
print(config)

Corre:

$ python test.py
{'password': 'my-password', 'username': 'my-name'}

~ / .pit / default.yml:

section-name:
  password: my-password
  username: my-name

3
Pit não possui nenhuma documentação
successhawk

Como @successhawk observou - não vejo QUALQUER documentação nesses links do github / pypi para "pit" - mas a descrição acima é clara - e no geral eu gosto dessa solução para "ocultar" credenciais de vista fácil ...
netdesignate

Estou relutante em usar um módulo que não é mantido e recebo erros quando tento usá-lo conforme as instruções:/usr/lib/python3.7/site-packages/pit.py:93: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details. return yaml.load(open(Pit._config))
netdesignate 18/06

1

Se estiver executando no Windows, você pode considerar o uso da biblioteca win32crypt. Ele permite o armazenamento e a recuperação de dados protegidos (chaves, senhas) pelo usuário que está executando o script, portanto, as senhas nunca são armazenadas em texto não criptografado ou no formato ofuscado no seu código. Não tenho certeza se existe uma implementação equivalente para outras plataformas, portanto, com o uso estrito do win32crypt, seu código não é portátil.

Acredito que o módulo pode ser obtido aqui: http://timgolden.me.uk/pywin32-docs/win32crypt.html


1

Uma maneira de fazer isso é a seguinte:

No shell python:

>>> from cryptography.fernet import Fernet
>>> key = Fernet.generate_key()
>>> print(key)
b'B8XBLJDiroM3N2nCBuUlzPL06AmfV4XkPJ5OKsPZbC4='
>>> cipher = Fernet(key)
>>> password = "thepassword".encode('utf-8')
>>> token = cipher.encrypt(password)
>>> print(token)
b'gAAAAABe_TUP82q1zMR9SZw1LpawRLHjgNLdUOmW31RApwASzeo4qWSZ52ZBYpSrb1kUeXNFoX0tyhe7kWuudNs2Iy7vUwaY7Q=='

Em seguida, crie um módulo com o seguinte código:

from cryptography.fernet import Fernet

# you store the key and the token
key = b'B8XBLJDiroM3N2nCBuUlzPL06AmfV4XkPJ5OKsPZbC4='
token = b'gAAAAABe_TUP82q1zMR9SZw1LpawRLHjgNLdUOmW31RApwASzeo4qWSZ52ZBYpSrb1kUeXNFoX0tyhe7kWuudNs2Iy7vUwaY7Q=='

# create a cipher and decrypt when you need your password
cipher = Fernet(key)

mypassword = cipher.decrypt(token).decode('utf-8')

Depois de fazer isso, você pode importar minha senha diretamente ou importar o token e a cifra para descriptografar conforme necessário.

Obviamente, existem algumas falhas nessa abordagem. Se alguém tiver o token e a chave (como teriam se tivesse o script), poderá descriptografar facilmente. No entanto, ele ofusca e, se você compilar o código (com algo como Nuitka), pelo menos sua senha não aparecerá como texto sem formatação em um editor hexadecimal.


0

Isso não responde exatamente à sua pergunta, mas está relacionado. Eu adicionaria como comentário, mas não era permitido. Estou lidando com esse mesmo problema e decidimos expor o script para os usuários que usam Jenkins. Isso nos permite armazenar as credenciais do banco de dados em um arquivo separado, criptografado e protegido em um servidor e não acessível a não administradores. Também nos permite um atalho para criar uma interface do usuário e limitar a execução.


0

Você também pode considerar a possibilidade de armazenar a senha fora do script e fornecê-la em tempo de execução

por exemplo, fred.py

import os
username = 'fred'
password = os.environ.get('PASSWORD', '')
print(username, password)

que pode ser executado como

$ PASSWORD=password123 python fred.py
fred password123

Camadas extras de "segurança através da obscuridade" podem ser obtidas usando base64 (como sugerido acima), usando nomes menos óbvios no código e distanciando ainda mais a senha real do código.

Se o código estiver em um repositório, geralmente é útil armazenar segredos fora dele , para que você possa adicioná-lo a ~/.bashrc(ou a um cofre ou a um script de inicialização, ...)

export SURNAME=cGFzc3dvcmQxMjM=

e mude fred.pypara

import os
import base64
name = 'fred'
surname = base64.b64decode(os.environ.get('SURNAME', '')).decode('utf-8')
print(name, surname)

depois faça login novamente e

$ python fred.py
fred password123

0

Por que não ter um xor simples?

Vantagens:

  • parece dados binários
  • ninguém pode lê-lo sem conhecer a chave (mesmo que seja um único caractere)

Chego ao ponto em que reconheço simples strings b64 para palavras comuns e rot13 também. Xor tornaria muito mais difícil.



-1

Existem vários utilitários ROT13 escritos em Python na 'Net - apenas o google para eles. O ROT13 codifica a string offline, copia-a na fonte, decodifica no ponto de transmissão.

Mas isso é realmente uma proteção fraca ...


por favor inclua um link ou código de exemplo para tornar esta resposta mais útil
Micah Stubbs
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.