mirror of
https://github.com/iv-org/invidious.git
synced 2025-05-19 13:01:11 +00:00
Remove text captchas from Invidious (#5308)
textcaptcha.com seems to be down since April and it does not appear that service will be restored. Text captchas can be easily automated using free LLMs, so keeping the text captcha is more like a gate to create accounts in mass on public Invidious instances. It also gives headaches like bots automating account creation to modify the videos that appear popular page of each instance (since the popular page is based on the subscriptions of the registered users).
This commit is contained in:
commit
2c857b5ab6
@ -64,8 +64,6 @@
|
|||||||
"User ID": "User ID",
|
"User ID": "User ID",
|
||||||
"Password": "Password",
|
"Password": "Password",
|
||||||
"Time (h:mm:ss):": "Time (h:mm:ss):",
|
"Time (h:mm:ss):": "Time (h:mm:ss):",
|
||||||
"Text CAPTCHA": "Text CAPTCHA",
|
|
||||||
"Image CAPTCHA": "Image CAPTCHA",
|
|
||||||
"Sign In": "Sign In",
|
"Sign In": "Sign In",
|
||||||
"Register": "Register",
|
"Register": "Register",
|
||||||
"E-mail": "E-mail",
|
"E-mail": "E-mail",
|
||||||
|
@ -21,9 +21,6 @@ module Invidious::Routes::Login
|
|||||||
account_type = env.params.query["type"]?
|
account_type = env.params.query["type"]?
|
||||||
account_type ||= "invidious"
|
account_type ||= "invidious"
|
||||||
|
|
||||||
captcha_type = env.params.query["captcha"]?
|
|
||||||
captcha_type ||= "image"
|
|
||||||
|
|
||||||
templated "user/login"
|
templated "user/login"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -88,34 +85,14 @@ module Invidious::Routes::Login
|
|||||||
password = password.byte_slice(0, 55)
|
password = password.byte_slice(0, 55)
|
||||||
|
|
||||||
if CONFIG.captcha_enabled
|
if CONFIG.captcha_enabled
|
||||||
captcha_type = env.params.body["captcha_type"]?
|
|
||||||
answer = env.params.body["answer"]?
|
answer = env.params.body["answer"]?
|
||||||
change_type = env.params.body["change_type"]?
|
|
||||||
|
|
||||||
if !captcha_type || change_type
|
|
||||||
if change_type
|
|
||||||
captcha_type = change_type
|
|
||||||
end
|
|
||||||
captcha_type ||= "image"
|
|
||||||
|
|
||||||
account_type = "invidious"
|
account_type = "invidious"
|
||||||
|
|
||||||
if captcha_type == "image"
|
|
||||||
captcha = Invidious::User::Captcha.generate_image(HMAC_KEY)
|
captcha = Invidious::User::Captcha.generate_image(HMAC_KEY)
|
||||||
else
|
|
||||||
captcha = Invidious::User::Captcha.generate_text(HMAC_KEY)
|
|
||||||
end
|
|
||||||
|
|
||||||
return templated "user/login"
|
|
||||||
end
|
|
||||||
|
|
||||||
tokens = env.params.body.select { |k, _| k.match(/^token\[\d+\]$/) }.map { |_, v| v }
|
tokens = env.params.body.select { |k, _| k.match(/^token\[\d+\]$/) }.map { |_, v| v }
|
||||||
|
|
||||||
answer ||= ""
|
if answer
|
||||||
captcha_type ||= "image"
|
|
||||||
|
|
||||||
case captcha_type
|
|
||||||
when "image"
|
|
||||||
answer = answer.lstrip('0')
|
answer = answer.lstrip('0')
|
||||||
answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer)
|
answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer)
|
||||||
|
|
||||||
@ -124,27 +101,8 @@ module Invidious::Routes::Login
|
|||||||
rescue ex
|
rescue ex
|
||||||
return error_template(400, ex)
|
return error_template(400, ex)
|
||||||
end
|
end
|
||||||
else # "text"
|
else
|
||||||
answer = Digest::MD5.hexdigest(answer.downcase.strip)
|
return templated "user/login"
|
||||||
|
|
||||||
if tokens.empty?
|
|
||||||
return error_template(500, "Erroneous CAPTCHA")
|
|
||||||
end
|
|
||||||
|
|
||||||
found_valid_captcha = false
|
|
||||||
error_exception = Exception.new
|
|
||||||
tokens.each do |tok|
|
|
||||||
begin
|
|
||||||
validate_request(tok, answer, env.request, HMAC_KEY, locale)
|
|
||||||
found_valid_captcha = true
|
|
||||||
rescue ex
|
|
||||||
error_exception = ex
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if !found_valid_captcha
|
|
||||||
return error_template(500, error_exception)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -4,8 +4,6 @@ struct Invidious::User
|
|||||||
module Captcha
|
module Captcha
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
private TEXTCAPTCHA_URL = URI.parse("https://textcaptcha.com")
|
|
||||||
|
|
||||||
def generate_image(key)
|
def generate_image(key)
|
||||||
second = Random::Secure.rand(12)
|
second = Random::Secure.rand(12)
|
||||||
second_angle = second * 30
|
second_angle = second * 30
|
||||||
@ -60,19 +58,5 @@ struct Invidious::User
|
|||||||
tokens: {generate_response(answer, {":login"}, key, use_nonce: true)},
|
tokens: {generate_response(answer, {":login"}, key, use_nonce: true)},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_text(key)
|
|
||||||
response = make_client(TEXTCAPTCHA_URL, &.get("/github.com/iv.org/invidious.json").body)
|
|
||||||
response = JSON.parse(response)
|
|
||||||
|
|
||||||
tokens = response["a"].as_a.map do |answer|
|
|
||||||
generate_response(answer.as_s, {":login"}, key, use_nonce: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
question: response["q"].as_s,
|
|
||||||
tokens: tokens,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -25,44 +25,17 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if captcha %>
|
<% if captcha %>
|
||||||
<% case captcha_type when %>
|
|
||||||
<% when "image" %>
|
|
||||||
<% captcha = captcha.not_nil! %>
|
<% captcha = captcha.not_nil! %>
|
||||||
<img style="width:50%" src='<%= captcha[:question] %>'/>
|
<img style="width:50%" src='<%= captcha[:question] %>'/>
|
||||||
<% captcha[:tokens].each_with_index do |token, i| %>
|
<% captcha[:tokens].each_with_index do |token, i| %>
|
||||||
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">
|
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">
|
||||||
<% end %>
|
<% end %>
|
||||||
<input type="hidden" name="captcha_type" value="image">
|
|
||||||
<label for="answer"><%= translate(locale, "Time (h:mm:ss):") %></label>
|
<label for="answer"><%= translate(locale, "Time (h:mm:ss):") %></label>
|
||||||
<input type="text" name="answer" type="text" placeholder="h:mm:ss">
|
<input type="text" name="answer" type="text" placeholder="h:mm:ss">
|
||||||
<% else # "text" %>
|
|
||||||
<% captcha = captcha.not_nil! %>
|
|
||||||
<% captcha[:tokens].each_with_index do |token, i| %>
|
|
||||||
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">
|
|
||||||
<% end %>
|
|
||||||
<input type="hidden" name="captcha_type" value="text">
|
|
||||||
<label for="answer"><%= captcha[:question] %></label>
|
|
||||||
<input type="text" name="answer" type="text" placeholder="<%= translate(locale, "Answer") %>">
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
|
<button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Register") %>
|
<%= translate(locale, "Register") %>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<% case captcha_type when %>
|
|
||||||
<% when "image" %>
|
|
||||||
<label>
|
|
||||||
<button type="submit" name="change_type" class="pure-button pure-button-primary" value="text">
|
|
||||||
<%= translate(locale, "Text CAPTCHA") %>
|
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
<% else # "text" %>
|
|
||||||
<label>
|
|
||||||
<button type="submit" name="change_type" class="pure-button pure-button-primary" value="image">
|
|
||||||
<%= translate(locale, "Image CAPTCHA") %>
|
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
<% end %>
|
|
||||||
<% else %>
|
<% else %>
|
||||||
<button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
|
<button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Sign In") %>/<%= translate(locale, "Register") %>
|
<%= translate(locale, "Sign In") %>/<%= translate(locale, "Register") %>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user