background blurbackground mobile blur

1/1/1970

প্রক্সির পেছনে Foony-কে কীভাবে কাজ করালাম

হ্যালো! আমি বহুদিন ধরেই জানি যে ওয়েব প্রক্সি ওয়েবসাইটগুলোর জন্য সামঞ্জস্যতার সমস্যা তৈরি করে। তবে প্রক্সিতে Foony-এর সাপোর্ট কুখ্যাতভাবে খারাপ ছিল, এবং Foony-এর প্রক্সি সামঞ্জস্যতা সমাধান করা বেশ জটিল ছিল।

এটা “Foony অদ্ভুত API ব্যবহার করে” এমন কোনো সমস্যাও না (যদিও আমরা করি)। এটা ছিল কয়েকটি বিষয়ের সমন্বয়:

  • প্রক্সিগুলো এমন জায়গায় আক্রমণাত্মক স্ট্রিং রিরাইটিং করছিল যেখানে একদমই করা উচিত না।
  • প্রক্সিগুলো মূল সাইট ডোমেইনকে “অন্যান্য” ডোমেইনের (CDN, অ্যাসেট হোস্ট, ইত্যাদি) থেকে আলাদাভাবে ট্রিট করছিল।
  • এবং কঠোর বাস্তবতা হলো কিছু প্রক্সি আধুনিক ওয়েব অ্যাপ সাপোর্ট করতেই পারে না (HTTPS শুদ্ধতা, WebSockets, ইত্যাদি)।

আমরা প্রতিটা প্রক্সির সাথে কাজ করি না, কিন্তু এখন অন্তত croxyproxy এবং proxyorb-এর সাথে কাজ করি, যেটাই লক্ষ্য ছিল।

নিচে আমি ব্যাখ্যা করছি কী ভেঙেছিল, কেন ভেঙেছিল, এবং যে ফিক্সগুলো আসলেই গুরুত্বপূর্ণ ছিল।


পাস ১: বৈধ, কিন্তু ভাঙা Three.js শেডার

লক্ষণ

যখন আমি croxyproxy চেষ্টা করেছিলাম, আমি 8 Ball Pool বা Foony-এর অন্য কোনো three.js গেম খেলতে পারছিলাম না। আমি বারবার Three.js-এ শেডার কম্পাইলেশন ফেইলিওর পাচ্ছিলাম, যেমন:

  • “Shader Error 1282 - VALIDATE_STATUS false”

এই মেসেজটা প্রায় পুরোপুরি অকার্যকর। সাধারণত এর মানে “তোমার শেডার অবৈধ, শুভকামনা।” দারুণ। যদি কখনো ভাবো Foony-তে আমি কেন প্রতিটা এররের জন্য ইউনিক এরর মেসেজ ব্যবহার করি, এটাই সেই কারণ। এটা সমস্যা চিহ্নিত করতে সাহায্য করে, “কোড ভেঙেছে, ঠিক করো” বলার পরিবর্তে।

কিন্তু পুরোপুরি বৈধ three.js শেডার কেন ভাঙছিল? কী হচ্ছে?

আসল কারণ: প্রক্সি layout(location = N)-কে নষ্ট করছে

Three.js এমন লেআউট কোয়ালিফায়ারসহ GLSL তৈরি করে:

layout(location = 0) in vec3 position;

কিছু প্রক্সি JavaScript-এর location API-র মতো দেখতে যেকোনো কিছুকে নাইভ গ্লোবাল স্ট্রিং রিপ্লেসমেন্ট দিয়ে রিরাইট করার চেষ্টা করে। এটা JS-এই খারাপ, কিন্তু তারা শেডার সোর্স স্ট্রিংয়ের ভেতরেও এটা করছিল। মনে হয় AST পার্সিং তাদের জন্য বেশি ব্যয়বহুল।

তাই শেডার সোর্স এমন কিছুতে নষ্ট হয়ে যাচ্ছিল:

layout(__cpLocation = 0) in vec3 position;

সেখানে আইডেন্টিফায়ার অবশ্যই location হতে হবে। অন্য কিছু অবৈধ GLSL, এবং কম্পাইলার সেটা প্রত্যাখ্যান করে। (GLSL-এ Layout Qualifiers)

