Menu

htmlspecialchars() ב-PHP: escape ל-HTML ומניעת XSS

htmlspecialchars($text) הופכת את חמשת התווים שיש להם משמעות ב-HTML (& < > " ') לישויות, כך שקלט משתמש מוצג כטקסט במקום לרוץ כ-markup. למדו ENT_QUOTES, escape למאפיינים, קידוד כפול, htmlentities ו-strip_tags.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

htmlspecialchars($text) ממירה את &, <, >, " ו-' ל-&amp;, &lt;, &gt;, &quot; ו-&#039;. קראו לה על כל פיסת קלט משתמש שאתם מדפיסים לתוך דף, והקלט יוצג כטקסט במקום להיקרא כ-HTML.

הבלוק מדפיס את אותה תגובה פעמיים, פעם גולמית ופעם אחרי escape. הריצו אותו והשוו את שתי השורות בלשונית Page, ואז הקלידו HTML משלכם בטופס, למשל <h1>big</h1> או <img src=x>, ולחצו על Show.

בשורה הגולמית הדפדפן מציית לתגיות: המילה המודגשת מודגשת, ומבקר שמקליד <script> גורם לסקריפט שלו לרוץ בדפדפן של כל קורא אחר. המתקפה הזו נקראת cross-site scripting (XSS). בשורה שעברה escape אותם תווים מגיעים כ-&lt;b&gt; והדפדפן מצייר אותם כטקסט. עברו ללשונית Output כדי לראות את הישויות ש-PHP הדפיסה בפועל.

מה htmlspecialchars ממירה

חמישה תווים, שום דבר אחר. אותיות, סימנים דיאקריטיים ואימוג'י עוברים בלי שינוי.

& נמצא ברשימה כי הוא מתחיל כל ישות: אם הוא היה נשאר כמו שהוא, תגובה שמזכירה &lt; הייתה מוצגת כ-<.

escape למאפיינים, לא רק לטקסט

קלט משתמש בתוך מאפיין צריך escape באותה מידה. בלעדיו, מירכאה בערך סוגרת את המאפיין ושאר הקלט הופך למאפיינים חדשים. כאן ה"שם" מגניב פנימה מאפיין style; הריצו והסתכלו על שתי התיבות.

בתיבה הלא בטוחה הדפדפן רואה value="Ada" ואחריו מאפיין style חדש, ולכן התיבה הופכת לאדומה ומציגה רק Ada. תוקף היה כותב שם onfocus="..." במקום style, והקוד שלו היה רץ. בתיבה הבטוחה כל " הפכה ל-&quot;, ולכן כל המחרוזת נשארת בתוך value ומוצגת כפי שהוקלדה.

מאז PHP 8.1 הדגלים של ברירת המחדל הם ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ולכן גם גרשיים בודדים עוברים escape ומאפיינים שנכתבו עם '...' בטוחים. קוד ישן מעביר לעיתים קרובות ENT_QUOTES ידנית, וב-PHP 7 ומטה זה היה הכרחי:

פונקציית עזר קצרה לתבניות

כתיבה של htmlspecialchars($x, ENT_QUOTES, 'UTF-8') עשרות פעמים בתבנית רועשת, ולכן רוב הפרויקטים עוטפים אותה בפונקציה בת אות אחת. מנועי תבניות כמו Twig ו-Blade עושים את אותו הדבר אוטומטית לכל {{ $var }}.

הטיפוס ?string וה-?? '' חשובים: העברה של null ל-htmlspecialchars() הוצאה משימוש מאז PHP 8.1, וה-PHP שלכם הייתה מדפיסה הודעת deprecation לכל משתמש בלי bio.

קידוד כפול ו-htmlspecialchars_decode

אם ערך עובר escape פעמיים, הקורא רואה את הישויות: & הופך ל-&amp; בפעם הראשונה ול-&amp;amp; בשנייה, שהדפדפן מציג כ-&amp;. זה אומר בדרך כלל שהערך עבר escape כשנשמר ושוב כשהודפס. העבירו double_encode: false כדי להשאיר ישויות קיימות כמו שהן, והשתמשו ב-htmlspecialchars_decode() כדי לחזור אחורה.

התיקון האמיתי הוא לשמור טקסט גולמי ולבצע escape רק בפלט. double_encode: false מיועד לטקסט שכבר מכיל ישויות שלא אתם יצרתם, כמו פיד שיובא.

htmlspecialchars מול htmlentities מול strip_tags

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

  • htmlspecialchars() מבצעת escape לחמשת תווי ה-HTML. השתמשו בה לכל טקסט שאתם מדפיסים לתוך HTML.
  • htmlentities() הופכת גם את é ל-&eacute;. היא הייתה שימושית כשדפים לא היו ב-UTF-8; היום היא רק הופכת את קוד המקור לקשה יותר לקריאה.
  • strip_tags() מוחקת תגיות ושומרת על הטקסט שלהן. היא נועדה להפוך HTML לטקסט פשוט (תצוגה מקדימה של אימייל, meta description), לא לאבטחה: השורה האחרונה מראה ש-<b> מותר שומר על ה-onclick שלו, וטקסט שמוצב בתוך מאפיין לא נוגעים בו בכלל.

איפה htmlspecialchars לא מספיקה

htmlspecialchars() היא ה-escape הנכון לטקסט HTML ולמאפיינים במירכאות. למקומות אחרים בדף יש כללים אחרים:

  • בכתובת URL, http_build_query() או urlencode() מקודדות את הערך; htmlspecialchars() הופכת אז את ה-& שבין הפרמטרים ל-HTML תקין.
  • ב-JavaScript, json_encode() מפיקה ערך JS תקין, ו-JSON_HEX_TAG הופך את < ואת > ל-\u003C ול-\u003E, כך ש-</script> בתוך הנתונים לא יכול לסגור את תגית הסקריפט.
  • לעולם אל תדפיסו קלט משתמש לתוך href בלי לבדוק את הסכמה: htmlspecialchars('javascript:alert(1)') לא משתנה ועדיין רץ כשלוחצים עליו. קבלו רק כתובות http ו-https, כמו שמוצג בעמוד filter_var.

לטיפול בטפסים שמחבר את כל זה יחד, ראו טפסים ב-PHP.

שאלות נפוצות

מה htmlspecialchars עושה ב-PHP?

היא מחליפה את &, <, >, " ו-' ב-&amp;, &lt;, &gt;, &quot; ו-&#039;. הדפדפן מציג אז את התווים האלה במקום לקרוא אותם כ-HTML, ולכן <script> שהוקלד בטופס מוצג כטקסט ואף פעם לא רץ.

מה ההבדל בין htmlspecialchars ל-htmlentities?

htmlspecialchars() ממירה רק את חמשת התווים המיוחדים ב-HTML. htmlentities() ממירה גם כל תו שיש לו ישות עם שם, ולכן café הופך ל-caf&eacute;. בדפי UTF-8 שתיהן בטוחות באותה מידה, ו-htmlspecialchars() שומרת על הפלט קריא, ולכן היא הבחירה הרגילה.

האם עדיין צריך ENT_QUOTES ב-PHP 8?

לא בשביל בטיחות: מאז PHP 8.1 הדגלים של ברירת המחדל הם ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ולכן גם גרשיים בודדים עוברים escape. בסיסי קוד רבים עדיין מעבירים ENT_QUOTES, 'UTF-8' במפורש כדי שהקריאה תתנהג אותו דבר בגרסאות ישנות ותהיה ברורה לקוראים.

להשתמש ב-htmlspecialchars בקלט או בפלט?

בפלט. שמרו ואמתו את הערך הגולמי, ובצעו לו escape ברגע שאתם מדפיסים אותו לתוך HTML. escape בקלט שומר &lt; במסד הנתונים שלכם, שובר אורכים וחיפושים, ומוביל ל-escape כפול כמו &amp;lt;.

האם strip_tags מספיקה כדי למנוע XSS?

לא. strip_tags() מסירה תגיות, אבל פרמטר התגיות המותרות שלה שומר על המאפיינים שלהן, ולכן <b onclick="..."> שורד, והיא לא עושה כלום לטקסט שמוצב בתוך מאפיין. השתמשו ב-htmlspecialchars() כשמדפיסים קלט משתמש.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל