דילוג לתוכן
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after a top-up of $10 or more. Until Nov 30.

How it works

יסודות OWASP ZAP מול Juice Shop מקומי

להריץ את סריקת ה-baseline הפסיבית של ZAP מול Juice Shop על המחשב שלך, לקרוא את הדוח ולבדוק התראה אחת ידנית לפני שסומכים עליה.

מתחילים10 דקות קריאהמאת NotHoly

מהו ZAP ואיפה הוא יושב

Zed Attack Proxy, שנקרא בדרך כלל ZAP, הוא כלי חינמי למציאת בעיות באפליקציות web. הוא עושה שני דברים שרלוונטיים למדריך הזה. הוא עובד כפרוקסי מיירט, כך שאפשר לקרוא כל בקשה שהדפדפן שולח וכל תשובה שהוא מקבל. והוא מריץ סורקים שבודקים את התעבורה הזו ומחפשים בה דפוסים חלשים.

פרוקסי הוא מתווך. בדרך כלל הדפדפן שולח בקשה ישירות אל 127.0.0.1:3000. כשהיא עוברת דרך ZAP, הדפדפן שולח אותה אל ZAP. הכלי מעביר אותה לאפליקציה, מקבל את התשובה, שומר את הזוג בהיסטוריה שלו ומחזיר את התשובה לדפדפן. האפליקציה לא יודעת ששום דבר השתנה. פשוט מתקבל אצלך עותק של השיחה.

המיקום הזה הוא הסיבה שהכלי עוזר. סורק רואה מה השרת מחזיר. פרוקסי מראה מה הדפדפן עשה בפועל, כולל כותרות, עוגיות, הפניות וה-JavaScript שהדף טען. הרבה בעיות web מתגלות רק כשמשווים בין שתי התצוגות האלה.

סריקה פסיבית וסריקה אקטיבית

ל-ZAP יש שני סוגי סריקה, וההבדל קובע מה מותר להריץ ואיפה.

סריקה פסיבית קוראת תעבורה שכבר עברה דרך ZAP. כל תשובה נבדקת לדברים כמו כותרת Content-Security-Policy חסרה, עוגיות בלי הדגל HttpOnly או stack traces בדפי שגיאה. החוקים הפסיביים לא שולחים מחרוזות תקיפה. הם רק מסתכלים על מה שחזר.

סריקה אקטיבית שולחת בקשות חדשות שנבנו כדי לעורר תגובה. היא משנה פרמטרים, שולחת קלט מוזר ובודקת איך האפליקציה מגיבה. רוב הממצאים הרציניים מגיעים מהסוג הזה של בדיקה, וזה גם הסוג שמתנהג כמו תקיפה. להריץ אותה רק מול מעבדה שבבעלותך, או מול מערכת שיש לך עבורה הרשאה בכתב וסקופ מוגדר.

סריקת ה-baseline במדריך הזה פסיבית. התיעוד של ZAP מתאר אותה כסקריפט שמריץ את ה-spider מול היעד לדקה אחת כברירת מחדל, מחכה שהחוקים הפסיביים יסיימו ואז כותב את הדוח. היא לא מריצה payloads של תקיפה, ולכן היא צעד ראשון סביר במעבדה שלך.

למה התראה היא רמז ולא הוכחה

כל התראה של ZAP היא חוק שהתאים. חוק יכול להתאים לכותרת חסרה, לדפוס בגוף התשובה או לשילוב של שניהם. הוא לא יכול לדעת למה האפליקציה שלך נועדה, על אילו נתונים היא מגנה או אם תוקף יכול להגיע לנתיב החשוב. התראה אומרת איפה לחפש. היא לא מאשרת שאפשר לנצל חולשה.

שני סוגי טעויות מופיעים כל הזמן. חלק מההתראות הן false positives: החוק התאים למשהו לא מזיק. בעיות אחרות מתפספסות לגמרי, כי הסורק לא הגיע לדף או לא שלח את הקלט שמפעיל את הפגם. מה שהופך רמז לממצא הוא צעד שאפשר לחזור עליו בעצמך: בקשה ששלחת, התשובה שראית והסבר פשוט למה זה משנה. עד שיש את שלושתם, ההתראה נשארת ברשימה מסומנת "לאימות".

כדאי לזכור את הכלל הזה לאורך המדריך. הסורק נותן נקודת התחלה, ועבודת האימות היא שלך.

לפני שמתחילים