এটা Three.js-এর সমস্যা শুধু এই অর্থে যে Three.js ডায়নামিকভাবে শেডার তৈরি করে, এবং আমরা সেগুলো রানটাইমে WebGL-এ পাঠাই। আসল বাগটা প্রক্সির রিরাইটিং কৌশলে।

কেন আমি “প্রক্সি ফিক্স” করিনি

একটা সাদাসিধা পদ্ধতি হবে croxyproxy-এর location রিপ্লেসমেন্ট স্ট্রিং __cpLocation খুঁজে সেটাকে location দিয়ে রিপ্লেস করা। কিন্তু বিভিন্ন প্রক্সি ভিন্ন ভিন্ন রিপ্লেসমেন্ট নাম ব্যবহার করে। কেউ __cpLocation ব্যবহার করে, কেউ অন্য অদ্ভুত আইডেন্টিফায়ার। তাই “__cpLocation-কে আবার location-এ রিপ্লেস করো” এমন হার্ডকোড ফিক্স ভঙ্গুর।

আমার প্রয়োজন ছিল:

  • একটা জেনেরিক ফিক্স (কোনো প্রক্সি আইডেন্টিফায়ার হার্ডকোড ছাড়া)।
  • এমন একটা ফিক্স যেটা কাজ করবে এমনকি যদি প্রক্সি আমার JavaScript-এর শব্দ location-ও রিরাইট করে।

base64 ট্রিক: প্রক্সি থেকে location শব্দটা লুকানো

যদি প্রক্সি দেখা প্রতিটা লিটারাল location-কে রিরাইট করে, সবচেয়ে সহজ পদক্ষেপ হলো location ব্যবহারই না করা। সহজ ব্যাপার। আমি Lua-তে এর আগে এ ধরনের ট্রিক দেখেছি যখন RestedXP-এর গাইড প্রোটেকশন সিস্টেম রিভার্স-ইঞ্জিনিয়ার করেছিলাম (যদি ঠিক মনে থাকে, তারা BNGetInfo-এর ব্যবহার অবফাসকেট করে, যেমন _G("\x42\x4E\x47\x65\x74\x49\x6E\x66\x6F"))।

এই ট্রিক অবশ্যই JavaScript-এও কাজ করে। client/index.html-এ আমি রানটাইমে নিচেরটা ডিকোড করি:

// যেহেতু এই প্রক্সিগুলো প্রতিটা `location` রিপ্লেস করার চেষ্টা করে, আমরা base64 এনকোডেড স্ট্রিং ব্যবহার করি।
const suffix = 'pb24=';
const locStr = atob('bG9jYXR' + suffix); // "location"
const loc = window[locStr]; // window.location

atob() ঘটে প্রক্সি তার HTML/JS রিরাইটিং করার পরে, তাই এটা স্ট্রিংটাকে “প্রি-করাপ্ট” করতে পারে না। আমি স্ট্রিংটাকে দুই ভাগে ভাগ করেছি যেন ডিটেক্ট করা আরো কঠিন হয়, এবং 'atob' ব্যবহার করি কারণ আমি পারি, তবে String.fromCharCode বা hex-escaping window['\x6c\x6f\x63\x61\x74\x69\x6f\x6e']-ও কাজ করতে পারে।

ভাঙা শেডার প্যাটার্ন সবসময় কাঠামোগতভাবে একই:

layout(<something> = <number>)

তাই আমি সেটাকে জেনেরিকভাবে ম্যাচ করি এবং <something>-কে সঠিক আইডেন্টিফায়ার দিয়ে রিপ্লেস করি:

source.replace(/layout\s*\(\s*[^=)]+\s*=\s*(\d+)\s*\)/g, 'layout(' + locStr + ' = $1)');

WebGL হুক: shaderSource প্যাচ করা (WebGL1 + WebGL2)

যেহেতু Three.js gl.shaderSource(shader, source) কল করে, আমি shaderSource নিজেই প্যাচ করি:

const originalShaderSource = WebGLRenderingContext.prototype.shaderSource;

