Class: Shugoi::Pow
- Inherits:
-
Object
- Object
- Shugoi::Pow
- Defined in:
- lib/shugoi/pow.rb
Overview
Proof-of-work anti-curl + cookies HMAC — parité avec core.ts (module Node) :
salt = HMAC(secret, ts)
proof = "ts:nonce" où SHA256(salt:nonce) a >= POW_DIFFICULTY bits à zéro en tête.
__sg_ok : ts:HMAC(secret, "sg_ok:ts") — 30 jours, saute le pre-flight PoW
__sg_authorized: ts:HMAC(secret, "sg_authorized:ts") — 120 s, protège les assets /assets/*
Constant Summary collapse
- POW_OK_TTL_MS =
30 * 24 * 3600 * 1000
- AUTHORIZED_TTL_MS =
120_000
Instance Method Summary collapse
-
#challenge ⇒ Object
Génère le challenge à injecter (window.__sg_pow).
-
#initialize(signing_secret, difficulty = 14, ttl_ms = 60_000) ⇒ Pow
constructor
A new instance of Pow.
- #sg_authorized_cookie ⇒ Object
- #sg_authorized_valid?(cookie_val) ⇒ Boolean
-
#sg_authorized_value ⇒ Object
Cookie __sg_authorized posé par handleRender après un render réussi (grant valide).
-
#sg_ok_cookie(proof) ⇒ String?
String Set-Cookie pour __sg_ok (posé par le middleware après une preuve valide).
- #sg_ok_valid?(cookie_val) ⇒ Boolean
-
#sg_ok_value ⇒ Object
Valeur du cookie __sg_ok (HMAC serveur, 30 j).
-
#valid?(proof) ⇒ Boolean
Vérifie un proof "ts:nonce" (fenêtre @ttl_ms, comptage de bits CORRIGÉ).
Constructor Details
#initialize(signing_secret, difficulty = 14, ttl_ms = 60_000) ⇒ Pow
Returns a new instance of Pow.
13 14 15 16 17 |
# File 'lib/shugoi/pow.rb', line 13 def initialize(signing_secret, difficulty = 14, ttl_ms = 60_000) @secret = signing_secret.to_s @difficulty = difficulty @ttl_ms = ttl_ms end |
Instance Method Details
#challenge ⇒ Object
Génère le challenge à injecter (window.__sg_pow).
20 21 22 23 |
# File 'lib/shugoi/pow.rb', line 20 def challenge ts = Utils.now_sec { ts: ts, salt: salt(ts), difficulty: @difficulty } end |
#sg_authorized_cookie ⇒ Object
85 86 87 88 |
# File 'lib/shugoi/pow.rb', line 85 def secure = production? ? "; Secure" : "" "__sg_authorized=#{}; Path=/; HttpOnly; SameSite=Strict; Max-Age=120#{secure}" end |
#sg_authorized_valid?(cookie_val) ⇒ Boolean
72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/shugoi/pow.rb', line 72 def () return false if @secret.empty? || .to_s.empty? ts_str, sig = .to_s.split(":", 2) return false if ts_str.nil? || sig.nil? ts = ts_str.to_i return false if ts.zero? return false if Utils.now_ms - ts * 1000 > AUTHORIZED_TTL_MS return false if ts * 1000 > Utils.now_ms + 60_000 Utils.secure_equals(sig, Utils.hmac_hex(@secret, "sg_authorized:#{ts_str}")) end |
#sg_authorized_value ⇒ Object
Cookie __sg_authorized posé par handleRender après un render réussi (grant valide).
67 68 69 70 |
# File 'lib/shugoi/pow.rb', line 67 def ts = Utils.now_sec "#{ts}:#{Utils.hmac_hex(@secret, "sg_authorized:#{ts}")}" end |
#sg_ok_cookie(proof) ⇒ String?
String Set-Cookie pour __sg_ok (posé par le middleware après une preuve valide).
60 61 62 63 64 |
# File 'lib/shugoi/pow.rb', line 60 def (proof) return nil unless valid?(proof) secure = production? ? "; Secure" : "" "__sg_ok=#{sg_ok_value}; Path=/; HttpOnly; SameSite=Lax; Max-Age=#{POW_OK_TTL_MS / 1000}#{secure}" end |
#sg_ok_valid?(cookie_val) ⇒ Boolean
45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/shugoi/pow.rb', line 45 def sg_ok_valid?() return false if @secret.empty? || .to_s.empty? ts_str, sig = .to_s.split(":", 2) return false if ts_str.nil? || sig.nil? ts = ts_str.to_i return false if ts.zero? return false if Utils.now_ms - ts * 1000 > POW_OK_TTL_MS return false if ts * 1000 > Utils.now_ms + 60_000 Utils.secure_equals(sig, Utils.hmac_hex(@secret, "sg_ok:#{ts_str}")) end |
#sg_ok_value ⇒ Object
Valeur du cookie __sg_ok (HMAC serveur, 30 j).
40 41 42 43 |
# File 'lib/shugoi/pow.rb', line 40 def sg_ok_value ts = Utils.now_sec "#{ts}:#{Utils.hmac_hex(@secret, "sg_ok:#{ts}")}" end |
#valid?(proof) ⇒ Boolean
Vérifie un proof "ts:nonce" (fenêtre @ttl_ms, comptage de bits CORRIGÉ).
26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/shugoi/pow.rb', line 26 def valid?(proof) return false if proof.to_s.empty? || @secret.empty? ts_str, solution = proof.to_s.split(":", 2) return false if ts_str.nil? || solution.nil? ts = ts_str.to_i return false if ts.zero? return false if (Utils.now_ms - ts * 1000).abs > @ttl_ms digest = Utils.sha256_hex("#{salt(ts_str)}:#{solution}") Utils.leading_zero_bits(digest) >= @difficulty end |