cifrar campos para protegerlos en el repositorio

This commit is contained in:
f 2020-08-10 20:40:12 -03:00
parent 9a2f242e0d
commit c0d0e52df5
11 changed files with 92 additions and 3 deletions

View file

@ -56,6 +56,7 @@ gem 'inline_svg'
gem 'jekyll'
gem 'jekyll-data', require: 'jekyll-data',
git: 'https://0xacab.org/sutty/jekyll/jekyll-data.git'
gem 'lockbox'
gem 'mini_magick'
gem 'mobility'
gem 'pg'

View file

@ -262,6 +262,7 @@ GEM
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
lockbox (0.4.6)
lograge (0.11.2)
actionpack (>= 4)
activesupport (>= 4)
@ -540,6 +541,7 @@ DEPENDENCIES
jekyll-data!
letter_opener
listen (>= 3.0.5, < 3.2)
lockbox
lograge
memory_profiler
mini_magick

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
# Íbamos a usar OpenSSL pero esto es más simple de implementar y no
# tenemos que usar cifrado compatible con JavaScript.
class MetadataEncryptedText < MetadataText
# Decifra el valor si está guardado en el sitio
def value
self[:value] ||= if (v = document.data.dig(name.to_s))
box.decrypt_str v
else
default_value
end
rescue Lockbox::DecryptionError => e
ExceptionNotifier.notify_exception(e)
self[:value] ||= I18n.t('lockbox.help.decryption_error')
end
# Cifra el valor antes de guardarlo
def save
self[:value] = box.encrypt sanitize(value)
true
end
private
# Genera una lockbox a partir de la llave privada del sitio
#
# @return [Lockbox]
def box
@box ||= Lockbox.new key: site.private_key, padding: true, encode: true
end
end

View file

@ -7,6 +7,12 @@ class Site < ApplicationRecord
include Site::Forms
include Site::FindAndReplace
# Cifrar la llave privada que cifra y decifra campos ocultos. Sutty
# tiene acceso pero los datos se guardan cifrados en el sitio. Esto
# protege información privada en repositorios públicos, pero no la
# protege de acceso al panel de Sutty!
encrypts :private_key
# TODO: Hacer que los diferentes tipos de deploy se auto registren
# @see app/services/site_service.rb
DEPLOYS = %i[local www zip hidden_service].freeze

View file

@ -0,0 +1,7 @@
%tr{ id: attribute }
%th
%abbr{ title: t('lockbox.help.description') }
&#128274;
%span.sr-only= t('lockbox.help.title')
= post_label_t(attribute, post: post)
%td{ dir: dir, lang: locale }= metadata.value

View file

@ -0,0 +1,11 @@
.form-group
= label_tag "post_#{attribute}" do
%abbr{ title: t('lockbox.help.description') }
&#128274;
%span.sr-only= t('lockbox.help.title')
= post_label_t(attribute, post: post)
= text_area_tag "post[#{attribute}]", metadata.value,
dir: dir, lang: locale,
**field_options(attribute, metadata)
= render 'posts/attribute_feedback',
post: post, attribute: attribute, metadata: metadata

View file

@ -0,0 +1 @@
Lockbox.master_key = Rails.application.credentials.lockbox_master_key

View file

@ -514,3 +514,8 @@ en:
title: '404: Page not found :('
description: "You're reading this message because the page you wanted doesn't exist."
button: 'Back to panel'
lockbox:
help:
title: Encrypted content
description: The field contents are encrypted before being stored and won't be available on the public website or its source code. You can save private information here and it will only be readable to this site's users through Sutty's panel.
decryption_error: There was an error trying to decrypt the content, Sutty's team has been notified!

View file

@ -527,3 +527,8 @@ es:
title: '404: Página no encontrada :('
description: 'Estás leyendo este error porque la página que quisiste acceder no existe.'
button: 'Volver al panel'
lockbox:
help:
title: Contenido cifrado
description: El contenido de este campo se guarda cifrado y no estará disponible en el sitio ni en su código fuente. Puedes guardar información privada aquí y sólo estará disponible para quienes tengan acceso a ese sitio en el panel de Sutty.
decryption_error: Hubo un error al decifrar la información, ¡el equipo de Sutty ya fue notificado!

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
# Agrega las llaves privadas cifradas a cada sitio
class AddPrivKeyToSites < ActiveRecord::Migration[6.0]
def up
add_column :sites, :private_key_ciphertext, :string
Site.find_each do |site|
site.update_attribute :private_key, Lockbox.generate_key
end
end
def down
remove_column :sites, :private_key_ciphertext
end
end

View file

@ -12,8 +12,8 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20_200_616_133_218) do
# Could not dump table "access_logs" because of following StandardError
ActiveRecord::Schema.define(version: 20_200_810_230_944) do
# Could not dump table 'access_logs' because of following StandardError
# Unknown type '' for column 'id'
create_table 'action_text_rich_texts', force: :cascade do |t|
@ -58,7 +58,7 @@ ActiveRecord::Schema.define(version: 20_200_616_133_218) do
t.index ['deploy_id'], name: 'index_build_stats_on_deploy_id'
end
# Could not dump table "csp_reports" because of following StandardError
# Could not dump table 'csp_reports' because of following StandardError
# Unknown type 'uuid' for column 'id'
create_table 'deploys', force: :cascade do |t|
@ -157,6 +157,7 @@ ActiveRecord::Schema.define(version: 20_200_616_133_218) do
t.string 'title'
t.boolean 'colaboracion_anonima', default: false
t.boolean 'contact', default: false
t.string 'private_key_ciphertext'
t.index ['design_id'], name: 'index_sites_on_design_id'
t.index ['licencia_id'], name: 'index_sites_on_licencia_id'
t.index ['name'], name: 'index_sites_on_name', unique: true