Steganografia em Python Puro: O Algoritmo Que Esconde Uma Mensagem Secreta Dentro de Uma Imagem Sem Que Ninguem Perceba (Sem Bibliotecas Externas)
Você já imaginou enviar uma mensagem secreta para alguém usando uma foto aparentemente normal? Não estou falando de criptografia — estou falando de algo muito mais sutil: steganografia.
Enquanto a criptografia transforma sua mensagem em um emaranhado ilegível (que grita “tenho algo a esconder!”), a steganografia faz exatamente o oposto: ela esconde a própria existência da mensagem. Para qualquer observador, é apenas uma foto do pôr do sol. Mas para quem tem a chave certa, é uma confissão, um contrato, ou talvez a senha do cofre.
E a melhor parte? Você não precisa de nenhuma biblioteca externa para fazer isso. Com Python puro e cerca de 200 linhas de código, vou te mostrar como esconder qualquer texto dentro de uma imagem PNG — e como extraí-lo de volta.
O Problema Real: Quando Criptografia Não É Suficiente
Vamos ser honestos: criptografia resolve 90% dos problemas de comunicação segura. Mas e os outros 10%?
Imagine este cenário: você precisa enviar uma informação sensível através de um canal monitorado. Se você usar criptografia, o monitor vê dados embaralhados e imediatamente pensa: “algo suspeito está acontecendo aqui”. Mesmo que ele não consiga decifrar, a presença de criptografia já é um alerta vermelho.
Agora imagine o mesmo cenário, mas você envia uma foto inocente do seu gato. O monitor vê apenas uma foto. Nenhum padrão suspeito, nenhum dado cifrado, nenhum motivo para investigação. Mas dentro dessa foto, escondida nos pixels, está sua mensagem completa.
Isso é steganografia: a arte de esconder informação à plena vista.
Como Funciona: Bits Menos Significativos (LSB)
O conceito é elegantemente simples. Cada pixel em uma imagem RGB tem três canais: vermelho, verde e azul. Cada canal é representado por um número de 0 a 255, que em binário são 8 bits.
Aqui está o pulo do gato: o bit menos significativo (o último bit) de cada canal tem um impacto mínimo na cor final. Mudar esse bit de 0 para 1 altera a intensidade da cor em apenas 1/255 — uma diferença imperceptível ao olho humano.
Por exemplo:
- Vermelho 150 em binário:
10010110 - Se mudarmos o último bit para 1:
10010111= 151 - A diferença visual entre RGB(150, G, B) e RGB(151, G, B) é invisível
Então, para esconder uma mensagem:
- Convertemos o texto em binário (sequência de 0s e 1s)
- Substituímos o bit menos significativo de cada canal pelos bits da mensagem
- Salvamos a imagem modificada
Para extrair:
- Lemos o bit menos significativo de cada canal
- Reconstruímos a sequência binária
- Convertemos de volta para texto
Simples, não? Vamos ao código.
Implementação: Escondendo Mensagens em Imagens
Primeiro, precisamos de uma forma de converter texto para binário e vice-versa:
def texto_para_binario(texto):
"""Converte uma string em uma sequência de bits"""
binario = ''
for caractere in texto:
binario += format(ord(caractere), '08b')
return binario
def binario_para_texto(binario):
"""Converte uma sequência de bits de volta para string"""
texto = ''
for i in range(0, len(binario), 8):
byte = binario[i:i+8]
if len(byte) == 8:
caractere = chr(int(byte, 2))
texto += caractere
return texto
A Classe Principal: Steganografia LSB
Agora, o coração do sistema. Vamos trabalhar diretamente com os pixels da imagem:
from PIL import Image
class SteganografiaLSB:
DELIMITADOR = '<<FIM>>'
@staticmethod
def esconder(imagem_path, mensagem, saida_path):
"""Esconde uma mensagem dentro de uma imagem"""
img = Image.open(imagem_path).convert('RGB')
largura, altura = img.size
# Adiciona delimitador e converte para bits
msg_completa = mensagem + SteganografiaLSB.DELIMITADOR
bits = texto_para_binario(msg_completa)
# Verifica capacidade
capacidade = largura * altura * 3
if len(bits) > capacidade:
raise ValueError(
f"Mensagem muito longa. "
f"Capacidade: {capacidade} bits, "
f"Necessário: {len(bits)} bits"
)
# Insere bits nos pixels
pixels = list(img.getdata())
novos_pixels = []
idx = 0
for r, g, b in pixels:
novo_r = (r & ~1) | int(bits[idx]) if idx < len(bits) else r
idx += 1
novo_g = (g & ~1) | int(bits[idx]) if idx < len(bits) else g
idx += 1
novo_b = (b & ~1) | int(bits[idx]) if idx < len(bits) else b
idx += 1
novos_pixels.append((novo_r, novo_g, novo_b))
# Salva imagem modificada
img_nova = Image.new('RGB', (largura, altura))
img_nova.putdata(novos_pixels)
img_nova.save(saida_path, 'PNG')
return len(bits)
@staticmethod
def extrair(imagem_path):
"""Extrai uma mensagem escondida de uma imagem"""
img = Image.open(imagem_path).convert('RGB')
pixels = list(img.getdata())
# Extrai bits de todos os pixels
bits = ''
for r, g, b in pixels:
bits += str(r & 1)
bits += str(g & 1)
bits += str(b & 1)
# Converte bits para texto
texto = binario_para_texto(bits)
# Procura delimitador
fim = texto.find(SteganografiaLSB.DELIMITADOR)
if fim == -1:
raise ValueError("Nenhuma mensagem encontrada")
return texto[:fim]
Exemplo Prático: Escondendo e Extraindo
# Esconde uma mensagem
msg = "Encontre-me no café da esquina às 15h. Leve o pacote."
bits_usados = SteganografiaLSB.esconder(
'foto_original.png', msg, 'foto_secreta.png'
)
print(f"Mensagem escondida! {bits_usados} bits usados.")
# Extrai a mensagem
recuperada = SteganografiaLSB.extrair('foto_secreta.png')
print(f"Recuperada: {recuperada}")
# Saída: "Encontre-me no café da esquina às 15h. Leve o pacote."
A imagem original e a imagem com mensagem são visualmente idênticas. Mas a segunda carrega informações ocultas nos pixels.
Capacidade: Quanto Cabe em Uma Imagem?
Vamos fazer as contas para diferentes resoluções:
def calcular_capacidade(largura, altura, pct_uso=0.15):
"""Calcula quantos caracteres cabem na imagem"""
bits_totais = largura * altura * 3
bits_uteis = int(bits_totais * pct_uso)
caracteres = bits_uteis // 8
return {
'bits_totais': bits_totais,
'caracteres': caracteres,
'kb': bits_uteis // 8 // 1024
}
# Full HD (1920x1080)
print(calcular_capacidade(1920, 1080))
# {'bits_totais': 6220800, 'caracteres': 116640, 'kb': 113}
# 4K (3840x2160)
print(calcular_capacidade(3840, 2160))
# {'bits_totais': 24883200, 'caracteres': 466560, 'kb': 455}
Uma foto Full HD carrega até 113 KB de texto sem degradação perceptível. Suficiente para um livro inteiro.
Camada Extra: Criptografia + Steganografia
Steganografia sozinha não é criptografia. Se alguém descobrir a mensagem, lê tudo. A solução? Combinar as duas técnicas:
import hashlib
def cifra_xor(texto, chave):
"""Cifra XOR com chave derivada via SHA-256"""
chave_bytes = hashlib.sha256(chave.encode()).digest()
texto_bytes = texto.encode('utf-8')
cifrado = bytearray()
for i, byte in enumerate(texto_bytes):
cifrado.append(byte ^ chave_bytes[i % len(chave_bytes)])
return bytes(cifrado).hex()
def decifra_xor(hex_cifrado, chave):
"""Decifra texto cifrado com XOR"""
chave_bytes = hashlib.sha256(chave.encode()).digest()
cifrado = bytes.fromhex(hex_cifrado)
decifrado = bytearray()
for i, byte in enumerate(cifrado):
decifrado.append(byte ^ chave_bytes[i % len(chave_bytes)])
return decifrado.decode('utf-8')
# Fluxo completo: cifra -> esconde -> extrai -> decifra
mensagem = "Coordenadas: -23.5505, -46.6333"
chave = "senha_ultra_secreta"
# Esconde cifrada
cifrada = cifra_xor(mensagem, chave)
SteganografiaLSB.esconder('foto.png', cifrada, 'foto_secret.png')
# Extrai e decifra
extraida = SteganografiaLSB.extrair('foto_secret.png')
final = decifra_xor(extraida, chave)
print(final) # "Coordenadas: -23.5505, -46.6333"
Agora são duas camadas de proteção: primeiro, ninguém sabe que existe uma mensagem. Segundo, mesmo que descubram, não conseguem ler sem a chave.
Steganálise: Como Detectar Mensagens Escondidas
Nem tudo são flores. Existem técnicas para detectar steganografia. A mais simples analisa a distribuição dos bits LSB:
def analisar_lsb(imagem_path):
"""Detecta anomalias na distribuição de bits LSB"""
img = Image.open(imagem_path).convert('RGB')
pixels = list(img.getdata())
contagem = [0, 0] # [zeros, uns]
for r, g, b in pixels:
contagem[r & 1] += 1
contagem[g & 1] += 1
contagem[b & 1] += 1
total = sum(contagem)
proporcao_zeros = contagem[0] / total
desvio = abs(proporcao_zeros - 0.5)
return {
'zeros_pct': round(proporcao_zeros * 100, 2),
'uns_pct': round((1 - proporcao_zeros) * 100, 2),
'desvio': round(desvio * 100, 2),
'suspeita': desvio > 0.05
}
# Teste
resultado = analisar_lsb('foto_secreta.png')
if resultado['suspeita']:
print(f"⚠️ Suspeita! Desvio: {resultado['desvio']}%")
else:
print("✅ Distribuição normal")
Em imagens naturais, esperamos uma distribuição ~50/50 de zeros e uns nos LSBs. Steganografia pode criar desvios detectáveis — especialmente se a mensagem for grande.
Limitações Importantes
Antes de sair escondendo segredos por aí, algumas ressalvas:
1. Formato importa:
- PNG: Lossless — perfeito para steganografia
- JPEG: Compressão lossy destrói os bits escondidos
- BMP: Funciona, mas arquivos são enormes
2. Redes sociais destroem: Instagram, WhatsApp e Facebook recomprimem imagens automaticamente. Sua mensagem é apagada no processo. Steganografia LSB funciona apenas em transferência direta de arquivos.
3. Não é à prova de forensic: Steganálise avançada (chi-square, RS analysis) pode detectar mensagens. Use sempre com criptografia.
4. Tamanho do arquivo: Imagens modificadas podem ter tamanhos ligeiramente diferentes das originais — mais um sinal para analistas.
Aplicações Práticas Legítimas
Steganografia não é só espionagem. Usos do mundo real:
- Watermarking invisível: Artistas assinam digitalmente suas obras dentro dos pixels
- Controle de versão: Embed de metadados em imagens de documentação técnica
- Autenticação: Tokens de verificação dentro de imagens de perfil
- Backup discreto: Senhas ou chaves de recuperação escondidas em fotos pessoais
- Comunicação em regimes opressivos: Quando até criptografia é monitorada
O Script Completo Para Copiar e Usar
#!/usr/bin/env python3
"""
steg.py - Steganografia LSB em Python
Uso:
python steg.py esconder foto.png "mensagem secreta" saida.png
python steg.py extrair saida.png
"""
import sys
from PIL import Image
DELIMITADOR = '<<FIM>>'
def texto_para_binario(texto):
return ''.join(format(ord(c), '08b') for c in texto)
def binario_para_texto(bits):
texto = ''
for i in range(0, len(bits) - 7, 8):
texto += chr(int(bits[i:i+8], 2))
return texto
def esconder(img_path, mensagem, saida_path):
img = Image.open(img_path).convert('RGB')
w, h = img.size
bits = texto_para_binario(mensagem + DELIMITADOR)
if len(bits) > w * h * 3:
print(f"Erro: mensagem muito longa ({len(bits)} bits)")
sys.exit(1)
pixels = list(img.getdata())
novos = []
i = 0
for r, g, b in pixels:
nr = (r & ~1) | int(bits[i]) if i < len(bits) else r; i += 1
ng = (g & ~1) | int(bits[i]) if i < len(bits) else g; i += 1
nb = (b & ~1) | int(bits[i]) if i < len(bits) else b; i += 1
novos.append((nr, ng, nb))
out = Image.new('RGB', (w, h))
out.putdata(novos)
out.save(saida_path, 'PNG')
print(f"✅ Mensagem escondida em {saida_path}")
def extrair(img_path):
img = Image.open(img_path).convert('RGB')
bits = ''
for r, g, b in img.getdata():
bits += f'{r&1}{g&1}{b&1}'
texto = binario_para_texto(bits)
fim = texto.find(DELIMITADOR)
if fim == -1:
print("❌ Nenhuma mensagem encontrada")
sys.exit(1)
print(texto[:fim])
if __name__ == '__main__':
if len(sys.argv) < 3:
print("Uso: python steg.py [esconder|extrair] arquivo [msg] [saida]")
sys.exit(1)
cmd = sys.argv[1]
if cmd == 'esconder' and len(sys.argv) == 5:
esconder(sys.argv[2], sys.argv[3], sys.argv[4])
elif cmd == 'extrair' and len(sys.argv) == 3:
extrair(sys.argv[2])
else:
print("Uso incorreto.")
Salve como steg.py, instale pip install Pillow, e pronto. Duas linhas de comando para esconder e extrair qualquer mensagem.
Conclusão
Steganografia é a arte da invisibilidade. Enquanto a criptografia protege o conteúdo, a steganografia protege a existência. Combinadas, formam uma das camadas de privacidade mais elegantes que existem — e você pode implementar tudo em Python puro, sem depender de nada além da Pillow.
Lembre-se: a melhor segurança é aquela que ninguém sabe que existe.
—
Você já precisou esconder informação à plena vista? Tem algum caso real de steganografia que te marcou? Conta aqui nos comentários — prometo não extrair suas mensagens secretas. 🔍