Object.defineProperty(WebGLRenderingContext.prototype, 'shaderSource', {
  value: function (shader, source) {
    return originalShaderSource.call(this, shader, fixCorruptedShaderSource(source));
  },
  writable: true,
  configurable: true,
});

এবং আমি একই প্যাচ WebGL2RenderingContext-এও প্রয়োগ করি যদি সেটা থাকে।

একবার সেটা জায়গামতো হয়ে গেলে, শেডার কম্পাইলেশন এররগুলো গায়েব হয়ে গেল। এই পর্যায়ে croxyproxy কাজ করছিল কিন্তু proxyorb এখনো ফেইল হচ্ছিল। কেন?! সেটাও তো একইভাবে কাজ করার কথা?


পাস ২: দ্বিতীয় সমস্যা (ডোমেইন) এবং কেন foony.io সরিয়ে দেওয়াতে সব সহজ হয়ে গেল

Foony ঐতিহাসিকভাবে দুটো ডোমেইন ব্যবহার করত, অন্তত গত মাস ধরে:

  • মূল সাইটের জন্য foony.com
  • স্ট্যাটিক অ্যাসেটের জন্য foony.io

মূল কারণ ছিল ব্যবহারিক: কুকি-হীন ডোমেইন থেকে অ্যাসেট সার্ভ করা প্রতিটা স্ট্যাটিক ফাইল রিকোয়েস্টে কুকি হেডার আপলোড ব্লোট এড়ায়। এটা চমৎকার, কিন্তু আপনি যতটা মনে করেন তার তুলনায় ততটা প্রয়োজনীয় না, কারণ HTTP/2 হেডারের জন্য ওয়্যারে পাঠানো বাইট কমাতে HPACK ব্যবহার করে।

স্বাভাবিক ব্রাউজিংয়ে এটা একটা বৈধ অপটিমাইজেশন।

প্রক্সির পেছনে এটা ভাঙনের একটা প্রধান উৎস হয়ে উঠেছিল। আর Foony-এর ইউজারবেস প্রক্সি ভালোবাসে। দীর্ঘশ্বাস

প্রক্সি “মূল সাইট”-কে “অন্যান্য সাইট” থেকে ভিন্নভাবে ট্রিট করে

অনেক প্রক্সি “এই এক পেজ / ডোমেইন প্রক্সি করো” এর জন্য অপটিমাইজড। তারা সফলভাবে মূল HTML লোড করবে, স্ক্রিপ্ট ইনজেক্ট করবে, নিজস্ব ServiceWorker রেজিস্টার করবে, ইত্যাদি।

কিন্তু যখন অ্যাপ অন্য একটা অরিজিন (যেমন foony.io) থেকে অ্যাসেট টানতে শুরু করে, আপনি নানা ধরনের মজাদার ভাঙনে পড়েন:

  • ServiceWorker ইন্টারসেপশন ফেইলিওর যেমন:
    • “ServiceWorker intercepted the request and encountered an unexpected error”
    • “Loading failed for the module with source”
  • প্রক্সি অবকাঠামো দ্বারা প্রয়োজনীয় কোয়েরি প্যারামস (এবং দুর্ঘটনাবশত স্ট্রিপ করা সহজ)।
  • অ্যাসেট রিকোয়েস্ট প্রক্সির অভ্যন্তরীণ রাউটিং মেটাডেটা হারিয়ে ফেলে।
  • অদ্ভুত প্রক্সি যারা পুরো রিকোয়েস্টকে generic-php-slug.php?someQueryParam=hugeEncodedString দিয়ে রিপ্লেস করে (হ্যাঁ, আমি সেটা সাপোর্ট করার ঝামেলা নেইনি)।

এই প্রক্সির অভ্যন্তরীণ মেকানিজম কোয়েরি প্যারামস / URL এনকোডিংয়ের উপর নির্ভর করে, এবং সেগুলো বেশ ভঙ্গুর।

একটা স্পষ্ট উদাহরণ ছিল এমন একটা অ্যাসেট URL:

https://<proxy-ip>/assets/firebase-<hash>.js?__pot=aHR0cHM6Ly9mb29ueS5jb20