צריך ארבעה דברים:

  • Docker. מתאימים Docker Desktop ב-Windows או ב-macOS ו-Docker Engine ב-Linux. לאמת עם docker version.
  • מחשב שבבעלותך, שמריצים עליו רק את המעבדה שמתוארת כאן.
  • טרמינל. הדוגמאות משתמשות ב-POSIX shell. ב-Windows הרצתי אותן ב-Git Bash.
  • דפדפן, בשביל שלב הפרוקסי השולחני האופציונלי.

המעבדה נשארת על המחשב שלך. יש לפרסם את הפורט שלה רק על 127.0.0.1. פורט שמתפרסם בלי הקידומת הזו יכול להאזין על כל ממשק רשת, ו-Juice Shop בנוי כדי להיפרץ.

שלב 1: הפעלת המעבדה

מפעילים את Juice Shop ברקע עם שם שאפשר להשתמש בו אחר כך:

bash
docker run -d --name juice-local -p 127.0.0.1:3000:3000 bkimminich/juice-shop

הדגל -d מריץ את הקונטיינר במצב detached. השמטתי את --rm בכוונה, כדי שהקונטיינר יישאר עד שעוצרים ומסירים אותו לפי השם. הקידומת 127.0.0.1: מרחיקה את הפורט מהרשת שלך.

בודקים שהוא עונה:

bash
curl -i http://127.0.0.1:3000/

אצלי השורות הראשונות של התשובה נראו כך. הסרתי את מד ההתקדמות שהכלי curl מדפיס כשהוא לא רץ עם -s:

text
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Feature-Policy: payment 'self'
X-Recruiting: /#/jobs
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sun, 08 Nov 2026 09:06:18 GMT
ETag: W/"2700-1a1ba6896a5"
Content-Type: text/html; charset=UTF-8
Content-Length: 9984

הכותרות והתאריכים שלך יהיו שונים. כדאי לרשום גם את גרסת Juice Shop, כי האפליקציה משתנה בין גרסאות. ה-endpoint הזה מחזיר אותה:

bash
curl -s http://127.0.0.1:3000/rest/admin/application-version

אצלי הוא הדפיס {"version":"20.3.0"}. את המספר הזה רושמים בהערות ליד תוצאות הסריקה.

שלב 2: הרצת סריקת ה-baseline

מריצים את הסורק מהתיקייה שבה הדוח אמור להופיע. זו הפקודה עבור Docker Desktop:

bash
docker run --rm -t -v "$(pwd)":/zap/wrk ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.html

לכל חלק יש תפקיד:

  • --rm מסיר את קונטיינר הסורק כשהסריקה מסתיימת.
  • -v "$(pwd)":/zap/wrk מחבר את התיקייה הנוכחית שלך לתוך הקונטיינר, כך שהדוח נכתב לדיסק שלך.
  • -t http://host.docker.internal:3000 הוא היעד. מתוך קונטיינר, host.docker.internal מצביע על המחשב שלך, שבו המעבדה מאזינה.
  • -r zap-report.html כותב את דוח ה-HTML המלא לתיקייה המחוברת.

ב-Linux, ה-host alias של Docker Desktop לא קיים כברירת מחדל. מוסיפים מיפוי host לאותה פקודה:

bash
docker run --rm -t --add-host=host.docker.internal:host-gateway -v "$(pwd)":/zap/wrk ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.html

בדקתי רק את הגרסה של Docker Desktop. ב-Linux, פורט שמתפרסם על 127.0.0.1 לא בהכרח יקבל חיבורים שמגיעים דרך כתובת ה-bridge של Docker. אם הסורק מדווח על שגיאות חיבור, צריך לקרוא את השורות הראשונות של הפלט שלו לפני שמשנים משהו אחר.

שתי בעיות שנתקלתי בהן

הניסיון הראשון נכשל בלי לסרוק כלום. בקונסולה הופיע A file based option has been specified but the directory '/zap/wrk' is not mounted, אחר כך טקסט ה-usage, והפקודה יצאה עם קוד 3. ה-mount לא עבד בסשן ה-Git Bash שלי, ולא גיליתי למה. הרצת הפקודה עם MSYS_NO_PATHCONV=1 ועם נתיב Windows מלא בדגל -v פתרה את זה:

bash
MSYS_NO_PATHCONV=1 docker run --rm -t -v "D:/path/to/your/folder:/zap/wrk" ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.html

צריך להחליף את הנתיב בתיקייה שלך. ההרצה הראשונה גם מורידה את האימג׳ של ZAP, ובחיבור שלי זה לקח כמעט שתים־עשרה דקות. הרצות מאוחרות יותר עולות תוך שניות.

קריאת הפלט בקונסולה

