Hoje, vamos discutir como aumentar a segurança do servidor utilizando o gerenciador de senhas Passwork como exemplo. Demonstrarei como reduzir a superfície de ataque e, em seguida, criaremos um script que notificará você via mensagem direta sobre ataques ao gerenciador de senhas.
Sou um defensor da ideia de que não existem sistemas absolutamente seguros; os atacantes continuam a encontrar vulnerabilidades cada vez mais sofisticadas. Adicione a isso o fator humano. Programadores, DevOps, quaisquer especialistas podem cometer erros. Portanto, do ponto de vista da proteção, a única coisa que você pode fazer é aumentar os custos para invadir seus recursos.
O Passwork é uma maneira rápida de aumentar significativamente os custos para invadir seu sistema. Ao mesmo tempo, gerenciando cofres, diretórios e senhas, você pode resolver tarefas sérias com apenas alguns cliques. Por exemplo, fornecer acesso temporário a uma equipe de desenvolvimento externa e, quando eles concluírem o projeto, revogar o acesso e gerar novas senhas.
Preparando o Passwork
Se sua empresa não possui uma licença do Passwork, a primeira pergunta é: onde obtê-la? Acesse passwork.ru e registre-se para um teste. Tenha em mente que, antes de enviar os dados de acesso, os desenvolvedores querem ter certeza de que você é um comprador real. Eles não ligarão para você, mas certamente verificarão seu domínio de e-mail. Ao solicitar uma versão de teste, você deve fornecer um e-mail corporativo, ou seja, não um Gmail, Outlook ou Mail.Ru, mas um em seu próprio domínio.
Você receberá um ambiente de teste e dados de login.
Entre na seção "Autenticação". Role até o final para o bloco "Acesso à API". Gere tokens de API.
O sistema criará três tokens com os quais seu servidor poderá obter dados do Passwork.
O sistema é construído com o princípio de API first – todo o trabalho com o Passwork é, na verdade, trabalhar com a API de diferentes maneiras. Existe a utilidade passwork-cli, existe um contêiner Docker com a utilidade pré-instalada, existe um pacote Python para desenvolver seus próprios scripts. Mas você pode escrever scripts em qualquer idioma, o principal é a capacidade de executar requisições GET e POST.
Base de API do Passwork
Vamos instalar o pacote passwork-python para Python, que é o cliente oficial do Passwork:
bashpip install passwork-python
Agora podemos criar um cliente e interagir com a API:
pythonfrom passwork_client import PassworkClient # Use os dados da etapa de geração de tokens client = PassworkClient(host="https://passwork.example.com") client.set_tokens("ACCESS_TOKEN", "REFRESH_TOKEN") client.set_master_key("MASTER_KEY")
O trabalho posterior é simplesmente chamar os métodos do client. Por exemplo, client.create_vault() para criar um novo cofre.
Info: Um cofre é um grupo de dados secretos que possui suas próprias configurações de privacidade. Você cria um cofre, especifica as permissões para ele e adiciona os dados secretos a ele.
Por exemplo, você pode ter duas equipes de DevOps e elas precisam de conjuntos diferentes de senhas e tokens. Crie vários cofres, conceda a cada equipe acesso ao cofre necessário e você obterá um excelente isolamento de dados.
Nos exemplos de código, você pode ver que create_vault() aceita dois parâmetros: o nome vault_name e o tipo de armazenamento type_id. O nome é uma string comum, mas com o tipo surgem nuances. Os tipos de cofres são outra forma de agrupamento com atribuição de permissões. Mas os identificadores só podem ser visualizados via API.
Na documentação técnica, você encontrará a descrição da API, mas vamos dar uma olhada dentro do passwork_client.py. Este import me interessou:
pythonfrom .modules.vault_type import VaultType
No arquivo, há uma função adequada: get_vault_types(). Vamos tentar extrair os dados:
pythonfrom passwork_client import PassworkClient client = PassworkClient(host="https://ruxakep.passwork-demo.space/") client.set_tokens("1KqoGku9GYYYlsxj3BcLAvUqxa5ej/9M50/akAUuRvU=", "mXjYIaJUSTCzFa+RwHowNbguv2TF3OofnW7q8Hbb9AU=") client.set_master_key("vznRsji3NiIjkuekQchhcpswkJxJNC2hmEwuEb4hFLphh1Xmxc545XO+mGCrbAlgSYJ9w1qp3ltQyuXqF91pVw==") print(client.get_vault_types())
Aqui está o que veio em resposta:
json{ "items": [ { "id": "685cfa5a3d0ba75d2f041972", "name": "Пользовательские сейфы", "code": "privateShared", "isBuiltIn": true, "creatorAccess": "admin", "administratorIds": [], "allUsersCanCreate": true, "userGroupCanCreateIds": [], "userCanCreateIds": [], "userRoleCanCreateIds": [], "administrators": [], "administratorsCount": 0, "usersAllowedToCreate": null, "creatorAccessName": "Администрирование", "isUsed": true }, { "id": "685cfa5a3d0ba75d2f041973", "name": "Корпоративные сейфы", "code": "company", "isBuiltIn": true, "creatorAccess": "admin", "administratorIds": ["68598c0a132196f6470ed522"], "allUsersCanCreate": true, "userGroupCanCreateIds": [], "userCanCreateIds": [], "userRoleCanCreateIds": [], "administrators": [ { "id": "68598c0a132196f6470ed522", "login": "admin", "fullName": "admin", "hasAvatar": false, "isDeleted": false } ], "administratorsCount": 1, "usersAllowedToCreate": null, "creatorAccessName": "Администрирование", "isUsed": false } ], "totalCount": 2 }
Agora não será difícil inserir o vault_id necessário no código e obter um script completo para adicionar um segredo ao Passwork.
pythonfrom passwork_client import PassworkClient client = PassworkClient(host="https://ruxakep.passwork-demo.space/") client.set_tokens("1KqoGku9GYYYlsxj3BcLAvUqxa5ej/9M50/akAUuRvU=", "mXjYIaJUSTCzFa+RwHowNbguv2TF3OofnW7q8Hbb9AU=") client.set_master_key("vznRsji3NiIjkuekQchhcpswkJxJNC2hmEwuEb4hFLphh1Xmxc545XO+mGCrbAlgSYJ9w1qp3ltQyuXqF91pVw==") vault_types = client.get_vault_types() private_shared = next((vt for vt in vault_types['items'] if vt['code'] == 'privateShared'), None) if not private_shared: raise ValueError("Vault type with code 'privateShared' not found") print(private_shared['id']) vault_id = client.create_vault(vault_name="Cool Vault", type_id=private_shared['id'])
Cofre criado com sucesso.
Agora temos um mecanismo completo para analisar a API do Passwork. Além disso, a maior parte do código é bem comentada e os métodos são nomeados de forma intuitiva.
Descrição do código nos comentários
Vamos adicionar uma nova senha ao cofre. A documentação contém um exemplo de objeto para criar uma senha:
jsonpassword = { "name": "Service Name", "login": "username", "password": "secure-password", "vaultId": vault_id, "folderId": folder_id, "description": "Описание", "url": "https://service-url.com", "tags": ["tag1", "tag2"], "customs": [ { "name": "Дополнительный логин", "value": "second-username", "type": "text" }, { "name": "Код восстановления", "value": "recovery-code-value", "type": "password" }, { "name": "TOTP", "value": "JBSWY3DPEHPK3PXP", "type": "totp" } ], "attachments": [ { "path": "path/to/file.png", "name": "file.png" } ] } password_id = client.create_password(password)
Pelo JSON, fica claro que a senha é uma estrutura séria com uma grande quantidade de dados. A palavra "senha" aqui não é muito adequada, "segredo" é muito mais conveniente. Por exemplo, o que impede o upload de uma chave de acesso SSH ou um certificado TLS? O upload de arquivos está presente, a criação de campos personalizados é suportada. Quase quaisquer dados que não devam ser armazenados no servidor podem ser colocados em um segredo.
Vamos criar um segredo mínimo, especificando apenas os dados obrigatórios:
pythonpassword_data = { "name": "Super Secret", "vaultId": vault_id, "password": "secure-password", } password_id = client.create_item(password_data)
Senha adicionada com sucesso.
Para obter a senha, use:
pythonsecret = client.get_item(password_id)
Agora temos tudo o que precisamos em nosso arsenal para escrever scripts poderosos e integrar o Passwork em CI/CD.
Ideias para DevOps
O repositório do Passwork contém muitos bons exemplos de scripts. Com base neles, é fácil criar um rotador de senhas ou algum outro projeto útil. Mas um script rotador é algo batido, e eu até tenho vergonha de mencioná-lo. Acho que será muito mais interessante enviar notificações do Passwork para o Telegram. Assim, você saberá sobre todos os eventos em seu Passwork.
Na biblioteca do Passwork, não encontrei uma função adequada para notificações. Terei que usar o método call, que executa uma requisição para o endpoint especificado. Isso ajudará a passar por todos os processos de criptografia e descriptografia. Você também pode usar requests, mas então terá que escrever muito código extra.
Para encontrar o endpoint, iniciei o proxy de tráfego através do Burp. Acontece que é /api/v1/notifications.
Algoritmo de trabalho do script:
- Obter todas as notificações do Passwork.
- Comparar os IDs das notificações com aqueles que já foram enviados para o Telegram.
- Se houver um novo, enviar a notificação.
- Adicionar o ID à lista de enviados e salvar em um arquivo.
Crie um bot que servirá como nosso informante. Em seguida, crie o arquivo passwork-notify.py.
Imports:
pythonimport json import sys import asyncio from datetime import datetime from passwork_client import PassworkClient from telegram import Bot
Bloco de constantes para trabalhar com Passwork e o bot do Telegram:
pythonACCESS_TOKEN = "..." REFRESH_TOKEN = "..." MASTER_KEY = "..." HOST = "https://ruxakep.passwork-demo.space/" TELEGRAM_TOKEN = "..." TELEGRAM_CHAT_ID = "..." KNOWN_IDS_FILE = "known_notification_ids.json"
Crie os clientes:
pythonpasswork = PassworkClient(HOST) passwork.set_tokens(ACCESS_TOKEN, REFRESH_TOKEN) passwork.set_master_key(MASTER_KEY) bot = Bot(token=TELEGRAM_TOKEN)
Você precisará de algumas funções de serviço para que o código fique bonito e seja mantido:
python# Carregamento de JSON com IDs de notificação já conhecidos def load_known_ids(): try: with open(KNOWN_IDS_FILE, encoding="utf-8") as f: return set(json.load(f)) except (FileNotFoundError, json.JSONDecodeError): return set() # Salvamento da lista atualizada com notificações def save_known_ids(ids_set): with open(KNOWN_IDS_FILE, "w", encoding="utf-8") as f: json.dump(list(ids_set), f, ensure_ascii=False) # Função de serviço para envio de mensagens async def send_notification(chat_id, message): await bot.send_message(chat_id=chat_id, text=message)
Mas todo o resto, como um pacote de espaguete, foi jogado em uma função:
pythonasync def main(): known = load_known_ids() updated = known.copy() try: # Aqui estará a mágica except Exception as e: print(f"Erro: {e}", file=sys.stderr) sys.exit(1)
Quando o esqueleto da função estiver pronto, adicione carne na forma de uma chamada de método call:
pythonraw = passwork.call("GET", "/api/v1/notifications") if not raw or "items" not in raw: print("Não há items na resposta", file=sys.stderr) return
Se não houver itens, saímos da função e encerramos a execução do script. Se houver itens, iniciamos um loop chato. A tarefa do loop é filtrar notificações já visualizadas e enviadas para o bot. As restantes são enviadas, adicionadas à lista de enviadas e salvas em um arquivo:
pythonsent = 0 for item in raw["items"]: nid = item.get("id") if not nid or nid in known: continue # Remova este if se você precisar de todas as notificações if item.get("isViewed", False): updated.add(nid) continue # Formando uma mensagem simples text = item.get("text", "—") placeholders = item.get("placeholders", {}) for k, v in placeholders.items(): text = text.replace(f"{{{{k}}}}", str(v)) actor = item.get("creator", {}).get("fullName", "—") event = item.get("activityLogEvent", "—") ts = item.get("createdAt") time_str = "" if not ts else f" ({datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')})" message = f"Nova notificação\nID: {nid}\nEvento: {event}\nDe: {actor}\nTexto: {text}{time_str}" # Chamada da nova função await send_notification(TELEGRAM_CHAT_ID, message) sent += 1 updated.add(nid) # Para que o trabalho do script não seja completamente chato if sent > 0: print(f"Enviado {sent} novas notificações") save_known_ids(updated) else: print("Não há novas notificações não lidas")
Resultado do trabalho do script de notificações.
Configure o script no cron, defina um tempo adequado para a execução e aproveite o controle total sobre o Passwork!
Não se interessa por todas as notificações, mas apenas por alterações? O Passwork registra todo o histórico de alterações de senhas. Em scripts, você pode obter informações sobre uma alteração específica por um método legítimo do cliente:
pythonsnapshot = passwork.get_snapshot(ITEM_ID, SNAPSHOT_ID)
Mas o problema é que você precisa saber o identificador específico da senha e do snapshot. A função é útil para enviar uma alteração específica que foi feita em uma mensagem para você. Mas para obter uma lista de snapshots, você terá que usar o método call e investigar as requisições com a ajuda do Burp.
Você pode obter uma lista de snapshots por senha através do endpoint /api/v1/items/<ITEM_ID>/snapshots.
Obtenção de snapshots por requisição.
Você obterá os itens de /api/v1/items?vaultId=<VAULT_ID>. A cadeia quase se fechou. Falta obter a lista de cofres e percorrê-los em um loop.
Para obter a lista de cofres, use o endpoint com os seguintes parâmetros:
/api/v1/vaults?includeAccessInfo=true&includeFoldersCount=true&includeIsPrivate=true&includePermissions=true
Eu extraí esta requisição do Burp. É conveniente: você não precisa percorrer os tipos de cofres se sua tarefa não exigir isso.
O loop final se parece com isto:
python# ... outro código ... vaults_raw = passwork.call("GET", "/api/v1/vaults", payload={ "includeAccessInfo": "true", "includeFoldersCount": "true", "includeIsPrivate": "true", "includePermissions": "true" }) vaults = vaults_raw.get("items", []) if isinstance(vaults_raw, dict) else vaults_raw for vault in vaults: vault_id = vault.get("id") items_raw = passwork.call("GET", f"/api/v1/items", payload={"vaultId": vault_id}) items = items_raw.get("items", []) if isinstance(items_raw, dict) else items_raw for item in items: snaps_raw = passwork.call("GET", f"/api/v1/items/{item_id}/snapshots") # ... código de processamento de snapshots ...
Por analogia com o exemplo anterior, você facilmente escreverá o código para sempre saber sobre o aparecimento de novos snapshots.
CLI
Escrever um script toda vez para testar um determinado método de API é inconveniente. Para esses fins, existe a utilidade passwork-cli. Ela é projetada para tarefas de DevOps na automação de CI/CD. Mas também é boa para se familiarizar com a estrutura da API.
Quando você instalou o pacote passwork-python, o CLI também foi instalado. Mas na documentação do Passwork, eles sugerem instalar do repositório Git Verse:
bashpip install git+ssh://git@gitverse.ru:2222/passwork-ru/passwork-python.git
Ou via HTTPS:
bashpip install git+https://gitverse.ru/passwork-ru/passwork-python.git
Info: No Windows, não se esqueça de especificar o caminho para passwork-cli. Você descobre o caminho para a ferramenta com o comando: pip show passwork-python.
Provavelmente, as versões PyPL e Git Verse são lançadas simultaneamente. Não notei nenhuma diferença e nenhuma comunicação a respeito.
Exporte o host e os tokens para variáveis. Isso é necessário para que você não precise inseri-los toda vez ao iniciar passwork-cli:
bashexport PASSWORK_HOST="https://ruxakep.passwork-demo.space/" export PASSWORK_TOKEN="..." export PASSWORK_MASTER_KEY="..."
No Windows:
powershell$env:PASSWORK_HOST="https://ruxakep.passwork-demo.space/" $env:PASSWORK_TOKEN="..." $env:PASSWORK_MASTER_KEY="..."
Se você não gosta de armazenar dados no ambiente ou tem automação específica, especifique os valores ao iniciar:
bashpasswork-cli api --host <HOST> --token <TOKEN> --refresh-token <REFRESH_TOKEN> --master-key <MASTER_KEY> ...outros parâmetros...
Encontre no código-fonte uma rota como /api/v1/users. Para o cliente, use a rota sem /api/:
bashpasswork-cli api --method GET --endpoint "v1/users"
Resultado do trabalho do cliente.
Às vezes, é necessário adicionar parâmetros. Use a opção --params e a descrição dos parâmetros em JSON. Por exemplo, você pode obter todas as senhas para um determinado tipo de cofre:
bashpasswork-cli api --method GET --endpoint "v1/items/search" --params '{"vaultIds":["69aa963c1195f2c8420a4b52"]}'
Além do modo api, o passwork-cli tem os modos get, update e exec. O último é o modo de operação mais interessante. Nele, o Passwork aceita o identificador da senha (ou vários identificadores separados por vírgula) e um comando para executar no sistema. Primeiro, a ferramenta extrairá as senhas especificadas e as despejará no ambiente, e depois executará o comando.
Descompacte as senhas e exiba as variáveis de ambiente:
bashpasswork-cli exec --password-id "..." printenv
No ambiente, as variáveis serão nomeadas como a senha é nomeada no Passwork. Se a senha for nomeada SECRET_LOGIN_ADMIN_DATABASE, após o trabalho da ferramenta, a variável SECRET_LOGIN_ADMIN_DATABASE aparecerá no ambiente. O valor da variável é a senha! No modo exec, o Passwork não verá outros valores além do nome e do valor do campo password.
Você pode obter várias variáveis em sequência não apenas listando-as, mas também especificando --folder ou --tag. Diretórios e tags também podem ser listados separadamente por vírgula.
Reduzindo a Superfície de Ataque
Para praticar, vamos instalar o Broken Crystals. É uma aplicação web vulnerável especializada para aprendizado e teste de ferramentas de pentest. Estamos interessados na vulnerabilidade de Inclusão de Arquivo Local (Local File Inclusion).
Instalação e execução:
bashgit clone https://github.com/NeuraLegion/brokencrystals docker compose --file=compose.local.yml up -d
Um hacker pode facilmente obter segredos do .env. A aplicação web é vulnerável a Local File Inclusion no endpoint http://localhost:3000/api/file/raw?path=.
Tente a seguinte requisição:
bashcurl.exe "http://localhost:3000/api/file/raw?path=.env"
Em resposta, você receberá um arquivo com muitos dados. Por exemplo: DATABASE_PASSWORD, JWT_SECRET_KEY, KEYCLOAK_ADMIN_CLIENT_SECRET, KEYCLOAK_PUBLIC_CLIENT_SECRET e assim por diante. Inaceitável! Com esses dados, um hacker tem controle quase total.
A segurança pode ser aumentada de várias maneiras, mas faremos isso no contexto do Passwork. Como exemplo, teremos um banco de dados, você pode ocultar os outros segredos por analogia. A seção do banco de dados na configuração se parece com isto:
yamlservices: db: image: 'postgres:17-alpine' restart: always healthcheck: interval: 10s retries: 10 test: ['CMD-SHELL', 'pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}'] timeout: 45s environment: POSTGRES_DB: ${DATABASE_SCHEMA} POSTGRES_USER: ${DATABASE_USER} POSTGRES_PASSWORD: ${DATABASE_PASSWORD} ports: - '5432:5432' volumes: - './pg.sql:/docker-entrypoint-initdb.d/pg.sql'
Edite o arquivo .env, remova dele DATABASE_SCHEMA, DATABASE_USER e DATABASE_PASSWORD. Mova esses dados para o Passwork. Crie um cofre. No cofre, crie uma pasta, por exemplo, bc. Na pasta, crie três segredos. No nome, coloque o nome da variável, coloque o valor na senha.
Pasta com senhas no cofre.
Certifique-se de que os dados para conexão com a API do Passwork estejam nas variáveis de ambiente, depois execute:
bashpasswork-cli exec --folder-id "69adfb16a7750e0b940be8f4" sh -c 'docker compose -f compose.local.yml up -d'
Após a execução, certifique-se de que o contêiner com o banco de dados foi iniciado:
bashdocker inspect --format='{{.State.Health.Status}}' brokencrystals-db-1
Verifique se o banco de dados está acessível:
bashpsql -h localhost -U bc -d bc -c "SELECT 1;"
O serviço iniciou claramente.
Conclusões
O Passwork é uma das ferramentas com as quais você pode aumentar não apenas a segurança, mas também o conforto. As senhas são protegidas de forma confiável e o mais longe possível do atacante. E as amplas possibilidades da API permitem aperfeiçoar infinitamente suas ferramentas de gerenciamento.
No artigo, fizemos apenas notificações, mas nada impede a criação de um painel de controle completo, através do qual em um clique será possível alterar todas as senhas, conceder ou revogar acesso.
Você também pode forçar a alteração de senhas daqueles que as têm muito fracas para seu nível de acesso. Veja o endpoint interessante /api/v1/items/security-analysis. Para ele, são necessárias duas chamadas: com a primeira você inicia a verificação, após um tempo com a mesma requisição você verá quem tem senhas fracas. Tudo o que você precisa é, dependendo da pontuação, iniciar a regeneração da senha.
Espero que o material tenha sido útil para você e você adquira mais uma ferramenta incrível para sua empresa.
Erid: Publicidade. LLC "Passwork". INN 2901311774. Erid: 2SDnjdeaXkW ← Anterior Checklist OAuth. Coletando todas as técnicas de ataque a OAuth 2.0 e OIDC para pentest de aplicações.





