Em um dia calmo e tranquilo, você está trabalhando no seu projeto, organizando as coisas, desorganizando as coisas… então este e-mail aparece na caixa de entrada de contato:
De: notthatdhh@example.com
Para: contact@yourcompany.com
Cc: veradikt@somelawfirm.com
Assunto: Tô foraOlá, aqui é o DHH (não aquele que você está pensando, eu sou Dustin Harper Holloway), e minha advogada Vera Dikt que está em cópia neste e-mail. Me enviem tudo o que vocês têm sobre mim. Também deletem minha conta. vlwflw
Você saberia o que fazer? Ou começaria a entrar em pânico pensando:
- Quais tabelas armazenam as informações privadas deste usuário?
- Podemos removê-las dos logs também?
- Ai não, e os payloads do ActiveJob?
- Quanto tempo nós temos para responder?!
- Estamos quebrando alguma lei? Seremos multados?
- Estamos prestes a declarar falência?
⚠️ Aviso importante ⚠️
Eu não sou advogado. Esta é uma perspectiva de engenharia sobre como cumprir pragmaticamente as leis de proteção de dados usando Rails. Se você precisar de aconselhamento jurídico sobre conformidade legal, consulte um advogado.
O que são leis de proteção de dados?
Depois de se sentir sobrecarregado por um momento, você recupera o foco e inicia sua pesquisa.
Você descobre que as leis de proteção de dados existem para impor a proteção de dados e a privacidade do usuário. Elas estão presentes em mais de 140 países, e aqui está a parte que te pega de surpresa: a maioria delas não se aplica apenas se você estiver sediado no país. Elas também se aplicam se você tiver usuários lá.
Elas tratam de consentimento, minimização de dados, direitos de acesso, notificação de violação e limitação de finalidade.
E "dados pessoais" é um conceito muito mais amplo do que você espera. Inclui:
- Identificadores pessoais básicos: Nome, e-mail, telefone, documento de identidade, data de nascimento
- Informações pessoais: Raça, religião, identidade de gênero, visões/afiliação política
- Informações médicas: Prescrições, diagnósticos, resultados de exames, deficiências, informações de seguro saúde
- Registros financeiros e oficiais: Registros fiscais, salário
- Dados comportamentais e inferidos: Perfil de comportamento, previsões geradas por IA
- Atividade digital e identificadores técnicos: Endereço IP, histórico de navegação, mensagens de chat, histórico de localização
E mais. Se pode ser usado para identificar uma pessoa, direta ou indiretamente, provavelmente está incluído.
Bem, caro leitor, se relacionar-se com este cenário fez você suar, tenho ótimas notícias: a solução está logo adiante!
Por que as empresas não conseguem cumprir as leis de privacidade?
Alguns anos atrás, o suporte à privacidade costumava ser uma vantagem competitiva, mas agora é a base para um produto. E as empresas ainda não conseguem cumprir. Por quê?
A resposta é: elas tratam a privacidade apenas como uma questão jurídica e colocam a inovação acima da privacidade do usuário.
Então vamos começar com isso em mente:
Não aposte com os dados do usuário para pagar pela inovação
Privacidade projetada
Quando você prioriza a privacidade do seu usuário e a trata como uma restrição normal do seu processo de desenvolvimento (assim como já fazemos para segurança, desempenho e responsividade móvel), caminhamos para o que chamamos de privacy by design.
O privacy by design começa com alguns princípios:
- Minimização de dados. Não colete o que você não pode proteger, ou o que você não precisa. Menos dados, menos preocupações.
- Privado por padrão. Cada exposição de dados precisa de um motivo explícito. Os dados devem ser protegidos com medidas técnicas apropriadas.
- Transparência. Os usuários devem saber e controlar o que você armazena sobre eles.
Lembre-se: os dados pertencem ao usuário, você não pode usá-los livremente apenas porque estão armazenados no seu banco de dados.
Ok, vamos falar sobre como o Rails pode ser usado para seguir esses princípios.
Minimização de dados
A ideia é simples. Se você não tem os dados, não precisa se preocupar com eles. Isso se aplica tanto aos dados que entram no seu app quanto aos que saem dele.
Minimize os dados que entram no seu app
Use strong parameters
Eu sei, isso parece óbvio. Mas o ponto não é apenas "não faça atribuição em massa". O ponto é a intencionalidade. Escreva uma lista explicitamente permitida e permita apenas o que você realmente precisa, não permita coisas "só porque você pode precisar". Você sempre pode alterar o código depois se algo mais for necessário.
def registration_params
params.require(:user).permit(
:email_address, :password, :password_confirmation,
:first_name, :last_name, :phone
)
endFiltrar no ponto de entrada abrange todo o aplicativo. É por isso que vale a pena ser cuidadoso aqui.
Anonimize os IPs dos usuários
Endereços de IP são dados pessoais sob a maioria das leis de proteção de dados. Se você não precisa do IP completo (e a maioria dos apps não precisa), anonimize-o no nível do middleware usando a gem ip_anonymizer:
config.middleware.insert_after ActionDispatch::RemoteIp, IpAnonymizer::MaskIpImportante: se a geolocalização for importante para o seu app, ela ainda funciona com IPs anonimizados! Talvez você perca um pouco de precisão, mas a maioria dos apps não precisa desse nível de precisão de qualquer maneira.
Novamente, um ponto de entrada, app inteiro coberto.
Minimize os dados que saem do seu app
Use serialização explícita
Não dependa de to_json ou as_json
padrões que descarregam registros inteiros. Escreva um serializer que liste exatamente o que deve ser exposto: “`ruby
class OrderSerializer
def initialize(order)
@order = order
end
def as_json(*)
{
id: @order.id,
total_cents: @order.total_cents,
status: @order.status,
items: @order.order_items.map do |item|
item.slice(:product_id, :quantity, :price_cents)
end,
shipping_address: {
street: @order.address.street,
city: @order.address.city,
state: @order.address.state,
zip_code: @order.address.zip_code
}
}
end
end
Se alguém adicionar uma coluna `social_security_number` à tabela de pedidos na próxima semana, ela não vazará silenciosamente para as suas respostas de API ou requisições de terceiros.
#### Minimize os payloads de jobs
Cada argumento que você passa para um job de segundo plano é serializado no seu backend de fila (Redis, banco de dados, onde quer que seja), exibido em dashboards de jobs como Sidekiq Web e registrado no enfileiramento e na repetição. Passe dados pessoais e você acabará de vazá-los para todo um novo conjunto de lugares com os quais agora precisa se preocupar.
A regra é simples: passe **apenas IDs**, nunca os dados em si. Deixe que o job busque novamente o que precisar. Como bônus, você também evita agir sobre dados obsoletos quando o job for executado minutos ou horas depois de ter sido enfileirado.
```ruby
# Don't
WelcomeEmailJob.perform_later(
user.email_address,
user.first_name,
user.last_name
)
# Do
WelcomeEmailJob.perform_later(user.id)
class WelcomeEmailJob < ApplicationJob
def perform(user_id)
user = User.find(user_id)
UserMailer.welcome(user).deliver_now
end
endProteja suas estratégias de rastreamento de erros
Serviços de rastreamento de erros capturam todo o contexto da requisição: parâmetros, cabeçalhos, IPs e, às vezes, corpos de requisição. PIIs acabam no seu rastreador de erros sem que você perceba, e então você não tem uma maneira adequada de excluí-los após a solicitação do usuário.
Primeiro, certifique-se de que sua configuração de `filter_parameters` esteja completa. Outra coisa boa sobre a configuração `filter_parameters` é que ela também se aplica ao método `#inspect` do ActiveRecord, portanto, não apenas objetos com chaves listadas aqui serão filtrados do log de requisições, mas também quando você registrar um modelo.
Rails.application.config.filter_parameters += [
:passw, :email, :secret, :token, :_key,
:crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc,
# App-specific PII:
:first_name, :last_name, :phone, :date_of_birth
]Depois, use a gem `logstop` para limpar PIIs de mensagens de erro antes que elas cheguem ao seu serviço de rastreamento:
Logstop.scrub(error)Anonimize dados de analytics
É aqui que os chamados "inovadores" ficam bravos, então direi novamente: a inovação não deve vir às custas da privacidade dos seus usuários. Se o seu negócio depende de manter ou compartilhar os dados dos seus usuários sem o consentimento deles, talvez você deva reconsiderá-lo. Sim, eu sei, a análise de dados é importante, e a questão é: você pode tê-la sem prejudicar a privacidade dos seus usuários.
Use um serviço de analytics focado em privacidade e certifique-se de que os scripts de rastreamento dependam de consentimento. Anonimize qualquer informação pessoal enviada para o analytics.
Não há exemplo de código nesta seção, é uma questão de princípio. Veremos modelagem de consentimento e técnicas de anonimização em breve.
Mantenha dados pessoais fora de e-mails
Dados podem vazar através de logs de e-mail e do tratamento de e-mails devolvidos. Mantenha as informações pessoais em e-mails no mínimo possível e filtre os logs de e-mail. Precisa enviar um e-mail para o usuário? Não mencione o nome dele nele, torne necessário fazer login para ter acesso a esse tipo de informação.
Além disso, você deve usar URLs assinadas com expiração automática e downloads autenticados em vez de incorporar dados do usuário diretamente. E aqui está algo que você deve saber: o Rails suporta isso nativamente!
# In the mailer
@download_url = user_file_download_url(
token: Rails.application.message_verifier("some_file")
.generate(user.id, expires_in: 30.minutes)
)# In the controller
user_id = Rails.application.message_verifier("some_file")
.verify(params[:token])Políticas de retenção de dados e TTL
Não guarde dados que você não precisa mais. Configure políticas de retenção automáticas que anonimizem ou excluam usuários inativos, limpem sessões expiradas e apaguem registros de tarefas concluídas.
Um exemplo de tarefa de retenção:
class DataRetentionJob < ApplicationJob
RETENTION_PERIOD = 3.years
def perform
cutoff = RETENTION_PERIOD.ago
User.customer
.not_anonymized
.where(updated_at: ...cutoff)
.find_each do |user|
# Don't delete users that have pending matters
next if user.orders.where(status: [:pending, :confirmed, :shipped]).exists?
user.addresses.not_anonymized.find_each(&:anonymize!)
user.anonymize!
end
end
endAutomatize isso, não faça apenas quando um usuário solicitar. Se depender de alguém lembrar de executar, não acontecerá.
Privado por padrão
O segundo princípio é sobre tornar os dados protegidos por padrão, sem esforço extra do desenvolvedor. Esforço extra deve ser necessário apenas quando você precisar expor algo privado.
Logs
Logs são o vazamento silencioso número um de dados pessoais. Às vezes você implementa todas as outras estratégias para manter as informações privadas e ainda assim as vaza por meio de logs.
Verifique tudo que é explicitamente registrado. E para todo o restante, proteja seu logger com a gem `logstop`:
Logstop.guard(Rails.logger)Isso filtra automaticamente padrões comuns de PII (e-mails, IPs, números de cartão de crédito e assim por diante) da saída do seu log.
Encryption at rest
O Rails suporta nativamente três níveis de proteção de dados para atributos de modelo. Escolha com base no padrão de uso do campo:
| --- | ||||||||
|---|---|---|---|---|---|---|---|---|
| `encrypts :first_name` | Sim | Não | Sim | |||||
| `encrypts :email_address, deterministic: true, downcase: true` | Sim | Sim | Sim | |||||
| `has_secure_password` | Não | Não | Apenas verificar |
A criptografia não determinística é para campos que você só precisa exibir, como nomes ou números de telefone. A criptografia determinística é para campos que você precisa consultar. Você precisa buscar usuários por e-mail, então o e-mail vai ali. O hashing (`has_secure_password`) é para campos que você só precisa verificar, nunca ler de volta.
Force SSL
Criptografia em repouso é apenas metade da história. Se seu app aceita uma única requisição via HTTP simples em produção, cookies de sessão e envios de formulários viajam abertamente, e qualquer coisa no caminho entre o usuário e seu servidor pode lê-los. Todo app Rails em produção deve forçar HTTPS.
O Rails torna isso uma linha única:
# config/environments/production.rb
config.force_ssl = trueEssa flag faz três coisas ao mesmo tempo: redireciona qualquer requisição HTTP para HTTPS, define o cabeçalho `Strict-Transport-Security` para que os navegadores recusem HTTP simples em visitas futuras, e marca seus cookies como `secure` para que eles nunca saiam do navegador por uma conexão não criptografada.
Se você quiser ir além e enviar seu domínio para a lista de preload do HSTS, faça isso de forma intencional - o preload é persistente e difícil de desfazer:
config.ssl_options = {
hsts: {
expires: 1.year,
subdomains: true,
preload: true
}
}Não ative o `preload` até ter certeza de que cada subdomínio consegue servir HTTPS. Uma vez que um navegador tenha seu domínio com preload, não existe a saída de "apenas usar HTTP por um minuto".
Proteja os cookies adequadamente
Seja intencional com os cookies. Defina políticas de `same_site` adequadas e escolha o nível certo de proteção:
cookies.signed_permanent[:app_session_id] = {
value: session_id,
httponly: true,
same_site: :strict # or :lax
}
cookies.encrypted[:contact_draft] = {
value: {
name: "Hugh Zerr",
email: "hughzerr@example.com",
message: "Hello, here's..."
}.to_json,
httponly: true,
same_site: :strict, # or :lax
expires: 30.minutes.from_now
}Use `signed` para dados que não devem ser alterados. Use `encrypted` para dados que não devem ser lidos pelo cliente de forma alguma.
Não trate a política de `same_site` como as pessoas fazem com o CORS, onde tentam adivinhar qual opção fará o navegador reclamar. Seja intencional.
Torne seus backups seguros
Sempre criptografe seus backups de banco de dados e aplique a eles uma política de retenção automática. Não faz sentido criptografar seu banco de dados de produção se backups antigos e não criptografados estiverem em um bucket em algum lugar.
Proteja o acesso direto ao console em produção
Quando alguém abre um console Rails em produção, essa pessoa tem acesso direto a todos os dados dos seus usuários. Registre quem o acessou, por que e quais comandos foram executados. As gems `console1984` e `audits1984` fazem um bom trabalho nisso.

