MYPRO / README.md
DD8777's picture
Update README.md YAML header to sdk: gradio with app_file: app.py for ZeroGPU compatibility
04bdd3f
|
Raw
History Blame Contribute Delete
12.5 kB
---
title: MYPRO
emoji:
colorFrom: blue
colorTo: indigo
sdk: gradio
sdk_version: 4.44.0
app_file: app.py
pinned: false
---
# מעקף פרוקסי 2.0 (Etrog Bypass Proxy)
Reverse proxy מבוזר: **Cloudflare Worker** + **Service Worker** בדפדפן, לעקיפת סינון יתר (SNI/DPI/DNS) לצריכת תוכן חוקי (מנוי פעיל).
## כתובת פרוסה
https://etrog-bypass-proxy.miriklain616.workers.dev
| נתיב | תפקיד |
|------|--------|
| `/` | דף בית + ניווט |
| `/ytp?v=VIDEO_ID` | נגן YouTube יציב (מומלץ) |
| `/websearch?q=…&t=web\|images\|videos\|news&p=N` | חיפוש נייטיב: רשת / תמונות / וידאו / חדשות |
| `/api/websearch?q=…&t=…&p=N` | אותן תוצאות כ-JSON |
| `/x/p?d=<token>` | פרוקסי תמונות כללי (אטום ב-XOR כשה-SW פעיל) |
| `/cdn/<token>` | פרוקסי מוצפן (XOR) לכתובת יעד |
| `/api/m?v=…&i=18` | זרם וידאו YouTube (same-isolate) |
| `/sw.js` | Service Worker |
| `/import-cookies` | ייבוא עוגיות (YouTube / אתרים אחרים) |
| `/api/health` | בדיקת חיים |
### חיפוש — למה אין "אני לא רובוט"
הכלל היחיד: **המשתמש לעולם לא מגיע לדף של מנוע חיפוש זר.** כל תוצאה נאספת ב-Worker ומוצגת
בדף שלנו, ולכן אין CAPTCHA, אין באנר הסכמה ואין SPA שנשבר דרך הפרוקסי.
Google / Brave / Mojeek / Startpage / SearX מציגים אתגר ל-IP של דאטה-סנטר — ולכן **אינם בשימוש
כלל**, וגם הוסרו מהקיצורים בדף הבית.
| לשונית | מקור ראשי | גיבויים |
|--------|-----------|---------|
| רשת | Bing (עמוד 1) | Yahoo (עמודים 2+ — רק לו יש דפדוף אמין), DuckDuckGo |
| תמונות | Bing (לטינית) / DuckDuckGo (עברית ושאר כתבים) | Wikimedia Commons |
| וידאו | YouTube InnerTube → נגן `/ytp` הפנימי | Bing Videos |
| חדשות | Bing News RSS | — |
שתי מלכודות שהקוד מטפל בהן:
1. **דפדוף של Bing מדומה.** `&first=` מתעלם ומחזיר שוב את עמוד 1, ולכן עמודים 2+ מגיעים מ-Yahoo
(`&b=`), שבו הדפדוף אמיתי.
2. **Bing "חוטף" שאילתות שאינן לטיניות.** הוא מחזיר דף עם כותרת נכונה (`ירושלים - חיפוש תמונות`)
אבל תוצאות שאינן קשורות בכלל — בלי שום קוד שגיאה. `resultsLookRelevant()` פוסל סט כזה
וממשיך למנוע הבא; אם כולם נכשלים מוצג "לא נמצאו תוצאות" ולא זבל. הבדיקה חלה רק על מנועים
שנגרדים מ-HTML — לא על API מובנה (Commons/RSS/InnerTube), שם הכיתובים לגיטימית בשפה אחרת.
**תמונות** עוברות דרך `/x/p`: ה-Worker מאמת magic-bytes, וה-Service Worker מוסיף `X-Etrog-SW`
כדי לקבל אותן אטומות (XOR) — כך הפילטר לא מזהה JPEG ולא מטשטש. בלי SW הן מוגשות רגילות,
כך שגם טעינה ראשונה עובדת.
## איך עוקפים את הסינון (ומה נכשל קודם)
הפילטר (Rimon/Etrog) **מסווג לפי גוף התשובה, לא לפי הכתובת**. הוא עושה MITM על החיבור
לוורקר, קורא את ה-HTML, ומחליף אותו בדף חסימה — מזוהה לפי `rimon: RWC_BLOCK` +
`server: lighttpd`. לכן ynet ו-mako נחסמו, למרות שהוורקר החזיר אותם תקין.
התשובה: **כל מה שניתן לסיווג נשלח אטום (XOR)**. הפילטר רואה בייטים חסרי-משמעות,
ה-Service Worker מפענח לפני שהדפדפן רואה.
| סוג | אטום? | למה |
|-----|-------|-----|
| מסמך HTML (ניווט + iframe) | ✅ | זה מה שהפילטר מסווג |
| XHR/JSON | ✅ | אתרי חדשות מגישים את הכתבה כ-JSON |
| תמונות | ✅ | אחרת הפילטר מטשטש אותן |
| JS | ❌ | אטימה שברה מודולים של YouTube (SyntaxError) |
| CSS | ❌ | לא מסווג, וסיכון מיותר |
| פונטים | ❌ | בקשה שתחמוק מה-SW תקבל בייטים אטומים ו-OTS יפסול |
| מדיה / Range | ❌ | שובר ניגון פרוגרסיבי |
**המלכודת שהייתה:** ה-Service Worker ויתר לגמרי על ניווטים
(`if (e.request.mode === 'navigate') return;`), ולכן המסמך הראשי — בדיוק מה שנחסם — עבר
בטקסט גלוי. בנוסף, ענף ה-HTML בוורקר החזיר תשובה לפני שהגיע לקוד האטימה, כך ש-
`X-Proxy-Obfuscate` פשוט לא נלקח בחשבון עבור HTML. שני החצאים תוקנו.
> אחרי ריווייט, **כל** משאבי המשנה של דף הם כתובות `/cdn/` באותו origin — ולכן הענף
> ה-same-origin ב-SW (ולא זה של cross-origin) הוא שקובע מה נשלח אטום. הוא אטם רק תמונות.
## ניווט בין דפים (הבאג של "הדף הראשי נטען אבל אי אפשר להתקדם")
רוב הקישורים עוברים ריווייט ל-`/cdn/<token>` ולכן הם חד-משמעיים. אבל **כתובת "עירומה"**
ניתוב SPA, קישור שנבנה ב-JS, או רענון של אחד מהם — מגיעה בלי יעד משלה, והוורקר חייב
להסיק לאיזה אתר היא שייכת. שני מקורות המידע לכך היו שבורים:
1. **`__proxy_target` נדרס על ידי כל תשובת HTML** — כולל `<iframe>` ממקור אחר או XHR
שמחזיר HTML. מספיק ווידג'ט/פרסומת שמתארחת על S3 או CDN, והעוגיה של כל הסשן מצביעה
עליו. הכתובת העירומה הבאה נפתרה ל-`<bucket>/path`, ו-S3 החזיר `<Code>AccessDenied</Code>`.
**תוקן:** רק ניווט של מסמך ראשי מזיז את `__proxy_target`. iframe ו-XHR — לעולם לא.
2. **`referrer-policy: no-referrer`** ביטל את הפתרון לפי Referer, שהוא דווקא המדויק,
והשאיר את העוגיה (הניתנת לזיהום) כאות היחיד. **תוקן:** `same-origin` — כל בקשת משנה
מפורקסטת היא same-origin ולכן שומרת Referer שימושי, ולאתרי יעד לא נשלח כלום.
(הוורקר ממילא דורס את ה-Referer שיוצא החוצה לכתובת של אתר היעד עצמו.)
בנוסף, טעינת **דף** בכתובת עירומה מקבלת עכשיו 302 לכתובת הקנונית `/cdn/<token>`, כך
שרענון, סימנייה או Back כבר לא תלויים במצב סשן שצד שלישי יכול להזיז.
## ארכיטקטורה
1. **Service Worker** מיירט Fetch/XHR/Media, מצפין כתובות (XOR + base64url, מפתח `antigravity`) ושולח ל-Worker.
2. **Cloudflare Worker** מפענח, מביא מהיעד, משכתב HTML/M3U8/JSON, מחזיר תשובה (לעיתים מוצפנת).
3. **YouTube**: נתיבי `/watch` מנותבים ל-`/ytp`; הזרם מגיע מ-`/api/m` באותו isolate של ה-Worker (מונע 403 IP mismatch).
4. **מדיה (Scania וכו')**: זיהוי `isMedia` מדלג על HTMLRewriter כדי לא להשחית מקטעים בינאריים שמוסווים כ-`text/html`.
## פריסה
```bash
# Windows PowerShell
$env:CLOUDFLARE_API_TOKEN="your_token"
npx wrangler deploy
```
או: `npm run deploy` אחרי `npm install`.
## אבטחה
- **אל תשימו API Token בקוד או ב-git.** השתמשו במשתנה סביבה `CLOUDFLARE_API_TOKEN`.
- אם הטוקן דלף — בטלו אותו ב-Cloudflare Dashboard וצרו חדש.
### Secrets מומלצים (Cloudflare → Worker → Settings → Variables/Secrets)
הקוד קורא קודם מ-env ונופל לברירת-מחדל רק אם לא הוגדר. הגדירו כ-**Secret** (לא Plaintext):
| Secret | תפקיד | ברירת מחדל אם לא מוגדר |
|--------|-------|------------------------|
| `GATE_PASSWORD` | סיסמת השער | הערך המוטמע (מומלץ לדרוס) |
| `GATE_SECRET` | מפתח חתימת טוקן השער. **שינוי שלו מנתק מיד את כל הסשנים** (רוטציה) | מפתח מוטמע — מומלץ לדרוס |
| `NACHOTOY_SID` | סשן ה-VIP של Nachotoy (`connect.sid` בלבד, בלי השם) | ריק → האתר פשוט לא מחובר-אוטומטית |
```bash
# דוגמה (PowerShell)
$env:CLOUDFLARE_API_TOKEN="…"
npx wrangler secret put GATE_SECRET
npx wrangler secret put GATE_PASSWORD
npx wrangler secret put NACHOTOY_SID
```
- **טוקן השער** אינו עוד ערך קבוע: הוא בפורמט `v2.<תפוגה>.<חתימה>`, פג אחרי 30 יום, וניתן
לביטול מיידי ע"י שינוי `GATE_SECRET`.
- **`connect.sid` של Nachotoy הוסר מהקוד.** הסשן הישן דלף ל-git history — **החליפו אותו**
(התנתקו והתחברו מחדש ב-Nachotoy כדי לפסול את הישן) והכניסו את החדש כ-Secret‏ `NACHOTOY_SID`.
- **הגנת SSRF:** נתיב `/cdn/` חוסם כתובות פנימיות (loopback / RFC1918 / link-local /
169.254.169.254). ה-relay חוסם גם הוא כתובות-IP פנימיות בנוסף ל-allowlist של Google.
## למה `/ytp` ולא YouTube עצמו
נבדק אמפירית (2026-07-26):
| מאיפה | `youtube.com/watch` | תוצאה |
|-------|---------------------|-------|
| Cloudflare (הוורקר) | דף `/sorry/` — CAPTCHA | הוורקר נופל ל-`makeSorryResponse``/ytp` |
| IP ביתי (relay) | 938KB, `playabilityStatus: OK`, כתובות `url=` גלויות | תקין לחלוטין |
| InnerTube API מ-Cloudflare | `ANDROID+IOS`, 12 פורמטים | **עובד** — וזה מה ש-`/ytp` משתמש בו |
כלומר: **דף ה-HTML** של YouTube חסום ל-IP של דאטה-סנטר, אבל **ה-API** לא. `/ytp` מנצל
בדיוק את הפער הזה, ולכן המדיה זורמת דרך Cloudflare (מהיר) ולא דרך הבית.
להצגה ביוטיוב עצמו צריך: להביא את ה-HTML דרך ה-relay (≈1MB לכל טעינת דף על החיבור
הביתי), ואז **להחליף** את `streamingData` בתשובת `/youtubei/v1/player` בכתובות שהוורקר
פתר בעצמו — אחרת כתובות googlevideo יהיו חתומות ל-IP הביתי בעוד הבייטים נמשכים
מ-Cloudflare, ויחזור 403. התשתית לכך קיימת חלקית (`rewritePlayerApiJson`).
`?yt_ui=1` הוא פתח המילוט הקיים לניסיון.
## ניגון: למה 502 על `/x/s/…bin`
כתובות googlevideo **חתומות ל-IP שפתר אותן**. פורמט שנפתר בוורקר חתום ל-Cloudflare וחייב
להישלף מ-Cloudflare; פורמט שנפתר דרך ה-relay חתום ל-IP הביתי וחייב לחזור דרכו.
הבאג: `tryFetchMedia` לקח את ה-relay **בלעדית** בכל פעם שהוא מוגדר, והחזיר `null` ברגע
שהוא נכשל — בלי לנסות אף פעם את הנתיב הישיר. כשהמנהרה נופלת, הניגון מת לגמרי, למרות
שהכתובות חתומות ל-Cloudflare וניתנות לשליפה ישירה (רואים את זה ב-`ip=172.70.x` שבתוך
כתובת ה-videoplayback). התוצאה: "שגיאה בניגון" + מאות 502.
עכשיו **שני** הנתיבים נוסים, לפי הסדר שממנו הכתובות באמת הגיעו (`pack.via`).
בנוסף, לולאת הפורמטים בלעה חריגות (`catch { /* next */ }`), כך ש-502 היה עיוור לחלוטין.
גוף השגיאה כולל עכשיו `dbg[]` עם ניסיון-אחר-ניסיון (סטטוס, content-type, שגיאה) —
אם ניגון נכשל שוב, זה מה שצריך להעתיק.
## פתרון בעיות YouTube
1. פתחו `/ytp` (לא את ממשק YouTube המלא — לא יציב דרך CF).
2. אם יש 502 / no format: `/import-cookies` עם עוגיות מחשבון מחובר (כולל `SAPISID`).
3. כתובות googlevideo חתומות ל-IP של ה-Worker — אל תעבירו `X-Forwarded-For`.