JavaのCloudFlare ScrapeShieldバイパス(Android)





ある時点で、ヘッダーに記載されている問題を解決する必要がありました。 私は長い間、自明なことを書くべきかどうかを考え、最終的には誰かに役立つかもしれないと決めました。



その理由は非常に簡単です- 非常にニッチなサイトの Androidクライアントの作成者として、同時にその管理者の数や共同創業者の数には含まれていません。 したがって、サイト管理者が実際に発効するまで、サイト管理者の決定を知りません。



少し前まで、このサイトでDDoS攻撃が始まり、管理者はCloudFlareからのDDoS保護をオンにしました。 したがって、以前はPOST + Cookieを介して標準の認証メカニズムを使用していたクライアントアプリケーションは、ユーザーの認証を停止しました。 管理者とのコミュニケーションは何にもつながりませんでした-「何ができるのか、モバイルクライアントがなければ、まったくできないよりはましです」。



当然、これはすべて評価に影響を及ぼし始め、非常に質素なレビューを引き起こしました。



解決策は、ページ上のブラウザの動作をシミュレートすることにより、CloudFlare保護をバイパスすることでした。 この特定のケースでは、CloudFlareは、ブラウザーが実行するハッシュ、キー、およびランダムなJavaScriptコードを使用し(難読化されたゴミのように見えるいくつかの算術演算を計算)、後でハッシュとキーと共に結果の数値を検証ページに送信します。 したがって、私たちのタスクは、javascriptタスクをインターセプトし、何らかの方法でそれを解決し、推測が正しいかどうかを尋ねることです。 その場合、パン(cf_clearance cookies)を取得します。 そうでない場合、503を取得します。



検索エンジンを調べてみると、 プロジェクトが非常によく似たものにつながるリンクが1つだけありました。 node.jsまたはPyExecJSの別の互換性のあるプロバイダーを使用してPythonで記述されています。 Pythonに敬意を払って、軽量のニッチアプリケーションでPythonを使用するのは不合理な贅沢であり、その統合には何時間もかかりました。 ソルバーコードをJavaで書き直すために戦略的な決定が行われました。



コードの作成中に発生したいくつかのメモ/あいまいさ:





最終バージョンは以下です。 一緒にホイップされますが、すべてがどのように機能するかの基本的なアイデアを提供します。

private final static Pattern OPERATION_PATTERN = Pattern.compile("setTimeout\\(function\\(\\)\\{\\s+(var t,r,a,f.+?\\r?\\n[\\s\\S]+?a\\.value =.+?)\\r?\\n"); private final static Pattern PASS_PATTERN = Pattern.compile("name=\"pass\" value=\"(.+?)\""); private final static Pattern CHALLENGE_PATTERN = Pattern.compile("name=\"jschl_vc\" value=\"(\\w+)\""); abstract public HttpResponse getPage(URI url, HashMap<String, String> headers) throws IOException; abstract public CookieStore getCookieStore(); public boolean cloudFlareSolve(String responseString) { //  Rhino Context rhino = Context.enter(); try { String domain = "www.example.com"; // CF      Thread.sleep(5000); //   Matcher operationSearch = OPERATION_PATTERN.matcher(responseString); Matcher challengeSearch = CHALLENGE_PATTERN.matcher(responseString); Matcher passSearch = PASS_PATTERN.matcher(responseString); if(!operationSearch.find() || !passSearch.find() || !challengeSearch.find()) return false; String rawOperation = operationSearch.group(1); //  String challengePass = passSearch.group(1); //  String challenge = challengeSearch.group(1); //  //    String operation = rawOperation .replaceAll("a\\.value =(.+?) \\+ .+?;", "$1") .replaceAll("\\s{3,}[az](?: = |\\.).+", ""); String js = operation.replace("\n", ""); rhino.setOptimizationLevel(-1); //    rhino    Android Scriptable scope = rhino.initStandardObjects(); //    // either do or die trying int result = ((Double) rhino.evaluateString(scope, js, "CloudFlare JS Challenge", 1, null)).intValue(); String answer = String.valueOf(result + domain.length()); //   javascript challenge final List<NameValuePair> params = new ArrayList<>(3); params.add(new BasicNameValuePair("jschl_vc", challenge)); params.add(new BasicNameValuePair("pass", challengePass)); params.add(new BasicNameValuePair("jschl_answer", answer)); HashMap<String, String> headers = new HashMap<>(1); headers.put("Referer", "http://" + domain + "/"); // url ,      String url = "http://" + domain + "/cdn-cgi/l/chk_jschl?" + URLEncodedUtils.format(params, "windows-1251"); HttpResponse response = getPage(URI.create(url), headers); if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { //    ,   Referer response.getEntity().consumeContent(); //       return true; } } catch (Exception e) { return false; } finally { Context.exit(); //  Rhino } return false; } private void syncCookiesWithWebViews() { List<Cookie> cookies = getCookieStore().getCookies(); CookieManager cookieManager = CookieManager.getInstance(); // CookieManager    cookies  WebView for (Cookie cookie : cookies) { String cookieString = cookie.getName() + "=" + cookie.getValue() + "; domain=" + cookie.getDomain(); cookieManager.setCookie("diary.ru", cookieString); } }
      
      







クライアントコードはGPLv3の下で公開されているため、CloudFlareがそれを見つける可能性が高く、アルゴリズムの変更につながります。 それでも、私はあいまいさによるセキュリティの原則を支持しておらず、DDoSが低下する前にモバイルユーザーを許可するという問題を解決できました。



ご清聴ありがとうございました。 コメント内の質問/コメント。



All Articles