הסורק מדפיס שורה אחת לכל חוק. זה החלק שחשוב מההרצה שלי, עם הרשימה הארוכה של החוקים שעברו מקוצרת:

text
Using the Automation Framework
Total of 116 URLs
PASS: Vulnerable JS Library (Powered by Retire.js) [10003]
PASS: Cookie No HttpOnly Flag [10010]
...
PASS: CSP [10055]
...
WARN-NEW: Content Security Policy (CSP) Header Not Set [10038] x 5
WARN-NEW: Cross-Domain Misconfiguration [10098] x 5
WARN-NEW: Modern Web Application [10109] x 5
FAIL-NEW: 0	FAIL-INPROG: 0	WARN-NEW: 3	WARN-INPROG: 0	INFO: 0	IGNORE: 0	PASS: 59

WARN-NEW אומר שחוק סימן משהו שהסריקה לא ראתה קודם. PASS אומר שהחוק רץ ולא מצא כלום. המספר אחרי כל שם חוק הוא מזהה החוק של ZAP, ואפשר לחפש אותו בתיעוד של ZAP.

בהרצה שלי הפקודה יצאה עם קוד 2. התיעוד של baseline מפרט את קודי היציאה: 0 הוא הצלחה, 1 אומר לפחות FAIL אחד, 2 אומר לפחות WARN אחד ובלי FAIL, ו-3 הוא כל כישלון אחר. קוד 2 הוא נורמלי כאן. הוא רק אומר שהסורק מצא משהו לקרוא.

שלב 3: קריאת הדוח

פותחים את zap-report.html בדפדפן. הסיכום בראש הדוח שלי הציג High 0, Medium 2, Low 0 ו-Informational 1. כל הרשימה הייתה שלוש התראות:

  • Content Security Policy (CSP) Header Not Set, Medium. התשובה לא שולחת כותרת Content-Security-Policy, כותרת שמאפשרת לדפדפן להחליט אילו סקריפטים ו-frames לטעון.
  • Cross-Domain Misconfiguration, Medium. התשובה כוללת את Access-Control-Allow-Origin: *, וזה מאפשר לכל אתר לקרוא את התשובה בדפדפן.
  • Modern Web Application, Informational. ZAP זיהה שהאתר מתנהג כמו single-page app. זה תיאור של האפליקציה, ואינו פגם.

לכל התראה יש אותם חלקים: תיאור, ה-URLs שבהם היא הופעלה, הראיה ש-ZAP ראה, כמה הערות נוספות והצעת תיקון. קודם קוראים את הראיה ואת ה-URL. התיאור מסביר מה החוק אומר באופן כללי. הראיה מראה מה קרה בתשובה הזו.

לא מדרגים את הדוח רק לפי תווית הסיכון. ZAP קובע רמת סיכון לפי החוק שלו, ולא לפי הנתונים של האפליקציה שלך. Medium על דף בלי נתונים רגישים עשוי להיות פחות חשוב מ-Low על endpoint שמטפל בחשבונות.

שלב 4: אימות ידני של התראה אחת

בוחרים בהתראת ה-CSP. הראיה שלה היא היעדרות, ולכן הבדיקה פשוטה: מבקשים את הדף, מחפשים את הכותרת ורושמים את התוצאה. הפקודה הזו מדפיסה את מספר השורות התואמות:

bash
curl -s -i http://127.0.0.1:3000/ -o /dev/null -D - | grep -i -c '^content-security-policy'

אצלי היא הדפיסה 0. גם ברשימת הכותרות המלאה שתפסתי קודם לא הייתה שורת Content-Security-Policy. ההתראה תואמת למה שהשרת שולח.

עכשיו ההתראה השנייה. הראיה היא השורה Access-Control-Allow-Origin: * שכבר הופיעה בפלט של curl -i. הבדיקה שלך צריכה לענות על שאלה אחרת: האם התשובה הזו מכילה משהו שאתר צד שלישי לא אמור לקרוא? דף השורש של המעבדה הוא HTML ציבורי, ולכן הכותרת עצמה היא לא הבעיה. הטקסט של ZAP אומר את אותו דבר. דפדפנים לא נותנים לדף של צד שלישי לקרוא בדרך הזו תשובות מאומתות. ההתראה נעשית רצינית רק כש-endpoint שמחזיר נתונים פרטיים שולח גם wildcard origin. אם נמצא endpoint כזה, בודקים אותו עם חשבון המעבדה שלך ורושמים מה הוא מחזיר.

בהתראה השלישית אין מה להוכיח. היא מתארת את סוג האפליקציה. משאירים אותה בדוח כהקשר וממשיכים.