Quando você abre o console, é recebido com uma mensagem clara: "Você tem acesso a dados de produção aqui. Isso é algo sério. Como parte da nossa promessa de manter os dados dos clientes seguros e privados, auditamos os comandos que você digita aqui." Ele até pede que você explique por que está usando o console antes de começar.
Transparência e direitos de dados
O terceiro princípio trata do conhecimento e controle dos usuários sobre o que você armazena sobre eles. É aqui que as coisas ficam mais complexas, e também onde o Rails oferece muito material para trabalhar.
Modelagem de consentimento
Apenas um banner de cookies não conta como consentimento. Pense em tudo o que você faz com os dados do usuário: processamento de pedidos, marketing, rastreamento de analytics, compartilhamento com terceiros e recursos específicos. Cada um desses precisa de seu próprio consentimento.
Cada consentimento deve ser por finalidade, versionado e armazenado com prova. Os consentimentos também devem ser explícitos. Sem consentimento do usuário, sem ação.
class Consent < ApplicationRecord
PURPOSES = %w[
order_processing
marketing_analytics
third_party_sharing
].freeze
belongs_to :user
encrypts :ip_address
enum :status, { granted: 0, revoked: 1 }
validates :purpose, presence: true, inclusion: { in: PURPOSES }
validates :status, presence: true
endNote que criptografamos o endereço IP no próprio registro de consentimento; ele funciona como prova. Além disso, note que os consentimentos são armazenados como uma trilha de auditoria imutável e apenas de adição. A revogação cria um novo registro. Nada é excluído.
Em seguida, aplique o consentimento no nível do controller:
before_action -> { require_consent!("order_processing") }, only: :createSem consentimento, sem ação.

