From 26eb59e00d9596adf69e3a3d5f8cb899f8a1da0a Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Thu, 22 Nov 2018 13:26:08 -0600 Subject: [PATCH] Add text CAPTCHA --- src/invidious.cr | 87 +++++++++++++++++++++++++++-------- src/invidious/views/login.ecr | 20 +++++++- 2 files changed, 85 insertions(+), 22 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 76d772dc..cbba17c1 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -16,6 +16,7 @@ require "crypto/bcrypt/password" require "detect_language" +require "digest/md5" require "kemal" require "openssl/hmac" require "option_parser" @@ -82,10 +83,11 @@ PG_URL = URI.new( path: CONFIG.db[:dbname], ) -PG_DB = DB.open PG_URL -YT_URL = URI.parse("https://www.youtube.com") -REDDIT_URL = URI.parse("https://www.reddit.com") -LOGIN_URL = URI.parse("https://accounts.google.com") +PG_DB = DB.open PG_URL +YT_URL = URI.parse("https://www.youtube.com") +REDDIT_URL = URI.parse("https://www.reddit.com") +LOGIN_URL = URI.parse("https://accounts.google.com") +TEXTCAPTCHA_URL = URI.parse("http://textcaptcha.com/omarroth@hotmail.com.json") crawl_threads.times do spawn do @@ -632,8 +634,25 @@ get "/login" do |env| account_type = env.params.query["type"]? account_type ||= "invidious" + captcha_type = env.params.query["captcha"]? + captcha_type ||= "image" + if account_type == "invidious" - captcha = generate_captcha(HMAC_KEY, PG_DB) + if captcha_type == "image" + captcha = generate_captcha(HMAC_KEY, PG_DB) + else + response = HTTP::Client.get(TEXTCAPTCHA_URL).body + response = JSON.parse(response) + + tokens = response["a"].as_a.map do |answer| + create_response(answer.as_s, "sign_in", HMAC_KEY, PG_DB) + end + + text_captcha = { + question: response["q"].as_s, + tokens: tokens, + } + end end tfa = env.params.query["tfa"]? @@ -827,27 +846,55 @@ post "/login" do |env| end elsif account_type == "invidious" answer = env.params.body["answer"]? + text_answer = env.params.body["text_answer"]? - if !answer - error_message = "CAPTCHA is a required field" - next templated "error" - end + if answer + answer = answer.lstrip('0') + answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer) - answer = answer.lstrip('0') - answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer) + challenge = env.params.body["challenge"]? + token = env.params.body["token"]? - challenge = env.params.body["challenge"]? - token = env.params.body["token"]? + begin + validate_response(challenge, token, answer, "sign_in", HMAC_KEY, PG_DB) + rescue ex + if ex.message == "Invalid user" + error_message = "Invalid answer" + else + error_message = ex.message + end - begin - validate_response(challenge, token, answer, "sign_in", HMAC_KEY, PG_DB) - rescue ex - if ex.message && ex.message == "Invalid user" - error_message = "Invalid CAPTCHA response" - else - error_message = ex.message + next templated "error" + end + elsif text_answer + text_answer = Digest::MD5.hexdigest(text_answer.downcase.strip) + + challenges = env.params.body.select { |k, v| k.match(/text_challenge\d+/) } + tokens = env.params.body.select { |k, v| k.match(/text_token\d+/) } + + found_valid_captcha = false + + error_message = "Invalid CAPTCHA" + challenges.each_with_index do |challenge, i| + begin + challenge = challenge[1] + token = tokens[i][1] + validate_response(challenge, token, text_answer, "sign_in", HMAC_KEY, PG_DB) + found_valid_captcha = true + rescue ex + if ex.message == "Invalid user" + error_message = "Invalid answer" + else + error_message = ex.message + end + end end + if !found_valid_captcha + next templated "error" + end + else + error_message = "CAPTCHA is a required field" next templated "error" end diff --git a/src/invidious/views/login.ecr b/src/invidious/views/login.ecr index 0243d900..69f04ed2 100644 --- a/src/invidious/views/login.ecr +++ b/src/invidious/views/login.ecr @@ -24,11 +24,27 @@ + <% if captcha_type == "image" %> - - + + + + <% else %> + <% text_captcha.not_nil![:tokens].each_with_index do |token, i| %> + + + <% end %> + + + + + <% end %>