כל תוצאה מאומתת נרשמת בטבלה או בקובץ טקסט עם ארבע עמודות: ההתראה, הבקשה ששלחת, מה שראית וההכרעה שלך. הכרעה יכולה להיות "אושר", "לא שוחזר" או "צריך עוד הקשר". שלוש התשובות האלה מכסות את רוב מה שעושים עם פלט של סורק.

שלב 5: העברת הדפדפן דרך ZAP

השלב הזה מאפשר לראות בקשות אמיתיות, וזה החלק שהופך את הפרוקסי לשימושי מעבר לסריקה. אפשר לעשות את זה עם האפליקציה השולחנית או עם קונטיינר שמריץ ZAP במצב daemon. שתי הדרכים מתוארות כאן. הרצתי את גרסת ה-daemon. את ההדרכה השולחנית לא הרצתי, ולכן אין ממנה פלט בהמשך.

הגרסה השולחנית (לא הרצתי)

מתקינים את ZAP מהאתר הרשמי. דף ה-getting started מקשר למתקין הנוכחי. פותחים אותו ובוחרים Manual Explore, שמפעיל דפדפן ששולח את התעבורה שלו דרך ZAP. מזינים http://127.0.0.1:3000 ועוברים על דף הבית. אחר כך פותחים את הלשונית History. כל שורה היא זוג של בקשה ותשובה, עם ה-method, ה-URL וקוד הסטטוס. משווים את השורות האלה למה שרואים בכלי המפתחים של הדפדפן שלך. ההבדלים מראים מה הדפדפן שלח בלי שציפית לזה.

גרסת ה-daemon (מה שהרצתי)

מפעילים את ZAP כפרוקסי רקע על פורט 8080:

bash
docker run -d --name zap-proxy-lab -p 127.0.0.1:8080:8080 ghcr.io/zaproxy/zaproxy:stable zap.sh -daemon -host 0.0.0.0 -port 8080 -config api.disablekey=true -config api.addrs.addr.name=.* -config api.addrs.addr.regex=true

האפשרות api.disablekey=true מסירה את בדיקת מפתח ה-API כדי שהפקודות בהמשך יעבדו בלי הגדרות נוספות. צריך להשאיר את הפורט מפורסם על 127.0.0.1 ולהסיר את הקונטיינר בסיום. כל מי שיכול להגיע ל-API יכול לשלוט ב-ZAP.

בודקים שה-API עונה:

bash
curl -s http://127.0.0.1:8080/JSON/core/view/version/

הפלט אצלי:

text
{"version":"2.17.0"}

שולחים שתי בקשות דרך הפרוקסי. הדגל -x אומר לכלי curl להשתמש ב-ZAP. שם היעד הוא host.docker.internal כי ZAP רץ בתוך Docker, והשם שהוא משתמש בו עבור המחשב שלך הוא השם שהוא מפענח משם:

bash
curl -s -x http://127.0.0.1:8080 -o /dev/null -w 'proxied root http=%{http_code}\n' http://host.docker.internal:3000/
curl -s -x http://127.0.0.1:8080 -o /dev/null -w 'proxied api http=%{http_code}\n' "http://host.docker.internal:3000/rest/products/search?q=apple"

פלט:

text
proxied root http=200
proxied api http=200

שואלים את ZAP כמה הודעות הוא רשם:

bash
curl -s "http://127.0.0.1:8080/JSON/core/view/numberOfMessages/"

אצלי הוא החזיר {"numberOfMessages":"5"}. אחר כך מציגים אותן עם one-liner קצר ב-Python שמדפיס את שורת הבקשה ואת שורת הסטטוס של התשובה לכל אחת:

bash
curl -s "http://127.0.0.1:8080/JSON/core/view/messages/?start=1&count=5" | python -c "import json,sys; d=json.load(sys.stdin); [print(m['requestHeader'].splitlines()[0], '->', m['responseHeader'].splitlines()[0]) for m in d['messages']]"

פלט:

text
GET http://host.docker.internal:3000/ HTTP/1.1 -> HTTP/1.1 200 OK
GET http://host.docker.internal:3000 HTTP/1.1 -> HTTP/1.0 0
GET http://host.docker.internal:3000/rest/products/search?q=apple HTTP/1.1 -> HTTP/1.1 200 OK
GET http://host.docker.internal:3000/rest HTTP/1.1 -> HTTP/1.0 0
GET http://host.docker.internal:3000/rest/products HTTP/1.1 -> HTTP/1.0 0

