jasonraimondi / url-to-png

Selfhosted. URL to PNG utility featuring parallel rendering using Playwright for screenshots and with storage caching via Local, S3, or CouchDB
https://jasonraimondi.github.io/url-to-png/
MIT License
136 stars 26 forks source link

Support for hashing the URL params #30

Closed enstyled closed 4 months ago

enstyled commented 4 months ago

For security reasons, it would be great to have the option to encode the URL params with a salt string.

So instead of the URL like

http://localhost:3000?url=https://jasonraimondi.com&isFullPage=true&isMobile=true&width=400&height=400&viewPortHeight=400&viewPortWidth=400

One will get something like

http://localhost:3000?hash=dXJsPWh0dHBzOi8vamFzb25yYWltb25kaS5jb20maXNGdWxsUGFnZT10cnVlJmlzRnVsbFBhZ2U9dHJ1ZSZpc01vYmlsZT10cnVlJndpZHRoPTQwMCZoZWlnaHQ9NDAwJnZpZXdQb3J0SGVpZ2h0PTQwMCZ2aWV3UG9ydFdyaXRlPTQwMA==

The salt string can be shared between the app that generates the URL, e.g. you website and the url-to-png service for decoding, while rejecting invalid hashes. This will help preventing abuse since people won't be able to make requests with huge width/height values or pointing the service to malicious websites.

jasonraimondi commented 4 months ago

I would have no problem with supporting something like this. If you want to submit a PR, I would be happy to merge it!

jasonraimondi commented 4 months ago

I'll try and get this in the v1.6 release

jasonraimondi commented 4 months ago

This should be resolved in v2.0.0. Checkout the encryption docs here and let me know if you have any questions.

enstyled commented 4 months ago

Fantastic, thank you so much! ❤️

My only question about the encryption would be is it straight-forward to generate the hashed URL from another language than JavaScript? I can see right now it's an imported function from the JS package.

jasonraimondi commented 4 months ago

I imagine you could just plop this right into an LLM and tell it to convert it to x language, it would be able to.

https://github.com/jasonraimondi/ts-string-encrypt-decrypt/blob/main/index.ts

jasonraimondi commented 4 months ago

I just threw it into anthropic and got this. My initial glance at this, it looks roughly correct. I have not actually tried it.

require 'base64'
require 'json'
require 'openssl'

class StringEncrypter
  ENCRYPTED_STRING_DATA_REGEX = /^str-enc:(.+):(.+)$/

  attr_reader :crypto_key

  def initialize(crypto_key)
    @crypto_key = crypto_key
  end

  def encrypt(clear_text)
    iv = OpenSSL::Random.random_bytes(12)
    cipher = OpenSSL::Cipher.new('aes-256-gcm')
    cipher.encrypt
    cipher.key = @crypto_key
    cipher.iv = iv
    encrypted_data = cipher.update(clear_text) + cipher.final
    encoded_cipher_text = Base64.strict_encode64(encrypted_data)
    encoded_iv = Base64.strict_encode64(iv)
    "str-enc:#{encoded_cipher_text}:#{encoded_iv}"
  end

  def decrypt(encrypted_data)
    if (match = encrypted_data.match(ENCRYPTED_STRING_DATA_REGEX))
      encoded_cipher_text, encoded_iv = match.captures
      cipher_text = Base64.strict_decode64(encoded_cipher_text)
      iv = Base64.strict_decode64(encoded_iv)
      decipher = OpenSSL::Cipher.new('aes-256-gcm')
      decipher.decrypt
      decipher.key = @crypto_key
      decipher.iv = iv
      decipher.update(cipher_text) + decipher.final
    else
      raise 'Invalid encrypted data'
    end
  end

  def export_key
    Base64.strict_encode64(@crypto_key)
  end

  def export_key_string
    { key: export_key }.to_json
  end

  private

  def is_encrypted_string_data?(data)
    data.match?(ENCRYPTED_STRING_DATA_REGEX)
  end
end