מקודד ומפענח ישויות HTML
הבריחו טקסט כך שיהיה בטוח בתוך HTML, או פענחו ישויות בעלות שם וישויות מספריות בחזרה לתווים שהן מייצגות.
הפלט יופיע כאן
מה הכלי הזה עושה
ל-HTML אין דרך להבחין בין סימן קטן-מ שהתכוונתם אליו כטקסט לבין סימן שפותח תגית. השפה פותרת את זה באמצעות ישויות: רצפי בריחה קצרים שמייצגים תו בלי שייקרא כתגית. הכלי הזה ממיר בשני הכיוונים — טקסט רגיל לצורתו הבָּרוּחה, ו-HTML בָּרוּח בחזרה לתווים שהוא מייצג.
שני הכיוונים רצים כולם בדפדפן. כאן זה משנה יותר מברוב הכלים, כי הטקסט שאנשים מדביקים במפענח ישויות הוא בדרך כלל עמוד שנגרד, מייל מלקוח או עמודה במסד נתונים שמישהו מנסה להבין.
חמשת התווים שחשובים
קיימות אלפי ישויות, אבל צריך רק קומץ כדי להפוך טקסט לבטוח. הקידוד כאן מחליף בדיוק חמישה תווים ומשאיר את כל השאר קריא:
- האמפרסנד הופך ל-& — הוא ראשון, אחרת כל החלפה אחרת הייתה נקראת מחדש כישות.
- סימן קטן-מ הופך ל-< — התו שיכול לפתוח תגית.
- סימן גדול-מ הופך ל-> — פחות מסוכן בפני עצמו, נברח לשם סימטריה וכדי לשרוד מנתחים רשלניים.
- המרכאה הכפולה הופכת ל-" — נדרש בתוך ערכי מאפיין שנכתבו במרכאות כפולות.
- הגרש הופך ל-' — נדרש בתוך ערכי מאפיין שנכתבו בגרש בודד.
האחרון נכתב במספר בכוונה. יש לו שם, ', אבל הוא הגיע מ-XML ו-HTML 4 מעולם לא הגדיר אותו, ולכן מנתחים ישנים מעבירים אותו כטקסט מילולי וההברחה נכשלת בשקט. הצורה המספרית תמיד עבדה בכל מקום, ולכן ספריות הברחה רציניות פולטות אותה.
שמות, עשרוני והקסדצימלי
כל תו אפשר לכתוב בשלוש דרכים, והפענוח מקבל את כולן. הקו המפריד הארוך הוא דוגמה טובה:
named — הצורה הקריאה, אך רק לתווים שיש להם שם decimal — נקודת הקוד בבסיס 10 hex — אותה נקודת קוד בבסיס 16, הכתיב של טבלאות יוניקוד
הכלי מזהה בפענוח את 255 השמות של מערכת HTML 4 — האותיות הלטיניות, האלפבית היווני, החצים, הסימנים המתמטיים והפיסוק הטיפוגרפי, שביחד מכסים כמעט כל מה שמופיע בטקסט אמיתי. HTML 5 מגדיר עוד כ-2200 שמות, כמעט כולם סימנים מתמטיים נדירים; נשיאת הטבלה הזו הייתה מוסיפה עשרות קילובייטים לכל טעינת עמוד בתמורה לשמות שרוב המסמכים לעולם לא משתמשים בהם. שם מחוץ למערכת מדווח ולא נמחק, כך שהמחיר הוא אזהרה, לעולם לא תשובה שגויה.
הקידוד הולך לכיוון ההפוך ונשאר פשוט בכוונה: שמות לחמשת התווים שלמעלה, מספרים עשרוניים לכל השאר. שום דבר לא הופך ל-… או ל-’ כשהתו עצמו תקין לחלוטין במסמך UTF-8.
הברחה אינה כלל אחד אלא ארבעה
מפתה להתייחס להברחת ישויות כתשובה להזרקות, ובתוך תוכן של אלמנט היא באמת כזו. אבל HTML הוא בעצם ארבע שפות מקוננות זו בזו, ולכל אחת כללים משלה. הברחת ישויות נכונה בשני המקומות הראשונים כאן וחסרת תועלת בשאר:
- תוכן של אלמנט — טקסט בין תגיות. הברחת ישויות היא בדיוק הדבר הנכון.
- ערכי מאפיין במרכאות. הברחת ישויות נכונה, בתנאי שהמאפיין באמת במרכאות; בלעדיהן רווח בערך מסיים את המאפיין וההברחה לא קונה לכם דבר.
- בתוך בלוק script. חלים כללי ההברחה של JavaScript ולא ישויות — הדפדפן אינו מפענח ישויות שם, ולכן מרכאה בָּרוּחה מגיעה כתווים המילוליים ".
- מאפייני כתובת כמו href ו-src. הברחת ישויות אינה עוצרת כתובת javascript:, כי הסכנה נמצאת בסכימה ולא באף תו שישות הייתה מחליפה.
הכלל המעשי: הבריחו בנקודה שבה הטקסט מוכנס למסמך, לפי כללי ההקשר המדויק שאליו הוא נוחת — לא פעם אחת, מוקדם, מתוך תקווה שזה יחזיק בכל מקום בהמשך.
למה לפעמים רואים כתיב מוכפל
זו הסיבה הנפוצה ביותר להגיע למפענח ישויות. הטקסט הוברח, ואז הוברח שוב — בדרך כלל פעם אחת בקוד האפליקציה ופעם נוספת במנוע תבניות או במסגרת עבודה שכבר מבריחה את הפלט שלה:
original Fish & Chips escaped Fish & Chips escaped x2 Fish &amp; Chips
השורה השנייה מוצגת בעמוד כמקור. השלישית מוצגת בעמוד כשורה השנייה — כלומר המבקר רואה את הישות עצמה במקום את התו. הפענוח מסיר בדיוק שכבה אחת, ולכן הרצה אחת מספיקה כדי לדעת באיזה מקרה אתם. אם בפלט עדיין יש ישויות, היו יותר משכבה אחת. התיקון כמעט אף פעם אינו הסרת ישויות בזמן התצוגה — הוא איתור והסרה של שלב ההברחה הכפול, כי אותו באג משחית בשקט גם את הנתונים השמורים.
מעבר ל-ASCII, והאפשרות לכך
עמוד המוגש כ-UTF-8 אינו זקוק לישויות עבור אותיות מוטעמות, עברית, ערבית, סינית או אמוג׳י. התווים האלה תקינים כפי שהם, והשארתם קריאים שומרת על מקור קריא וקובץ קטן יותר. זו ברירת המחדל כאן.
האפשרות קיימת מפני שיש יעדים שעדיין דורשים ASCII בלבד: תבניות דואר ישנות, מערכות שמשבשות בתים מעל 127, או עמודה במסד נתונים שאף אחד לא מוכן לשנות את הקידוד שלה. הפעלתה כותבת כל תו כזה כהפניה עשרונית — é הופך ל-é, א הופכת ל-א.
האמוג׳י ראויים להערה. אמוג׳י נכתב כהפניה אחת לנקודת הקוד השלמה שלו, למשל 🙂, ולעולם לא כשני חצאים. אם השתמשתם בכלי ההברחה של JSON, זה ההפך ממה שקורה שם, שבו הפורמט דורש במפורש לפצל אמוג׳י לשני רצפי \u. אותו תו, שתי תשובות נכונות, מפני ששני הפורמטים מגדירים הברחה מעל יחידות שונות.
כשלא ניתן לפענח הפניה
הפענוח כאן לעולם אינו נכשל ולעולם אינו מוחק דבר. הפניה שלא ניתן לפתור נשארת בדיוק כפי שנמצאה ומופיעה מעל הפלט עם מספר שורה ועמודה. שלושה דברים מדווחים:
- שם לא מוכר, כמו &nbps; — כמעט תמיד טעות הקלדה ב- . זו האזהרה ששווה לפעול לפיה: היא אומרת שחסר רווח בתוכן שלכם ואף אחד לא שם לב.
- מספר שאינו תו, כמו � או ערך מעל טווח יוניקוד. אין למה לפענח אותו, ולכן הוא נשאר כטקסט.
- מספר בין 128 ל-159. ראו למטה.
אמפרסנד בודד אינו מדווח כלל. ב-HTML, R&D ו-Q&A הם טקסט רגיל, והתייחסות לכל אמפרסנד תועה כשגיאה הייתה קוברת את האזהרות החשובות תחת רעש מפרוזה.
הטווח שבין 128 ל-159 הוא מוזרות אמיתית. המספרים האלה מציינים תווי בקרה בלתי נראים שאיש לא מתכוון לכתוב; הם מופיעים מפני שהמסמך נכתב ב-Windows-1252, שבו אותם מספרים הם גרשיים מסולסלים, מקפים וסימן האירו. דפדפנים קוראים אותם כ-Windows-1252 בדיוק מהסיבה הזו, והכלי מתנהג כמותם כדי שהתוצאה תהיה שווה למה שהעמוד יציג בפועל — אבל הוא אומר זאת באזהרות, כי החלפה שקטה שלא ביקשתם גרועה מהחלפה רועשת.
שאלות נפוצות
- האם הברחת חמשת התווים האלה מספיקה כדי לעצור XSS?
- בתוכן של אלמנט ובערכי מאפיין במרכאות — כן, זו בדיוק מטרתה. היא אינה מספיקה בתוך בלוק script או style, במאפיין ללא מרכאות, או במאפיין כתובת כמו href, שכל אחד מהם זקוק לכללי ההברחה שלו. הברחה היא תכונה של המקום שאליו הטקסט נוחת, לא של הטקסט.
- למה הגרש מקודד כ-' ולא כ-'?
- מפני ש-' היא ישות XML ש-HTML 4 מעולם לא הגדיר. מנתחים שקדמו ל-HTML 5 משאירים אותה כטקסט מילולי, ולכן גרש שחשבתם שהוברח — לא הוברח. הצורה המספרית עובדת בכל מנתח, ולכן היא ברירת המחדל הבטוחה.
- בעמוד שלי מופיעה ישות במקום אמפרסנד. מה קרה?
- הטקסט הוברח פעמיים. פענחו אותו כאן: מעבר אחד מחזיר את הכתיב הכפול לכתיב יחיד ומאשר את השכבה המיותרת. תקנו את הצינור ולא את הטקסט — בדרך כלל הקוד שלכם מבריח פלט שמנוע התבניות כבר הבריח.
- האם צריך לקודד עברית, ערבית או אמוג׳י?
- לא בעמוד מודרני המוגש כ-UTF-8; הם תקינים כפי שהם. הפעילו את האפשרות שמעבר ל-ASCII רק כשמשהו בהמשך הצינור דורש ASCII בלבד, כמו תבנית דואר ישנה. כל אמוג׳י נכתב אז כהפניה מספרית אחת.
- למה הכלי מזהיר על “ אם הוא פענח אותו בסדר?
- מפני ש-147 מציין תו בקרה בלתי נראה ולא את הגרשיים שאתם רואים. מסמכים שמשתמשים בו נכתבו ב-Windows-1252, שבו 147 הוא גרשיים מסולסלים — דפדפנים קוראים כך, וגם הכלי. האזהרה מודיעה שהתרחשה החלפה, כדי שתוכלו לתקן את קידוד המקור אם הוא שלכם לתיקון.
- האם הטקסט שלי נשלח לאנשהו?
- לא. הקידוד והפענוח רצים כולם בדפדפן; שום דבר שתדביקו לא עוזב את המכשיר שלכם.