Mise à jour de l'algorithme de génération de clés

Mise à jour de l'algorithme de génération de clés

Préambule

Avant Rails 7, le générateur de clés ActiveSupport::KeyGenerator utilisait l’algorithme SHA1 pour chiffrer des messages. Ce générateur est utilisé dans de multiples cas :

  • Les cookies chiffrés (encrypted cookies)
  • Les attributs chiffrés (encrypted attributes) (Non utilisé par Osuny)
  • Les IDs signés des objets, notamment les blobs d’ActiveStorage
  • Les ETags et les clés de cache

A partir de Rails 7, le générateur de clés utilise SHA256 comme algorithme par défaut, ce qui casse les messages listés précédemment. Pas de vrai problème pour les cookies, les Etags et les clés de cache. Cependant, chaque ID signé de blobs créé avec SHA1 est désormais invalide. Ce qui pose problème dans les blocs qui stockent cet ID signé dans le data JSON ou encore dans l’attribut direct_url des fichiers médias des sites web créés avec Osuny.

Pour les fichiers médias, une mise à jour des sites web va actualiser sans problème. Cependant, il faut un script pour mettre à jour les IDs signés dans les data JSON des blocs.

Classe du convertisseur

Définition

class ActiveStorageKeyConverter
  def self.convert(legacy_signed_id)
    begin
      # Try to find blob with the un-modified legacy_signed_id
      blob = ActiveStorage::Blob.find_signed!(legacy_signed_id)
      legacy_signed_id
    rescue ActiveSupport::MessageVerifier::InvalidSignature
      begin
        # Try to find blob with ID from SHA1-signed_id
        key_generator = ActiveSupport::KeyGenerator.new(
          Rails.application.secrets.secret_key_base,
          iterations: 1000,
          hash_digest_class: OpenSSL::Digest::SHA1
        )
        key_generator = ActiveSupport::CachingKeyGenerator.new(key_generator)
        secret = key_generator.generate_key("ActiveStorage")
        verifier = ActiveSupport::MessageVerifier.new(secret)

        ActiveStorage::Blob.find_by_id(verifier.verify(legacy_signed_id, purpose: :blob_id)).try(:signed_id)
      rescue ActiveSupport::MessageVerifier::InvalidSignature
        # Blob not found (SHA1 and SHA256), corrupted blob ID, ignore
        legacy_signed_id
      end
    end
  end
end

Utilisation

ActiveStorageKeyConverter.convert legacy_signed_id

Script pour les Communication::Blocks


def crawl(enumerable)
  case enumerable
  when Array
    enumerable.each do |item|
      crawl(item) if [Array, Hash].include?(item.class)
    end
  when Hash
    enumerable.keys.each do |key|
      if key == "signed_id"
        # Convert value
        enumerable[key] = ActiveStorageKeyConverter.convert(enumerable[key]) if key == "signed_id"
      elsif [Array, Hash].include?(enumerable[key].class)
        crawl(enumerable[key])
      end
    end
  end
end

Communication::Block.all.find_each { |block|
  crawl(block.data)
  block.save
}

Sources

https://www.bigbinary.com/blog/how-we-upgraded-from-rails-6-to-rails-7