Content Security Policy

This commit is contained in:
f 2020-02-06 13:11:17 -03:00
parent c8bdec242e
commit 5b20919fb3
No known key found for this signature in database
GPG key ID: 2AE5A13E321F953D
12 changed files with 133 additions and 29 deletions

View file

@ -1,7 +1,8 @@
RAILS_ENV=production RAILS_ENV=
IMAP_SERVER= IMAP_SERVER=
DEFAULT_FROM= DEFAULT_FROM=
SKEL_SUTTY=https://0xacab.org/sutty/skel.sutty.nl SKEL_SUTTY=https://0xacab.org/sutty/skel.sutty.nl
SUTTY=sutty.nl SUTTY=sutty.local
SUTTY_WITH_PORT=sutty.local:3000
REDIS_SERVER= REDIS_SERVER=
REDIS_CLIENT= REDIS_CLIENT=

View file

@ -4,9 +4,6 @@ module Api
module V1 module V1
# API # API
class BaseController < ActionController::Base class BaseController < ActionController::Base
http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'],
password: ENV['HTTP_BASIC_PASSWORD']
protect_from_forgery with: :null_session protect_from_forgery with: :null_session
respond_to :json respond_to :json
end end

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
module Api
module V1
# Recibe los reportes de Content Security Policy
class CspReportsController < BaseController
# Crea un reporte de CSP intercambiando los guiones medios por
# bajos
#
# TODO: Aplicar rate_limit
def create
csp = CspReport.new(csp_report_params.to_h.map do |k, v|
{ k.tr('-', '_') => v }
end.inject(&:merge))
csp.id = SecureRandom.uuid
csp.save
render json: {}, status: :created
end
private
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only#Violation_report_syntax
def csp_report_params
params.require(:'csp-report')
.permit(:disposition,
:referrer,
:'blocked-uri',
:'document-uri',
:'effective-directive',
:'original-policy',
:'script-sample',
:'status-code',
:'violated-directive',
:'line-number',
:'column-number',
:'source-file')
end
end
end
end

View file

@ -4,6 +4,9 @@ module Api
module V1 module V1
# API para sitios # API para sitios
class SitesController < BaseController class SitesController < BaseController
http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'],
password: ENV['HTTP_BASIC_PASSWORD']
def index def index
render json: Site.all.order(:name).pluck(:name) render json: Site.all.order(:name).pluck(:name)
end end

4
app/models/csp_report.rb Normal file
View file

@ -0,0 +1,4 @@
# frozen_string_literal: true
# Almacena un reporte de CSP
class CspReport < ApplicationRecord; end

View file

@ -6,31 +6,34 @@
# For further information see the following documentation # For further information see the following documentation
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
# Rails.application.config.content_security_policy do |policy| Rails.application.config.content_security_policy do |policy|
# policy.default_src :self, :https policy.default_src :self
# policy.font_src :self, :https, :data # XXX: Varios scripts generan estilos en línea
# policy.img_src :self, :https, :data policy.style_src :self, :unsafe_inline
# policy.object_src :none # Repetimos la default para poder saber cuál es la política en falta
# policy.script_src :self, :https policy.script_src :self
# policy.style_src :self, :https policy.font_src :self
# # If you are using webpack-dev-server then specify # TODO: Permitimos cargar imágenes remotas?
# # webpack-dev-server host policy.img_src :self
# policy.connect_src :self, :https, "http://localhost:3035", # Ya no usamos applets!
# "ws://localhost:3035" if Rails.env.development? policy.object_src :none
if Rails.env.development?
policy.connect_src :self,
'http://localhost:3035',
'ws://localhost:3035'
end
# # Specify URI for violation reports # Specify URI for violation reports
# # policy.report_uri "/csp-violation-report-endpoint" policy.report_uri "https://api.#{ENV.fetch('SUTTY_WITH_PORT', 'sutty.nl')}/v1/csp_reports.json"
# end end
# If you are using UJS then enable automatic nonce generation # If you are using UJS then enable automatic nonce generation
# Rails.application.config.content_security_policy_nonce_generator = # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
# -> request { SecureRandom.base64(16) }
# Set the nonce only to specific directives # Set the nonce only to specific directives
# Rails.application.config.content_security_policy_nonce_directives = # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
# %w(script-src)
# Report CSP violations to a specified URI # Report CSP violations to a specified URI
# For further information see the following documentation: # For further information see the following documentation:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
# Rails.application.config.content_security_policy_report_only = true Rails.application.config.content_security_policy_report_only = false