שלחתי שתי בקשות דרך הפרוקסי, אבל בהיסטוריה יש חמש רשומות. לשלוש מהן אין תשובה, וזה מופיע כסטטוס 0. לא בדקתי מאיפה הן הגיעו. אין לקרוא אותן כראיה על Juice Shop. זו הסיבה לבדוק את ההיסטוריה בעצמך ולא לסמוך על ספירה. כשהרשימה שלך שונה, רושמים מה רואים ושואלים למה לפני שמסיקים מסקנה.

בסיום עוצרים ומסירים את הפרוקסי:

bash
docker rm -f zap-proxy-lab

טעויות נפוצות

לכוון את הסורק אל יעד שלא בבעלותך. הדגל -t מקבל כל URL, והסורק ישלח אליו בקשות. היעד צריך להישאר ה-alias של המעבדה במחשב שלך.

לפרסם את המעבדה על כל הממשקים. -p 3000:3000 בלי כתובת מאזין על כל ממשק. תמיד לכתוב 127.0.0.1:3000:3000.

להתייחס לאזהרות כאל ציון. דוח עם שלוש אזהרות אומר ששלושה חוקים התאימו. צריך לאמת כל אחת לפני שסופרים אותה.

לדלג על הגרסה. Juice Shop משתנה בין גרסאות, וכך גם החוקים של ZAP. רושמים את שתי הגרסאות עם הדוח. הגרסאות שהשתמשתי בהן היו Juice Shop 20.3.0 ו-ZAP 2.17.0.

להשאיר את הפרוקסי רץ. מפתח ה-API של ה-daemon מושבת. מסירים את הקונטיינר אחרי כל סשן.

לקרוא את עמודת הסטטוס כהוכחה. שורה עם סטטוס 0 אומרת שלא נרשמה תשובה. זה לא ממצא.

לחבר את התיקייה הלא נכונה. אם הדוח חסר, בודקים קודם את ה-mount של -v. הסורק יכול לרוץ בהצלחה ועדיין לא לכתוב כלום במקום הצפוי.

איך ממשיכים

כדאי להחזיק קובץ הערות אחד למעבדה. כל שורה מכילה התראה אחת, את הבקשה שבדקה אותה, מה שראית ואת ההכרעה שלך. אחרי כמה סריקות הקובץ נעשה תיעוד של מה הכלי מצא, מה אישרת ובמה הוא טעה.

אם דילגת על מדריך ההתקנה של Juice Shop, כדאי לקרוא אותו, כי המאמר הזה מניח את אותה מעבדה. המאמר הבא משתמש באותה מעבדה כדי להסביר SQL injection. שם קל יותר לראות למה התראה של סורק צריכה בדיקה ידנית: הפגם קשור לדרך שבה קלט מגיע לשאילתה, וכלי יכול רק לרמוז על זה.

התיעוד של ZAP מכסה contexts, אימות וסריקה אקטיבית. הנושאים האלה נעשים רלוונטיים ברגע שאפשר להבדיל בין בעיה מאומתת להתאמה של חוק. כדאי ללמוד אותם מול המעבדה, עם הרשאה, ולא להעביר אותם למערכות אחרות.

הפרויקט של Juice Shop מחזיק את רשימת האתגרים ואת הערות ההתקנה במאגר GitHub שלו. כדאי לבדוק בדף ה-releases שלו מהי הגרסה הנוכחית לפני שמושכים את האימג׳.

מקורות

מקורות

  1. ZAP documentation
  2. ZAP Baseline Scan (Docker)
  3. ZAP getting started
  4. OWASP Juice Shop (GitHub)

עוד בנושא כלי אבטחה

כלי אבטחה

ה-toolkit ל-bug bounty ו-NotHoly CLI

toolkit קטן וישר ל-bug bounty למתחילים, בתוספת NotHoly CLI בטרמינל עם scope רשום כך שהפקודות נשארות בתוך התוכנית.

מתחילים3 דקות קריאה

כלי אבטחה

למה ה-CLI של NotHoly ולא עוזר טרמינל כללי

הסוכן רץ על המכונה שלכם באישורכם, המודל מקבל את עבודת האבטחה, ו-engagement בודק כל פקודה מול ה-scope שלכם לפני שהיא רצה.

ביניים2 דקות קריאה

כלי אבטחה

למה תוסף ה-VS Code של NotHoly לסקירת קוד

סקרו את הקובץ או את ה-git diff בעורך שלכם, עם ההקשר שהקוד נותן. המודל מקבל את שאלת האבטחה והופך ממצא לבדיקה או לחוקה.

ביניים2 דקות קריאה