altcha-rails 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85c137a5cb28c3aba10baa2fb0a9726c357273b3fa3e18803c0b062fe1e899b8
4
- data.tar.gz: 935c3e5086bd1ce0e0903daf71cbbef552e36d5d457853fd56410b63a4e2a991
3
+ metadata.gz: 90110a4f6b3610f15fc055ff3112584ebbce5d95c83bef42edbe1231c8ea8bfa
4
+ data.tar.gz: 57b70648aca748843334555ef4248b797ec0a4c8afcdb80bdd5e838dd3a147e4
5
5
  SHA512:
6
- metadata.gz: 34d5185abba605464ef98015180cef4f74c670433cb6ee28070e2b7780b32c738fc065201a66edbcd58c333a8c6cee9b03b7dc3b914bd259e00efa885a07afdd
7
- data.tar.gz: 6198c9f114b1a5a8c8ac02a4171d4d79fdcc18cf1c6c073f753e07f773788e2b99c5390f6e5505119cdcc2034132b63609cee6724c0a83e3951dfe414d07a75f
6
+ metadata.gz: 521a8e5a81fa59babb816ea1ff8b352f57362af59744b01cab0dcb176fd458e899410d7273cd633fe4a8fac4cbce9bda0858b47193c5f1a916c0c51d41158561
7
+ data.tar.gz: fcbf3c2d8cddaf1dd3e46e101df6334ab32b17ff2ca1da01a7b873a7ff9030f84730756c420f06b75e1ef4b826857ec27dabbc20bf3e7f3379633ee46fb15923
data/.editorconfig ADDED
@@ -0,0 +1,5 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 4
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ /*.gem
2
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2024 Daniel Mack
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ [![Gem Version](https://badge.fury.io/rb/altcha-rails.svg)](https://badge.fury.io/rb/altcha-rails)
2
+
3
+ # Ruby gem for ALTCHA
4
+
5
+ [ALTCHA](https://altcha.org/) is a protocol designed for safeguarding against spam and abuse by utilizing a proof-of-work mechanism. This protocol comprises both a client-facing widget and a server-side verification process.
6
+
7
+ `altcha-ruby` is a Ruby gem that provides a simple way to integrate ALTCHA into your Ruby on Rails application.
8
+
9
+ The main functionality of the gem is to generate a challenge and verify the response from the client. This is done in the library code. An initializer and a controller is installed in the host application to handle the challenge generation and verification.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'altcha-ruby'
17
+ ```
18
+
19
+ Then execute `bundle install` to install the gem for your application.
20
+
21
+ Next, run the generator to install the initializer and the controller:
22
+
23
+ ```bash
24
+ $ rails generate altcha:install
25
+ create app/models/altcha_solution.rb
26
+ create app/controllers/altcha_controller.rb
27
+ create config/initializers/altcha.rb
28
+ route get '/altcha', to: 'altcha#new'
29
+ create db/migrate/20240211145410_create_altcha_solutions.rb
30
+ ```
31
+
32
+ This will create an initializer file at `config/initializers/altcha.rb` and a controller at `app/controllers/altcha_controller.rb` as well as a route in `config/routes.rb` and a model at `app/models/altcha-solutions.rb` (see below).
33
+
34
+ ## Configuration
35
+
36
+ The initializer file `config/initializers/altcha.rb` contains the following configuration options:
37
+
38
+ ```ruby
39
+ Altcha.setup do |config|
40
+ config.algorithm = 'SHA-256'
41
+ config.num_range = (50_000..500_000)
42
+ config.timeout = 5.minutes
43
+ config.hmac_key = 'change-me'
44
+ end
45
+ ```
46
+
47
+ The `algorithm` option specifies the hashing algorithm to use and must currently be set to `SHA-256`.
48
+ It is crucial change the `hmac_key` to a secure value. This key is used to sign the challenge and the response,
49
+ so it must be treated as a secret within your application.
50
+ The `num_range` option specifies the range of numbers to use in the challenge and determines the difficulty of the proof-of-work.
51
+ For an explanation of the `timeout` option see below.
52
+
53
+ ## Challenge expiration
54
+
55
+ The current time of the server is included in the salt of the challenge. When the client responds, it has to send the
56
+ same salt back, so the server can determine when the challenge was issued. The `timeout` option in the initializer file
57
+ specifies the time that a challenge is valid. If the response is received after the timeout, the verification will fail.
58
+
59
+ As users might complete the captcha before filling out a complex form, the `timeout` should be set to a reasonable
60
+ value.
61
+
62
+ ## Replay attacks
63
+
64
+ To also guard against replay attacks within the configured `timeout` period, the gem uses a model named `AltchaSolution` to
65
+ store completed responses. A unique constraint is added to the database to prevent the same response from being stored.
66
+
67
+ As these stored solutions are useless after the `timeout` period, the `AltchaSolution.cleanup` convenience function
68
+ should be called regularly.
69
+
70
+ ## Usage
71
+
72
+ You need to include the ALTCHA javascript widget in your application's asset pipeline. This is not done by the gem
73
+ at this point. Read up on the [ALTCHA documentation](https://altcha.org/docs/website-integration) for more information.
74
+
75
+ Add then following code to the form you want to protect:
76
+
77
+ ```erb
78
+ <altcha-widget challengeurl="<%= altcha_url() %>"></altcha-widget>
79
+ ```
80
+
81
+ The widget will create a hidden input field with the name `altcha` and the response to the challenge as its value.
82
+
83
+ In the controller that handles the form submission, you can verify the response with the following code:
84
+
85
+ ```ruby
86
+ def create
87
+ @model = Model.new(model_params)
88
+
89
+ unless AltchaSolution.verify_and_save(params.permit(:altcha)[:altcha])
90
+ flash.now[:alert] = 'ALTCHA verification failed.'
91
+ render :new
92
+ return
93
+
94
+ # ...
95
+ end
96
+ ```
97
+
98
+ The `verify_and_save` method will return `true` if the response is valid and has not been used before.
99
+
100
+ ## Contributing
101
+
102
+ Bug reports and pull requests are welcome.
103
+
104
+ ## License
105
+
106
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "altcha-rails"
5
+ s.version = "0.0.5"
6
+ s.authors = ["Daniel Mack"]
7
+ s.homepage = "https://github.com/zonque/altcha-rails"
8
+ s.metadata = { "source_code_uri" => "https://github.com/zonque/altcha-rails" }
9
+ s.summary = "Rails helpers for ALTCHA"
10
+ s.description = "ALTCHA is a free, open-source CAPTCHA alternative that protects your website from spam and abuse"
11
+ s.email = "altcha-rails.gem@zonque.org"
12
+ s.require_paths = ["lib"]
13
+ s.files = `git ls-files`.split("\n")
14
+ s.licenses = ["MIT"]
15
+ s.specification_version = 4
16
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Altcha
4
+ mattr_accessor :configured
5
+ @@configured = false
6
+
7
+ mattr_accessor :algorithm
8
+ @@algorithm = 'SHA-256'
9
+
10
+ mattr_accessor :num_range
11
+ @@num_range = (50_000..500_000)
12
+
13
+ mattr_accessor :hmac_key
14
+ @@hmac_key = "change-me"
15
+
16
+ mattr_accessor :timeout
17
+ @@timeout = 5.minutes
18
+
19
+ def self.setup
20
+ @@configured = true
21
+ yield self
22
+ end
23
+
24
+ class Challenge
25
+ attr_accessor :algorithm, :challenge, :salt, :signature
26
+
27
+ def self.create
28
+ raise "Altcha not configured" unless Altcha.configured
29
+
30
+ secret_number = rand(Altcha.num_range)
31
+
32
+ a = Challenge.new
33
+ a.algorithm = Altcha.algorithm
34
+ a.salt = [Time.now.to_s, SecureRandom.hex(12)].join('|')
35
+ a.challenge = Digest::SHA256.hexdigest(a.salt + secret_number.to_s)
36
+ a.signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(a.algorithm), Altcha.hmac_key, a.challenge)
37
+
38
+ return a
39
+ end
40
+ end
41
+
42
+ class Submission
43
+ attr_accessor :algorithm, :challenge, :salt, :signature, :number
44
+
45
+ def initialize(v = {})
46
+ @algorithm = v["algorithm"] || ""
47
+ @challenge = v["challenge"] || ""
48
+ @signature = v["signature"] || ""
49
+ @salt = v["salt"] || ""
50
+ @number = v["number"] || 0
51
+ end
52
+
53
+ def valid?
54
+ check = Digest::SHA256.hexdigest(@salt + @number.to_s)
55
+
56
+ parts = @salt.split('|')
57
+ t = Time.parse(parts[0]) rescue nil
58
+
59
+ return @algorithm == Altcha.algorithm &&
60
+ @challenge == check &&
61
+ @signature == OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(Altcha.algorithm), Altcha.hmac_key, check) &&
62
+ t.present? && t > Time.now - Altcha.timeout && t < Time.now
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ require "rails/generators/active_record"
3
+
4
+ module Altcha
5
+ module Generators
6
+ class InstallGenerator < ActiveRecord::Generators::Base
7
+ desc "Installs Altcha for Rails and generates a model, a controller and a route"
8
+ argument :name, type: :string, default: "Altcha"
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ def create_model
13
+ copy_file "models/altcha_solution.rb", "app/models/altcha_solution.rb"
14
+ end
15
+
16
+ def create_controller
17
+ copy_file "controllers/altcha_controller.rb", "app/controllers/altcha_controller.rb"
18
+ end
19
+
20
+ def create_initializer
21
+ copy_file "initializers/altcha.rb", "config/initializers/altcha.rb"
22
+ end
23
+
24
+ def setup_routes
25
+ route "get '/altcha', to: 'altcha#new'"
26
+ end
27
+
28
+ def create_migrations
29
+ migration_template "migrations/create_altcha_solutions.rb.erb", "db/migrate/create_altcha_solutions.rb"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ class AltchaController < ApplicationController
2
+ def new
3
+ render json: Altcha::Challenge.create.to_json
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ Altcha.setup do |config|
4
+ config.algorithm = 'SHA-256'
5
+ config.num_range = (50_000..500_000)
6
+ config.timeout = 5.minutes
7
+ config.hmac_key = 'change-me'
8
+ end
@@ -0,0 +1,15 @@
1
+ class CreateAltchaSolutions < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version.to_s %>]
2
+ def change
3
+ create_table(:altcha_solutions) do |t|
4
+ t.string :algorithm
5
+ t.string :challenge
6
+ t.string :salt
7
+ t.string :signature
8
+ t.integer :number
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :altcha_solutions, [ :algorithm, :challenge, :salt, :signature, :number ], unique: true
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ class AltchaSolution < ApplicationRecord
2
+ validates :algorithm, :challenge, :salt, :signature, :number, presence: true
3
+ attr_accessor :took
4
+
5
+ def self.verify_and_save(base64encoded)
6
+ p = JSON.parse(Base64.decode64(base64encoded)) rescue nil
7
+ return false if p.nil?
8
+
9
+ submission = Altcha::Submission.new(p)
10
+ return false unless submission.valid?
11
+
12
+ solution = self.new(p)
13
+
14
+ begin
15
+ return solution.save
16
+ rescue ActiveRecord::RecordNotUnique
17
+ # Replay attack
18
+ return false
19
+ end
20
+ end
21
+
22
+ def self.cleanup
23
+ # Replay attacks are protected by the time stamp in the salt of the challenge for
24
+ # the duration configured in the timeout. All solutions in the database that older
25
+ # can be deleted.
26
+ AltchaSolution.where('created_at < ?', Time.now - Altcha.timeout).delete_all
27
+ end
28
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: altcha-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Mack
@@ -16,7 +16,18 @@ email: altcha-rails.gem@zonque.org
16
16
  executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
- files: []
19
+ files:
20
+ - ".editorconfig"
21
+ - ".gitignore"
22
+ - LICENSE
23
+ - README.md
24
+ - altcha-rails.gemspec
25
+ - lib/altcha-rails.rb
26
+ - lib/generators/altcha/install/install_generator.rb
27
+ - lib/generators/altcha/install/templates/controllers/altcha_controller.rb
28
+ - lib/generators/altcha/install/templates/initializers/altcha.rb
29
+ - lib/generators/altcha/install/templates/migrations/create_altcha_solutions.rb.erb
30
+ - lib/generators/altcha/install/templates/models/altcha_solution.rb
20
31
  homepage: https://github.com/zonque/altcha-rails
21
32
  licenses:
22
33
  - MIT