?__pot=... হলো প্রক্সির নিজস্ব রাউটিং/স্টেট যেটা প্রক্সিকে বলে রিকোয়েস্টটা কোন ডোমেইনের জন্য। যদি এটা স্ট্রিপ করেন, প্রক্সি সঠিকভাবে রিকোয়েস্ট রিজলভ করতে পারে না, এবং আপনি ServiceWorker এরর পাথে পৌঁছান।

“রিসোর্স স্বাপিং” উদ্ধারে (এবং কেন এটা দ্রুত জটিল হয়ে গেল)

এক পর্যায়ে, আমি একটা ওয়ার্কঅ্যারাউন্ড চেষ্টা করেছিলাম: ডিটেক্ট করি “আমরা প্রক্সি হচ্ছি,” তারপর যেকোনো foony.io রিসোর্স URL-কে বর্তমান অরিজিনে স্বাপ করি যেন প্রক্সি সবকিছু একই অরিজিন হিসেবে দেখে।

এটা যুক্তিসঙ্গত শোনায়, এবং croxyproxy-এর জন্য কাজ করেছিল, কিন্তু এটা অনেক জটিলতা যোগ করেছিল:

  • HTML-এ ইতিমধ্যে থাকা link এবং script ট্যাগগুলো রিপ্লেস করতে হবে।
  • ডায়নামিকভাবে ইনজেক্ট করা ট্যাগগুলো (modulepreload, stylesheet, ইত্যাদি) সামলানোর জন্য MutationObserver দরকার।
  • প্রক্সির কোয়েরি প্যারামিটার সংরক্ষণ করতে হবে, নাহয় তাদের রাউটিং ভাঙবে। এবং বিভিন্ন প্রক্সি এটা ভিন্নভাবে করে। অবশ্যই করবে।
  • এবং আপনাকে এখনো লজিকটাকে জেনেরিক রাখতে হবে (কোনো প্রক্সি-নির্দিষ্ট গ্লোবাল ছাড়া) যেন কোডটা একটা ফোলা ময়লা-আগুনে পরিণত না হয়।

এখানেই “base64 ট্রিক” আবার আসে: এমনকি আমার নিজের JavaScript-এও আমাকে লিটারাল স্ট্রিং location সম্পর্কে সতর্ক থাকতে হয়েছিল কারণ প্রক্সি সেটা রিরাইট করতে পারে।

CroxyProxy-এর ইনজেক্টেড স্ক্রিপ্ট রিভার্স-ইঞ্জিনিয়ারিং

এই পর্যায়ে আমি কৌতূহলী হলাম: প্রক্সি আসলে আমার পেজে কী করছে? নিজের বিজ্ঞাপন ইনজেক্ট করছে? আরো খারাপ কিছু?

CroxyProxy-এর ক্লায়েন্ট-সাইড স্ক্রিপ্ট ভারীভাবে অবফাসকেটেড।

(new Function(new TextDecoder('utf-8').decode(new Uint8Array((atob('NjY3NTZlN...')).match(/.{1,2}/g).map(b => parseInt(b, 16))))))();

যেটা চালালে এর ফলাফল হয়:

