altcha-rails 0.0.4 → 0.0.6
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 +4 -4
- data/.editorconfig +5 -0
- data/.gitignore +2 -0
- data/LICENSE +20 -0
- data/README.md +112 -0
- data/altcha-rails.gemspec +16 -0
- data/lib/altcha-rails.rb +65 -0
- data/lib/generators/altcha/install/install_generator.rb +33 -0
- data/lib/generators/altcha/install/templates/controllers/altcha_controller.rb +5 -0
- data/lib/generators/altcha/install/templates/initializers/altcha.rb +8 -0
- data/lib/generators/altcha/install/templates/migrations/create_altcha_solutions.rb.erb +15 -0
- data/lib/generators/altcha/install/templates/models/altcha_solution.rb +28 -0
- metadata +14 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 166ab2cdd6732e309f96c332d0483bcb16f984697f6f56ffab78357635aaccc3
|
4
|
+
data.tar.gz: baa9cf000ae61e2a1da2e3571827ff318d046ca3707e0341c6a6c036718b9278
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 554a2a258ad6e498034ae21d82788ca50b93543fcdfe086cc346b270c8ebc35a7c44702a30bf3ca0d618ac3b38c0943f04099b47a34c37ad33c8f5a2e1555b11
|
7
|
+
data.tar.gz: 9acd0d3bedda678efb8480d9d4935cdc1e1d8f97455eeba40e4dbb692e9753a5d9662ce0d05f841f98af2d2288aa6102267e93765d6873df2c12d59d1b4be0f2
|
data/.editorconfig
ADDED
data/.gitignore
ADDED
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,112 @@
|
|
1
|
+
[](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-rails` 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-rails'
|
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
|
+
```
|
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
|
+
You will also have to run 'rails db:migrate` to apply pending changes to the database.
|
35
|
+
|
36
|
+
## Configuration
|
37
|
+
|
38
|
+
The initializer file `config/initializers/altcha.rb` contains the following configuration options:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
Altcha.setup do |config|
|
42
|
+
config.algorithm = 'SHA-256'
|
43
|
+
config.num_range = (50_000..500_000)
|
44
|
+
config.timeout = 5.minutes
|
45
|
+
config.hmac_key = 'change-me'
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
The `algorithm` option specifies the hashing algorithm to use and must currently be set to `SHA-256`.
|
50
|
+
It is crucial change the `hmac_key` to a random value. This key is used to sign the challenge and the response,
|
51
|
+
so it must be treated as a secret within your application.
|
52
|
+
The `num_range` option specifies the range of numbers to use in the challenge and determines the difficulty of the proof-of-work.
|
53
|
+
For an explanation of the `timeout` option see below.
|
54
|
+
|
55
|
+
## Challenge expiration
|
56
|
+
|
57
|
+
The current time of the server is included in the salt of the challenge. When the client responds, it has to send the
|
58
|
+
same salt back, so the server can determine when the challenge was issued. The `timeout` option in the initializer file
|
59
|
+
specifies the time that a challenge is valid. If the response is received after the timeout, the verification will fail.
|
60
|
+
|
61
|
+
As users might complete the captcha before filling out a complex form, the `timeout` should be set to a reasonable
|
62
|
+
value.
|
63
|
+
|
64
|
+
## Replay attacks
|
65
|
+
|
66
|
+
To also guard against replay attacks within the configured `timeout` period, the gem uses a model named `AltchaSolution` to
|
67
|
+
store completed responses. A unique constraint is added to the database to prevent the same response from being stored.
|
68
|
+
|
69
|
+
As these stored solutions are useless after the `timeout` period, the `AltchaSolution.cleanup` convenience function
|
70
|
+
should be called regularly to purge outdates soltutions from the database.
|
71
|
+
|
72
|
+
## Usage
|
73
|
+
|
74
|
+
You need to include the ALTCHA javascript widget in your application's asset pipeline. This is not done by the gem
|
75
|
+
at this point. Read up on the [ALTCHA documentation](https://altcha.org/docs/website-integration) for more information.
|
76
|
+
|
77
|
+
Then add the following code to the form you want to protect:
|
78
|
+
|
79
|
+
```erb
|
80
|
+
<altcha-widget challengeurl="<%= altcha_url() %>"></altcha-widget>
|
81
|
+
```
|
82
|
+
|
83
|
+
Once the user clicks the checkbox, the widget will send a request to the server to get a new challenge.
|
84
|
+
When the user-side code inside the widget found the solution to the challenge, the spinner will stop
|
85
|
+
and a hidden input field with the name `altcha` will be created in the form to convey the solution as
|
86
|
+
base64 encoded JSON dictionary.
|
87
|
+
|
88
|
+
In the controller that handles the form submission, you can verify the response with the following code:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
def create
|
92
|
+
@model = Model.create(model_params)
|
93
|
+
|
94
|
+
unless AltchaSolution.verify_and_save(params.permit(:altcha)[:altcha])
|
95
|
+
flash.now[:alert] = 'ALTCHA verification failed.'
|
96
|
+
render :new, status: :unprocessable_entity
|
97
|
+
return
|
98
|
+
end
|
99
|
+
|
100
|
+
# ...
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
The `verify_and_save` method will return `true` if the response is valid and has not been used before.
|
105
|
+
|
106
|
+
## Contributing
|
107
|
+
|
108
|
+
Bug reports and pull requests are welcome.
|
109
|
+
|
110
|
+
## License
|
111
|
+
|
112
|
+
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.6"
|
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
|
data/lib/altcha-rails.rb
ADDED
@@ -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,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, name: 'index_altcha_solutions'
|
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,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: altcha-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Mack
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: ALTCHA is a free, open-source CAPTCHA alternative that protects your
|
14
14
|
website from spam and abuse
|
@@ -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
|