Remove text captcha due to textcaptcha.com being down

Fixes https://github.com/iv-org/invidious/issues/5295

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:
Fijxu 2025-05-17 13:17:26 -04:00
parent 03f89be929
commit 6376fd55db
No known key found for this signature in database
GPG Key ID: 32C1DDF333EDA6A4
3 changed files with 11 additions and 96 deletions

View File

@ -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

View File

@ -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

View File

@ -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") %>