function a0_0x5ebf(_0x213dc9,_0x1c49b6){var _0x4aa7c1=a0_0x4274();return a0_0x5ebf=function(_0x159600,_0x51d898){_0x159600=...

এর উপর ভিত্তি করে মনে হচ্ছে croxyproxy এই অবফাসকেশনের জন্য Obfuscator.io ব্যবহার করছে। যেটা সৌভাগ্যক্রমে webcrack দিয়ে ডিঅবফাসকেট করা যথেষ্ট সহজ।

এর ফলে অনেক বেশি পঠনযোগ্য JavaScript পাওয়া যায়:

((_0x15ca2c, _0x489eaa) => {
  if (typeof module == "object" && module.exports) {
    module.exports = _0x489eaa(require("./punycode"), require("./IPv6"), require("./SecondLevelDomains"));
  } else if (typeof define == "function" && define.amd) {
    define(["./punycode", "./IPv6", "./SecondLevelDomains"], _0x489eaa);
  } else {
    _0x15ca2c.URI = _0x489eaa(_0x15ca2c.punycode, _0x15ca2c.IPv6, _0x15ca2c.SecondLevelDomains, _0x15ca2c);
  }
})(this, function (_0x724467, _0x275183, _0x219d84, _0x2dcc2c) {
  var _0x114c1e = _0x2dcc2c && _0x2dcc2c.URI;
  function _0x5a9187(_0x2fd4ea, _0x3bd460) {
    var _0x23a83f = arguments.length >= 1;
    if (!(this instanceof _0x5a9187)) {

বাহ। এখন আমরা দেখতে পাই এটা কী করছে। এবং... মনে হচ্ছে বেশিরভাগই ঠিক আছে। আমার ধারণা অবফাসকেশন মূলত প্রক্সি ডিটেকশন প্রতিরোধে সাহায্য করার জন্য। বেশিরভাগই

আমাদের কিছু বিজ্ঞাপন / UI ইনজেকশন আছে:

    Bi(_0x308e2f) {
      console.log("Ads: " + _0x331b11.showAds);
      console.log("Ad codes: " + !!_0x331b11.adsJson);
      console.log("Adblock: " + _0x308e2f);
      _0x34fa18.document.body.insertAdjacentHTML("afterbegin", _0x331b11.modal);
      if (_0x331b11.header) {
        if (_0x331b11.header) {
          this.Ri(_0x308e2f);
        }
        [...document.querySelectorAll("#__cpsHeader a")].forEach(_0xcce595 => {
          _0xcce595.addEventListener("click", function (_0x3ef5f1) {
            if (this.target === "_blank") {
              _0x34fa18.open(this.href, "_blank").focus();
            } else {
              _0x34fa18.location.href = this.href;
            }
            _0x3ef5f1.stopImmediatePropagation();
            _0x3ef5f1.preventDefault();
          }, true);
        });
      }
      return this;
    }

আরো কিছু জায়গা আছে, কিন্তু মূলত এটা বিজ্ঞাপন দেখাচ্ছে, পপ-আন্ডার স্টাইলের বিজ্ঞাপনসহ। এটা FuckAdBlock-ও ব্যবহার করে।

স্ট্রিংগুলোর আসল রিপ্লেসমেন্ট কিন্তু সার্ভার-সাইডে হচ্ছে। এবং কে জানে সেখানে কী হচ্ছে।

যাই হোক, আপনি যদি আপনার অ্যাকাউন্ট নিরাপত্তার বিষয়ে চিন্তিত হন তাহলে আপনার একদমই ওয়েব প্রক্সি ব্যবহার করা উচিত না। যদি ব্যবহার করতেই হয়, আপনার কোনো PII / অ্যাকাউন্ট / ক্রয় তথ্য প্রবেশ করানো এড়িয়ে চলুন।

"রিসোর্স স্বাপিং" বিনে

আমি সিদ্ধান্ত নিলাম যে রিসোর্স স্বাপিং থেকে আসা জটিলতা, foony.io সাপোর্টের জন্য কোডের অন্যান্য অংশের জটিলতার সাথে মিলে, সুন্দর কুকি-হীন রিকোয়েস্টের ছোট নেটওয়ার্ক সাশ্রয়ের জন্য মূল্যবান ছিল না। foony.io গ্রহণের পর থেকে আমাদের গেমপ্লে কনভার্সনে অব্যাখ্যাত ড্রপও দেখছিলাম, তাই আমি সন্দেহ করি foony.io-এর সাথে আরো কিছু সমস্যা ছিল যা আমরা জানতাম না।

তাই আমি foony.io সরিয়ে দিলাম। অন্তত আপাতত।

foony.io CDN লজিক মুছে এবং সবকিছু foony.com-এ স্ট্যান্ডার্ডাইজ করার পরে, প্রক্সি সাপোর্ট নাটকীয়ভাবে সহজ হয়ে গেল:

  • একই অরিজিন থেকে অ্যাসেট লোড।
  • প্রক্সি ServiceWorker-কে ব্যাখ্যা করার জন্য কম “বিশেষ ক্ষেত্র”।
  • কম রিরাইটিং।
  • কম ভঙ্গুর কোড।

সংক্ষেপে, foony.io সরিয়ে দেওয়া একটা স্থাপত্যগত সরলীকরণ ছিল যা অদ্ভুত প্রক্সি আচরণের পৃষ্ঠতল কমিয়ে দিল।


পাস ৩: কী কাজ করে, কী করে না, এবং কেন

নিশ্চিত কাজ করা প্রক্সি

এই পর্যায়ে, Foony এই প্রক্সিগুলোর পেছনে কাজ করে:

  • croxyproxy
  • proxyorb

কিছু অন্যান্য প্রক্সিও সম্ভবত কাজ করে। আমি বাজি ধরছি বেশিরভাগ এখনো করে না। কিন্তু অন্তত যেগুলো লোকেরা গেম খেলতে ব্যবহার করে সেগুলো কাজ করে বলে মনে হচ্ছে।

কেন “সব প্রক্সি” না?

কিছু প্রক্সি কেবল আধুনিক মাল্টিপ্লেয়ার ওয়েব অ্যাপ সাপোর্ট করতে পারে না। উদাহরণ:

  • যেসব প্রক্সি HTTPS ঠিকমতো সাপোর্ট করে না।
  • যেসব প্রক্সি WebSockets ভেঙে দেয় বা ব্লক করে (Foony রিয়েল-টাইম নেটওয়ার্কিং ব্যবহার করে)। প্রযুক্তিগতভাবে আপনি এটা ওয়ার্কঅ্যারাউন্ড করতে পারবেন, কিন্তু সেটা জটিলতা যোগ করবে।
  • যেসব প্রক্সির ক্রস-অরিজিন রিকোয়েস্ট, হেডার, বা ServiceWorkers নিয়ে অনেক বেশি বিধিনিষেধ আছে।

মূল গ্রহণযোগ্য বিষয়

ওয়েব প্রক্সি খুবই অনিরাপদ

তারা মিডলওয়্যার যারা:

  • HTML রিরাইট করে
  • JavaScript রিরাইট করে
  • কখনো কখনো একটা ServiceWorker ইনজেক্ট করে
  • এবং রিকোয়েস্ট রাউট করতে প্রায়ই কোয়েরি প্যারামস / URL এনকোডিংয়ের উপর নির্ভর করে
  • নানাভাবে আপনার পেজে হস্তক্ষেপ করতে পারে

আমি অবাক হয়েছি কিছু প্রক্সি কত গভীরে যায়: তারা শেডার সোর্স স্ট্রিং, কমেন্ট, এবং ভগবান জানেন আর কী কী রিরাইট করবে।

কখনো কখনো সবচেয়ে ভালো ফিক্স স্থাপত্যগত

WebGL প্যাচ গেমগুলোকে আবার রেন্ডার করিয়েছিল, কিন্তু মাল্টি-ডোমেইন CDN কৌশল সরিয়ে দেওয়াতে প্রক্সি সাপোর্ট স্থিতিশীল থাকল

এটা একটা ভালো অনুস্মারক: চতুর অপটিমাইজেশন পুরোপুরি যুক্তিসঙ্গত হতে পারে যতক্ষণ না সেগুলো বৈরী মিডলওয়্যারের সাথে সংঘর্ষ করে। বা ইউজারের ব্রাউজার এক্সটেনশনের সাথে। বা Safari-এর সাথে। বা ভাষার সেটিংসের সাথে। বা অ্যাক্সেসিবিলিটি ফিচারের সাথে। বা সৌর শিখার সাথে। বা আসলে যেকোনো কিছুর সাথে।


উপসংহার

Foony এখন গুরুত্বপূর্ণ প্রক্সিগুলোর (croxyproxy এবং proxyorb) পেছনে কাজ করে, কোডবেসকে প্রক্সি-নির্দিষ্ট জগাখিচুড়িতে পরিণত না করেই:

  • একটা জেনেরিক Three.js শেডার ফিক্স (কোনো প্রক্সি-নির্দিষ্ট আইডেন্টিফায়ার ছাড়া)।
  • একটা সহজতর ডোমেইন কৌশল (সবখানে foony.com)।
8 Ball Pool online multiplayer billiards icon