active_hashcash 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +85 -0
- data/lib/active_hashcash.rb +150 -0
- data/lib/hashcash.js +257 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 47a914f25827b270ed0e1bb2f0fc222316846b48232cf5dceb255c99725f1467
|
4
|
+
data.tar.gz: f5aaf27f5d578cfc271624c4ddd3746365e81424247cd50ed43744cb534ea893
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9799788e77f0f6d8415d0e69a153a8b88715e34d5742794ed64a0db3a30a41bd619bc54d9c840b1d4d37eff4f0f9cf90ec9bc4c552656aad8f086068997685e0
|
7
|
+
data.tar.gz: f439c8fb6265ae1a2999b6fe3995871762e09b8ab05b38a776dc4e0eafd4fe67d431396323425118f55d596c2f2270ff5160733ccc94fe7f1d47df0eba38ce9f
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Alexis Bernard
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# ActiveHashcash
|
2
|
+
|
3
|
+
<img align="right" width="200px" src="logo.png" alt="Active Hashcash logo"/>
|
4
|
+
|
5
|
+
ActiveHashcash protects your Rails application against DoS and bots.
|
6
|
+
|
7
|
+
Hashcash is proof-of-work algorithm, invented by Adam Back in 1997, to protect systems against denial of service attacks.
|
8
|
+
ActiveHashcash is an easy way to protect any Rails application against brute force attacks and some bots.
|
9
|
+
|
10
|
+
The idea is to force clients to spend some time to solve a hard problem that is very easy to verify for the server.
|
11
|
+
We have developped ActiveHashcash after seeing brute force attacks against our Rails application monitoring service [RorVsWild](https://rorvswild.com).
|
12
|
+
|
13
|
+
The idea is to enable ActiveHashcash on sensitive forms such as login and registration. While the user is filling the form,
|
14
|
+
ActiveHashcash performs the work in JavaScript and set the result into a hidden input text. The form cannot be submitted while the proof of work has not been found.
|
15
|
+
The user submits the form, and the stamp is verified by the controller in a before action.
|
16
|
+
|
17
|
+
It blocks bots that do not interpret JavaScript since the proof of work is not computed. For the more sophisticated bots, we are happy to slow them down.
|
18
|
+
|
19
|
+
Here is a [demo on a login form](https://www.rorvswild.com/session) :
|
20
|
+
|
21
|
+
![Active Hashcash GIF preview](demo.gif)
|
22
|
+
|
23
|
+
## Limitations
|
24
|
+
|
25
|
+
The JavaScript implementation is 10 to 20 times slower than the official C version.
|
26
|
+
It needs some work and knowledges to be optimised. Unfortunately, I'm not a JavaScript expert.
|
27
|
+
Maybe you have good JS skills to optimize it ?
|
28
|
+
|
29
|
+
## Installation
|
30
|
+
|
31
|
+
Add this line to your application's Gemfile:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem "active_hashcash"
|
35
|
+
```
|
36
|
+
|
37
|
+
Require hashcash from your JavaScript manifest.
|
38
|
+
|
39
|
+
```js
|
40
|
+
//= require hashcash
|
41
|
+
```
|
42
|
+
|
43
|
+
Add a Hashcash hidden field into the form you want to protect.
|
44
|
+
|
45
|
+
```erb
|
46
|
+
<form>
|
47
|
+
<%= hashcash_hidden_field_tag %>
|
48
|
+
</form>
|
49
|
+
```
|
50
|
+
|
51
|
+
Then you have to define a `before_action :check_hashcash` in you controller.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class SessionController < ApplicationController
|
55
|
+
include ActiveHashcash
|
56
|
+
|
57
|
+
# Only the action receiving the form needs to be protected
|
58
|
+
before_action :check_hashcash, only: :create
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
To customize some behaviour, you can override most of the methods which begins with `hashcash_`.
|
63
|
+
Simply have a look to `active_hashcash.rb`.
|
64
|
+
|
65
|
+
You must have Redis in order to prevent double spent stamps. Otherwise it will be useless.
|
66
|
+
It automatically tries to connect with the environement variables `ACTIVE_HASHCASH_REDIS_URL` or `REDIS_URL`.
|
67
|
+
You can also manually set the URL with `ActiveHashcash.redis_url = redis://user:password@localhost:6379`.
|
68
|
+
|
69
|
+
## Complexity
|
70
|
+
|
71
|
+
Complexity is the most important parameter. By default its value is 20 and requires most of the time 5 to 20 seconds to be solved on a decent laptop.
|
72
|
+
The user won't wait that long, since he needs to fill the form while the problem is solving.
|
73
|
+
Howevever, if your application includes people with slow and old devices, then consider lowering this value, to 16 or 18.
|
74
|
+
|
75
|
+
You can change the complexity, either with `ActiveHashcash.bits = 20` or by overriding the method `hashcash_bits` in you controller.
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/active_hashcash.
|
80
|
+
|
81
|
+
## License
|
82
|
+
|
83
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
84
|
+
|
85
|
+
Made by Alexis Bernard at [Base Secrète](https://basesecrete.com).
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module ActiveHashcash
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
include ActionView::Helpers::FormTagHelper
|
5
|
+
|
6
|
+
included do
|
7
|
+
helper_method :hashcash_hidden_field_tag
|
8
|
+
end
|
9
|
+
|
10
|
+
mattr_accessor :bits, instance_accessor: false, default: 20
|
11
|
+
mattr_accessor :resource, instance_accessor: false
|
12
|
+
mattr_accessor :redis_url, instance_accessor: false
|
13
|
+
|
14
|
+
# TODO: protect_from_brute_force bits: 20, exception: ActionController::InvalidAuthenticityToken, with: :handle_failed_hashcash
|
15
|
+
|
16
|
+
# Call me via a before_action when the form is submitted : `before_action :chech_hashcash, only: :create`
|
17
|
+
def check_hashcash
|
18
|
+
if hashcash_stamp_is_valid? && !hashcash_stamp_spent?
|
19
|
+
hashcash_redis.sadd("active_hashcash_stamps".freeze, hashcash_param)
|
20
|
+
hashcash_after_success
|
21
|
+
else
|
22
|
+
hashcash_after_failure
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Override the methods below in your controller, to change any parameter of behaviour.
|
27
|
+
|
28
|
+
# By default the host name is used as the resource.
|
29
|
+
# It' should be good for most cases and prevent from reusing the same stamp between sites.
|
30
|
+
def hashcash_resource
|
31
|
+
ActiveHashcash.resource || request.host
|
32
|
+
end
|
33
|
+
|
34
|
+
# Define the complexity, the higher the slower it is. Consider lowering this value to not exclude people with old and slow devices.
|
35
|
+
# On a decent laptop, it takes around 30 seconds for the JavaScript implementation to solve a 20 bits complexity and few seconds when it's 16.
|
36
|
+
def hashcash_bits
|
37
|
+
ActiveHashcash.bits
|
38
|
+
end
|
39
|
+
|
40
|
+
# Override if you want to rename the hashcash param.
|
41
|
+
def hashcash_param
|
42
|
+
params[:hashcash]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Override to customize message displayed on submit button while computing hashcash.
|
46
|
+
def hashcash_waiting_message
|
47
|
+
t("active_hashcash.waiting_label")
|
48
|
+
end
|
49
|
+
|
50
|
+
# Override to provide a different behaviour when hashcash failed
|
51
|
+
def hashcash_after_failure
|
52
|
+
raise ActionController::InvalidAuthenticityToken.new("Invalid hashcash #{hashcash_param}")
|
53
|
+
end
|
54
|
+
|
55
|
+
# Maybe you want something special when the hashcash is valid. By default nothing happens.
|
56
|
+
def hashcash_after_success
|
57
|
+
# Override me for your own needs.
|
58
|
+
end
|
59
|
+
|
60
|
+
# Call it inside the form that have to be protected and don't forget to initialize the JavaScript Hascash.setup().
|
61
|
+
# Unless you need something really special, you should not need to override this method.
|
62
|
+
def hashcash_hidden_field_tag(name = :hashcash)
|
63
|
+
options = {resource: hashcash_resource, bits: hashcash_bits, waiting_message: hashcash_waiting_message}
|
64
|
+
hidden_field_tag(name, "", "data-hashcash" => options.to_json)
|
65
|
+
end
|
66
|
+
|
67
|
+
def hashcash_redis
|
68
|
+
@hashcash_redis = Redis.new(url: hashcash_redis_url)
|
69
|
+
end
|
70
|
+
|
71
|
+
def hashcash_redis_url
|
72
|
+
ActiveHashcash.redis_url || ENV["ACTIVE_HASHCASH_REDIS_URL"] || ENV["REDIS_URL"]
|
73
|
+
end
|
74
|
+
|
75
|
+
def hashcash_stamp_is_valid?
|
76
|
+
stamp = Stamp.parse(hashcash_param)
|
77
|
+
stamp.valid? && stamp.bits >= hashcash_bits && stamp.parse_date >= Date.yesterday
|
78
|
+
end
|
79
|
+
|
80
|
+
def hashcash_stamp_spent?
|
81
|
+
hashcash_redis.sismember("active_hashcash_stamps".freeze, hashcash_param)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
class Engine < ::Rails::Engine
|
87
|
+
config.assets.paths << File.expand_path("..", __FILE__)
|
88
|
+
|
89
|
+
config.after_initialize { load_translations }
|
90
|
+
|
91
|
+
def load_translations
|
92
|
+
if !I18n.backend.exists?(I18n.locale, "active_hashcash")
|
93
|
+
I18n.backend.store_translations(:de, {active_hashcash: {waiting_label: "Warten auf die Überprüfung ..."}})
|
94
|
+
I18n.backend.store_translations(:en, {active_hashcash: {waiting_label: "Waiting for verification ..."}})
|
95
|
+
I18n.backend.store_translations(:es, {active_hashcash: {waiting_label: "A la espera de la verificación ..."}})
|
96
|
+
I18n.backend.store_translations(:fr, {active_hashcash: {waiting_label: "En attente de vérification ..."}})
|
97
|
+
I18n.backend.store_translations(:it, {active_hashcash: {waiting_label: "In attesa di verifica ..."}})
|
98
|
+
I18n.backend.store_translations(:jp, {active_hashcash: {waiting_label: "検証待ち ..."}})
|
99
|
+
I18n.backend.store_translations(:pt, {active_hashcash: {waiting_label: "À espera de verificação ..."}})
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Stamp
|
105
|
+
attr_reader :version, :bits, :date, :resource, :extension, :rand, :counter
|
106
|
+
|
107
|
+
def self.parse(string)
|
108
|
+
args = string.split(":")
|
109
|
+
new(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.mint(resource, options = {})
|
113
|
+
new(
|
114
|
+
options[:version] || 1,
|
115
|
+
options[:bits] || ActiveHashcash.bits,
|
116
|
+
options[:date] || Date.today.strftime("%y%m%d"),
|
117
|
+
resource,
|
118
|
+
options[:ext],
|
119
|
+
options[:rand] || SecureRandom.alphanumeric(16),
|
120
|
+
options[:counter] || 0).work
|
121
|
+
end
|
122
|
+
|
123
|
+
def initialize(version, bits, date, resource, extension, rand, counter)
|
124
|
+
@version = version
|
125
|
+
@bits = bits.to_i
|
126
|
+
@date = date.respond_to?(:strftime) ? date.strftime("%y%m%d") : date
|
127
|
+
@resource = resource
|
128
|
+
@extension = extension
|
129
|
+
@rand = rand
|
130
|
+
@counter = counter
|
131
|
+
end
|
132
|
+
|
133
|
+
def valid?
|
134
|
+
Digest::SHA1.hexdigest(to_s).hex >> (160-bits) == 0
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_s
|
138
|
+
[version, bits, date, resource, extension, rand, counter].join(":")
|
139
|
+
end
|
140
|
+
|
141
|
+
def parse_date
|
142
|
+
Date.strptime(date, "%y%m%d")
|
143
|
+
end
|
144
|
+
|
145
|
+
def work
|
146
|
+
@counter += 1 until valid?
|
147
|
+
self
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/hashcash.js
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
// http://www.hashcash.org/docs/hashcash.html
|
2
|
+
// <input type="hiden" name="hashcash" data-hashcash="{resource: 'site.example', bits: 16}"/>
|
3
|
+
Hashcash = function(input) {
|
4
|
+
options = JSON.parse(input.getAttribute("data-hashcash"))
|
5
|
+
Hashcash.disableParentForm(input, options)
|
6
|
+
input.dispatchEvent(new CustomEvent("hashcash:mint", {bubbles: true}))
|
7
|
+
|
8
|
+
Hashcash.mint(options.resource, options, function(stamp) {
|
9
|
+
input.value = stamp.toString()
|
10
|
+
Hashcash.enableParentForm(input, options)
|
11
|
+
input.dispatchEvent(new CustomEvent("hashcash:minted", {bubbles: true, detail: {stamp: stamp}}))
|
12
|
+
})
|
13
|
+
}
|
14
|
+
|
15
|
+
Hashcash.setup = function() {
|
16
|
+
if (document.readyState != "loading") {
|
17
|
+
var input = document.querySelector("input#hashcash")
|
18
|
+
input && new Hashcash(input)
|
19
|
+
} else
|
20
|
+
document.addEventListener("DOMContentLoaded", Hashcash.setup )
|
21
|
+
}
|
22
|
+
|
23
|
+
Hashcash.disableParentForm = function(input, options) {
|
24
|
+
input.form.querySelectorAll("[type=submit]").forEach(function(submit) {
|
25
|
+
submit.originalValue = submit.value
|
26
|
+
options["waiting_message"] && (submit.value = options["waiting_message"])
|
27
|
+
submit.disabled = true
|
28
|
+
})
|
29
|
+
}
|
30
|
+
|
31
|
+
Hashcash.enableParentForm = function(input, options) {
|
32
|
+
input.form.querySelectorAll("[type=submit]").forEach(function(submit) {
|
33
|
+
submit.originalValue && (submit.value = submit.originalValue)
|
34
|
+
submit.disabled = null
|
35
|
+
})
|
36
|
+
}
|
37
|
+
|
38
|
+
Hashcash.default = {
|
39
|
+
version: 1,
|
40
|
+
bits: 20,
|
41
|
+
extension: null,
|
42
|
+
}
|
43
|
+
|
44
|
+
Hashcash.mint = function(resource, options, callback) {
|
45
|
+
// Format date to YYMMDD
|
46
|
+
var date = new Date
|
47
|
+
var year = date.getFullYear().toString()
|
48
|
+
year = year.slice(year.length - 2, year.length)
|
49
|
+
var month = (date.getMonth() + 1).toString().padStart(2, "0")
|
50
|
+
var day = date.getDate().toString().padStart(2, "0")
|
51
|
+
|
52
|
+
var stamp = new Hashcash.Stamp(
|
53
|
+
options.version || Hashcash.default.version,
|
54
|
+
options.bits || Hashcash.default.bits,
|
55
|
+
options.date || year + month + day,
|
56
|
+
resource,
|
57
|
+
options.extension || Hashcash.default.extension,
|
58
|
+
options.rand || Math.random().toString(36).substr(2, 10),
|
59
|
+
)
|
60
|
+
return stamp.work(callback)
|
61
|
+
}
|
62
|
+
|
63
|
+
Hashcash.Stamp = function(version, bits, date, resource, extension, rand, counter = 0) {
|
64
|
+
this.version = version
|
65
|
+
this.bits = bits
|
66
|
+
this.date = date
|
67
|
+
this.resource = resource
|
68
|
+
this.extension = extension
|
69
|
+
this.rand = rand
|
70
|
+
this.counter = counter
|
71
|
+
}
|
72
|
+
|
73
|
+
Hashcash.Stamp.parse = function(string) {
|
74
|
+
var args = string.split(":")
|
75
|
+
return new Hashcash.Stamp(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
|
76
|
+
}
|
77
|
+
|
78
|
+
Hashcash.Stamp.prototype.toString = function() {
|
79
|
+
return [this.version, this.bits, this.date, this.resource, this.extension, this.rand, this.counter].join(":")
|
80
|
+
}
|
81
|
+
|
82
|
+
// Trigger the given callback when the problem is solved.
|
83
|
+
// In order to not freeze the page, setTimeout is called every 100ms to let some CPU to other tasks.
|
84
|
+
Hashcash.Stamp.prototype.work = function(callback) {
|
85
|
+
this.startClock()
|
86
|
+
var timer = performance.now()
|
87
|
+
while (!this.check())
|
88
|
+
if (this.counter++ && performance.now() - timer > 100)
|
89
|
+
return setTimeout(this.work.bind(this), 0, callback)
|
90
|
+
this.stopClock()
|
91
|
+
callback(this)
|
92
|
+
}
|
93
|
+
|
94
|
+
Hashcash.Stamp.prototype.check = function() {
|
95
|
+
var array = Hashcash.sha1(this.toString())
|
96
|
+
return array[0] >> (160-this.bits) == 0
|
97
|
+
}
|
98
|
+
|
99
|
+
Hashcash.Stamp.prototype.startClock = function() {
|
100
|
+
this.startedAt || (this.startedAt = performance.now())
|
101
|
+
}
|
102
|
+
|
103
|
+
Hashcash.Stamp.prototype.stopClock = function() {
|
104
|
+
this.endedAt || (this.endedAt = performance.now())
|
105
|
+
var duration = this.endedAt - this.startedAt
|
106
|
+
var speed = Math.round(this.counter * 1000 / duration)
|
107
|
+
console.debug("Hashcash " + this.toString() + " minted in " + duration + "ms (" + speed + " per seconds)")
|
108
|
+
}
|
109
|
+
|
110
|
+
/**
|
111
|
+
* Secure Hash Algorithm (SHA1)
|
112
|
+
* http://www.webtoolkit.info/
|
113
|
+
**/
|
114
|
+
Hashcash.sha1 = function(msg) {
|
115
|
+
var rotate_left = Hashcash.sha1.rotate_left
|
116
|
+
var Utf8Encode = Hashcash.sha1.Utf8Encode
|
117
|
+
|
118
|
+
var blockstart;
|
119
|
+
var i, j;
|
120
|
+
var W = new Array(80);
|
121
|
+
var H0 = 0x67452301;
|
122
|
+
var H1 = 0xEFCDAB89;
|
123
|
+
var H2 = 0x98BADCFE;
|
124
|
+
var H3 = 0x10325476;
|
125
|
+
var H4 = 0xC3D2E1F0;
|
126
|
+
var A, B, C, D, E;
|
127
|
+
var temp;
|
128
|
+
msg = Utf8Encode(msg);
|
129
|
+
var msg_len = msg.length;
|
130
|
+
var word_array = new Array();
|
131
|
+
for (i = 0; i < msg_len - 3; i += 4) {
|
132
|
+
j = msg.charCodeAt(i) << 24 | msg.charCodeAt(i + 1) << 16 |
|
133
|
+
msg.charCodeAt(i + 2) << 8 | msg.charCodeAt(i + 3);
|
134
|
+
word_array.push(j);
|
135
|
+
}
|
136
|
+
switch (msg_len % 4) {
|
137
|
+
case 0:
|
138
|
+
i = 0x080000000;
|
139
|
+
break;
|
140
|
+
case 1:
|
141
|
+
i = msg.charCodeAt(msg_len - 1) << 24 | 0x0800000;
|
142
|
+
break;
|
143
|
+
case 2:
|
144
|
+
i = msg.charCodeAt(msg_len - 2) << 24 | msg.charCodeAt(msg_len - 1) << 16 | 0x08000;
|
145
|
+
break;
|
146
|
+
case 3:
|
147
|
+
i = msg.charCodeAt(msg_len - 3) << 24 | msg.charCodeAt(msg_len - 2) << 16 | msg.charCodeAt(msg_len - 1) << 8 | 0x80;
|
148
|
+
break;
|
149
|
+
}
|
150
|
+
word_array.push(i);
|
151
|
+
while ((word_array.length % 16) != 14) word_array.push(0);
|
152
|
+
word_array.push(msg_len >>> 29);
|
153
|
+
word_array.push((msg_len << 3) & 0x0ffffffff);
|
154
|
+
for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
|
155
|
+
for (i = 0; i < 16; i++) W[i] = word_array[blockstart + i];
|
156
|
+
for (i = 16; i <= 79; i++) W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
|
157
|
+
A = H0;
|
158
|
+
B = H1;
|
159
|
+
C = H2;
|
160
|
+
D = H3;
|
161
|
+
E = H4;
|
162
|
+
for (i = 0; i <= 19; i++) {
|
163
|
+
temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
|
164
|
+
E = D;
|
165
|
+
D = C;
|
166
|
+
C = rotate_left(B, 30);
|
167
|
+
B = A;
|
168
|
+
A = temp;
|
169
|
+
}
|
170
|
+
for (i = 20; i <= 39; i++) {
|
171
|
+
temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
|
172
|
+
E = D;
|
173
|
+
D = C;
|
174
|
+
C = rotate_left(B, 30);
|
175
|
+
B = A;
|
176
|
+
A = temp;
|
177
|
+
}
|
178
|
+
for (i = 40; i <= 59; i++) {
|
179
|
+
temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
|
180
|
+
E = D;
|
181
|
+
D = C;
|
182
|
+
C = rotate_left(B, 30);
|
183
|
+
B = A;
|
184
|
+
A = temp;
|
185
|
+
}
|
186
|
+
for (i = 60; i <= 79; i++) {
|
187
|
+
temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
|
188
|
+
E = D;
|
189
|
+
D = C;
|
190
|
+
C = rotate_left(B, 30);
|
191
|
+
B = A;
|
192
|
+
A = temp;
|
193
|
+
}
|
194
|
+
H0 = (H0 + A) & 0x0ffffffff;
|
195
|
+
H1 = (H1 + B) & 0x0ffffffff;
|
196
|
+
H2 = (H2 + C) & 0x0ffffffff;
|
197
|
+
H3 = (H3 + D) & 0x0ffffffff;
|
198
|
+
H4 = (H4 + E) & 0x0ffffffff;
|
199
|
+
}
|
200
|
+
return [H0, H1, H2, H3, H4]
|
201
|
+
}
|
202
|
+
|
203
|
+
Hashcash.hexSha1 = function(msg) {
|
204
|
+
var array = Hashcash.sha1(msg)
|
205
|
+
var cvt_hex = Hashcash.sha1.cvt_hex
|
206
|
+
return cvt_hex(array[0]) + cvt_hex(array[1]) + cvt_hex(array[2]) + cvt_hex(array3) + cvt_hex(array[4])
|
207
|
+
}
|
208
|
+
|
209
|
+
Hashcash.sha1.rotate_left = function(n, s) {
|
210
|
+
var t4 = (n << s) | (n >>> (32 - s));
|
211
|
+
return t4;
|
212
|
+
};
|
213
|
+
|
214
|
+
Hashcash.sha1.lsb_hex = function(val) {
|
215
|
+
var str = '';
|
216
|
+
var i;
|
217
|
+
var vh;
|
218
|
+
var vl;
|
219
|
+
for (i = 0; i <= 6; i += 2) {
|
220
|
+
vh = (val >>> (i * 4 + 4)) & 0x0f;
|
221
|
+
vl = (val >>> (i * 4)) & 0x0f;
|
222
|
+
str += vh.toString(16) + vl.toString(16);
|
223
|
+
}
|
224
|
+
return str;
|
225
|
+
};
|
226
|
+
|
227
|
+
Hashcash.sha1.cvt_hex = function(val) {
|
228
|
+
var str = '';
|
229
|
+
var i;
|
230
|
+
var v;
|
231
|
+
for (i = 7; i >= 0; i--) {
|
232
|
+
v = (val >>> (i * 4)) & 0x0f;
|
233
|
+
str += v.toString(16);
|
234
|
+
}
|
235
|
+
return str;
|
236
|
+
};
|
237
|
+
|
238
|
+
Hashcash.sha1.Utf8Encode = function(string) {
|
239
|
+
string = string.replace(/\r\n/g, '\n');
|
240
|
+
var utftext = '';
|
241
|
+
for (var n = 0; n < string.length; n++) {
|
242
|
+
var c = string.charCodeAt(n);
|
243
|
+
if (c < 128) {
|
244
|
+
utftext += String.fromCharCode(c);
|
245
|
+
} else if ((c > 127) && (c < 2048)) {
|
246
|
+
utftext += String.fromCharCode((c >> 6) | 192);
|
247
|
+
utftext += String.fromCharCode((c & 63) | 128);
|
248
|
+
} else {
|
249
|
+
utftext += String.fromCharCode((c >> 12) | 224);
|
250
|
+
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
251
|
+
utftext += String.fromCharCode((c & 63) | 128);
|
252
|
+
}
|
253
|
+
}
|
254
|
+
return utftext;
|
255
|
+
};
|
256
|
+
|
257
|
+
Hashcash.setup()
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_hashcash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexis Bernard
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-07-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redis
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 5.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 5.2.0
|
41
|
+
description: Potect your Rails application against DoS and bots.
|
42
|
+
email:
|
43
|
+
- alexis@basesecrete.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- CHANGELOG.md
|
49
|
+
- LICENSE.txt
|
50
|
+
- README.md
|
51
|
+
- lib/active_hashcash.rb
|
52
|
+
- lib/hashcash.js
|
53
|
+
homepage: https://github.com/BaseSecrete/active_hashcash
|
54
|
+
licenses:
|
55
|
+
- MIT
|
56
|
+
metadata:
|
57
|
+
homepage_uri: https://github.com/BaseSecrete/active_hashcash
|
58
|
+
source_code_uri: https://github.com/BaseSecrete/active_hashcash
|
59
|
+
changelog_uri: https://github.com/BaseSecrete/active_hashcash/CHANGELOG.md
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.4.0
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubygems_version: 3.2.22
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: Potect your Rails application against DoS and bots.
|
79
|
+
test_files: []
|