Do lado do usuário, você pode criar uma página de gerenciamento de consentimento que mostre cada finalidade, seu status atual, o que ela controla e uma trilha de auditoria completa de quando os consentimentos foram concedidos ou revogados.
Direitos de acesso do titular dos dados
Lembra daquele e-mail do Dustin no início? Os usuários têm direitos de acesso, retificação e exclusão sobre seus dados. E existem prazos. A LGPD (Brasil) exige uma resposta em 15 dias. O GDPR (UE) exige uma resposta em 30 dias, prorrogável para 90. Parece tempo suficiente, mas você não quer ter que lembrar de tudo toda vez que um usuário fizer uma solicitação.
Modele isso explicitamente:
class DataSubjectRequest < ApplicationRecord
belongs_to :user
enum :request_type, { access: 0, rectification: 1, erasure: 2 }
enum :status, {
pending: 0,
approved: 1,
processing: 2,
completed: 3,
rejected: 4
}
validates :request_type, :status, presence: true
scope :pending_review, -> { where(status: :pending) }
def approve!
update!(status: :approved)
ProcessDataSubjectRequestJob.perform_later(id)
end
def reject!(reason:)
update!(status: :rejected, notes: reason)
end
endTorne isso o mais automático possível. Quando uma solicitação é aprovada, um job de background entra em ação e cuida do trabalho pesado automaticamente.
Implementando o tratamento de solicitações de acesso (DSAR)
Para solicitações de acesso, você precisa exportar cada dado que possui sobre um usuário. Comece com um concern reutilizável que declare quais campos são exportáveis:
module DataExportable
extend ActiveSupport::Concern
class_methods do
def exportable(*fields)
@exportable_fields = fields
end
def exportable_fields
@exportable_fields || column_names.map(&:to_sym)
end
end
def export_data
self.class.exportable_fields.each_with_object({}) do |field, hash|
hash[field] = public_send(field)
end
end
endDepois, declare os campos exportáveis em cada model:
class User < ApplicationRecord
# ...
include DataExportable
exportable :uuid,
:email_address,
:first_name,
:last_name,
:phone,
:date_of_birth
endAgora você tem uma maneira padronizada de obter todos os dados que devem ser enviados ao usuário, caso ele os solicite.
class DataExportSerializer
def initialize(user)
@user = user
end
def as_json(*)
{
profile: @user.export_data,
addresses: @user.addresses.map(&:export_data),
orders: @user.orders.includes(:order_items).map(&:export_data),
consents: @user.consents.map(&:export_data),
analytics: {
account_created_at: @user.created_at,
total_orders: @user.orders.count,
total_spent_cents: @user.orders.sum(:total_cents)
}
}
end
endA saída fica assim:
{
"profile": {
"uuid": "a1b2c3d4-e5f6-...",
"email_address": "hugh@example.com",
"first_name": "Hugh",
"last_name": "Zerr",
"phone": "+5511999990000",
"date_of_birth": "1998-05-14"
},
"addresses": [
{
"label": "Home",
"street": "Av Brasil, 500",
"city": "São Paulo",
"state": "SP",
"zip_code": "01430-000",
"country": "BR"
}
],
"orders": [
{
"number": "ORD-4A8F2B",
"status": "delivered",
"total_cents": 15000,
"created_at": "2025-11-20"
}
]
}Implementando a manipulação de solicitações de exclusão
Para solicitações de exclusão, o job anonimiza todos os registros associados e revoga consentimentos ativos:
class ProcessDataSubjectRequestJob < ApplicationJob
# ...
def process_erasure(request)
user = request.user
# Anonymize addresses
user.addresses.not_anonymized.find_each(&:anonymize!)
# Anonymize user profile
user.anonymize!
# Revoke all active consents
user.consents.where(status: :granted).find_each do |consent|
user.revoke_consent(consent.purpose)
end
end
endImplementando a anonimização
A anonimização em si também é um concern reutilizável:
module Anonymizable
extend ActiveSupport::Concern
class_methods do
def anonymizable(*fields)
@anonymizable_fields = fields
end
def anonymizable_fields
@anonymizable_fields || []
end
end
# scopes and #anonymized? omitted
def anonymize!
return if anonymized?
transaction do
self.class.anonymizable_fields.each do |field|
public_send(:"#{field}=", "[ANONYMIZED]")
end
self.anonymized_at = Time.current
save!(validate: false)
end
end
endclass User < ApplicationRecord
# ...
include Anonymizable
anonymizable :first_name,
:last_name,
:phone,
:date_of_birth
endApós a anonimização, aquela mesma exportação fica assim:
{
"profile": {
"uuid": "a1b2c3d4-e5f6-...",
"email_address": "[ANONYMIZED]",
"first_name": "[ANONYMIZED]",
"last_name": "[ANONYMIZED]",
"phone": "[ANONYMIZED]",
"date_of_birth": "[ANONYMIZED]"
},
"addresses": [
{
"label": "Home",
"street": "[ANONYMIZED]",
"city": "[ANONYMIZED]",
"state": "[ANONYMIZED]",
"zip_code": "[ANONYMIZED]",
"country": "BR"
}
],
"orders": [
{
"number": "ORD-4A8F2B",
"status": "delivered",
"total_cents": 15000,
"created_at": "2025-11-20"
}
]
}Os dados pessoais sumiram, mas os dados de negócio (totais de pedidos, status, datas) permanecem. Esse é o ponto da anonimização em vez da exclusão: você mantém seus analytics e a trilha de auditoria sem manter os dados pessoais.
Ok, eu reconheço que isso é muita coisa para fazer. Deve haver uma maneira de tornar isso automático, certo?
Automatize com agent skills
Se você está olhando para um app Rails existente e se perguntando por onde começar, você não está sozinho. A maioria das equipes está na mesma situação. E estamos na era da IA, então vamos usá-la.
Construímos um conjunto de agent skills de código aberto que trazem o privacy-by-design para o seu workflow do Rails. Elas funcionam em dois modos:
- Avaliação completa da base de código. Escaneia toda a sua aplicação e gera um relatório detalhado com descobertas, classificações de severidade e recomendações priorizadas.
- Revisão de alterações recentes. Verifica arquivos não commitados ou alterados recentemente em busca de violações de regras de privacidade antes que você faça o commit.
Ambos os modos geram um relatório detalhado e oferecem correções. As skills são totalmente open source e vêm com scripts Ruby para facilitar a auditoria, para que você possa inspecionar exatamente o que elas verificam.
Instale-as com:
# knowledge base skill
npx skills add codeminer42/skills --skill privacy-by-design-rails
# command skills
npx skills add codeminer42/skills --skill privacy-assessment-rails
npx skills add codeminer42/skills --skill privacy-review-railsQuando você executa a avaliação completa, recebe um relatório que começa com um resumo executivo:

Depois, as descobertas por severidade. Cada uma vem com sua localização no código, uma descrição do problema, o trecho de código relevante e uma correção recomendada:

Um resumo de checklist com uma visão geral de aprovado/reprovado de cada verificação de privacidade:

E recomendações priorizadas, organizadas por severidade: Crítica (resolver imediatamente), Alta (resolver logo) e Média (planejar e implementar):

As skills estão em github.com/codeminer42/skills.
Wrapping up
Leis de privacidade de dados não matam a inovação. Não estar em conformidade é um problema de habilidade.
Respeite a privacidade dos seus usuários, o Rails ajudará você nisso.
Previous work
Este post é baseado na minha palestra no Tropical on Rails 2026. Você pode encontrar os slides aqui, e atualizarei o post assim que a gravação estiver disponível.
We want to work with you. Check out our Services page!