View file

@ -1,6 +1,3 @@
# frozen_string_literal: true # frozen_string_literal: true
# Be sure to restart your server when you modify this file. Mime::Type.register 'application/csp-report', :json
# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf

View file

@ -18,6 +18,7 @@ Rails.application.routes.draw do
constraints subdomain: 'api' do constraints subdomain: 'api' do
scope module: 'api' do scope module: 'api' do
namespace :v1 do namespace :v1 do
resources :csp_reports, only: %i[create]
get 'sites/allowed', to: 'sites#allowed' get 'sites/allowed', to: 'sites#allowed'
resources :sites, only: %i[index] resources :sites, only: %i[index]
end end

View file

@ -0,0 +1,17 @@
class CreateCspReports < ActiveRecord::Migration[6.0]
def change
create_table :csp_reports, id: :uuid do |t|
t.timestamps
t.string :disposition
t.string :referrer
t.string :blocked_uri
t.string :document_uri
t.string :effective_directive
t.string :original_policy
t.string :script_sample
t.string :status_code
t.string :violated_directive
end
end
end

View file

@ -0,0 +1,7 @@
class AddSourceToCspReports < ActiveRecord::Migration[6.0]
def change
add_column :csp_reports, :column_number, :integer
add_column :csp_reports, :line_number, :integer
add_column :csp_reports, :source_file, :string
end
end

View file

@ -12,7 +12,10 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20_190_829_180_743) do ActiveRecord::Schema.define(version: 20_200_206_151_057) 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| create_table 'action_text_rich_texts', force: :cascade do |t|
t.string 'name', null: false t.string 'name', null: false
t.text 'body' t.text 'body'
@ -55,6 +58,9 @@ ActiveRecord::Schema.define(version: 20_190_829_180_743) do
t.index ['deploy_id'], name: 'index_build_stats_on_deploy_id' t.index ['deploy_id'], name: 'index_build_stats_on_deploy_id'
end end
# Could not dump table "csp_reports" because of following StandardError
# Unknown type 'uuid' for column 'id'
create_table 'deploys', force: :cascade do |t| create_table 'deploys', force: :cascade do |t|
t.datetime 'created_at', null: false t.datetime 'created_at', null: false
t.datetime 'updated_at', null: false t.datetime 'updated_at', null: false
@ -132,6 +138,7 @@ ActiveRecord::Schema.define(version: 20_190_829_180_743) do
t.string 'status', default: 'waiting' t.string 'status', default: 'waiting'
t.text 'description' t.text 'description'
t.string 'title' t.string 'title'
t.boolean 'colaboracion_anonima', default: false
t.index ['design_id'], name: 'index_sites_on_design_id' t.index ['design_id'], name: 'index_sites_on_design_id'
t.index ['licencia_id'], name: 'index_sites_on_licencia_id' t.index ['licencia_id'], name: 'index_sites_on_licencia_id'
t.index ['name'], name: 'index_sites_on_name', unique: true t.index ['name'], name: 'index_sites_on_name', unique: true

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'test_helper'
module Api
module V1
class CSPReportsControllerTest < ActionDispatch::IntegrationTest
test 'se puede enviar un reporte' do
post v1_csp_reports_url,
params: {
'csp-report': {
'document-uri': 'http://example.com/signup.html',
'referrer': '',
'blocked-uri': 'http://example.com/css/style.css',
'violated-directive': 'style-src cdn.example.com',
'original-policy': "default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports"
}
}
assert_equal 201, response.status
assert_equal 1, CspReport.all.count
end
end
end
end