# אבטחה ומצב taint Security in a Perl program is mostly about not trusting data that came from outside the program: command-line arguments, the environment, file contents, network input. The dangerous step is not reading that data but *acting* on it. A filename that arrives from a web form is harmless until it reaches `open`; an environment variable is harmless until it steers a `system` call. This page describes the mechanism PetaPerl gives you to make that boundary explicit: taint mode. It explains what taint mode is, when it turns on, what it marks as untrusted, and how data is cleaned. ## מצב taint PetaPerl runs the perl5 5.42 core, so taint mode is the real perl5 taint machinery, not an approximation. Every check perl5 performs, PetaPerl performs. מצב taint נדלק אוטומטית כאשר PetaPerl מזהה שהיא רצה עם מזהי משתמש או קבוצה ממשיים ואפקטיביים שונים, וכך מורץ סקריפט setuid (מצב Unix `04000`) או setgid (מצב `02000`). תוכל גם להפעיל אותו במפורש באמצעות מתג שורת הפקודה [`-T`](../../guide/oneliner/switches.md). `-T` מומלץ בחום עבור כל תוכנית שרצה מטעם מישהו אחר, כאשר סקריפט CGI הוא המקרה הקלאסי. לאחר שמצב taint דלוק, הוא נשאר דלוק עד סוף התוכנית; לא ניתן לכבותו מבפנים. המתג חייב להופיע מוקדם מספיק כדי שהמפרש יראה אותו לפני שיתחיל לפרש את תוכניתך. בשורת ה-`#!` של סקריפט זה קורה אוטומטית. בשורת הפקודה, מקם אותו בין המתגים הראשונים. המתג הקרוב [`-t`](../../guide/oneliner/switches.md) מפעיל את אותן הבדיקות אך מוריד את השגיאות הקטלניות לאזהרות, מה שמועיל בעת התאמת מצב taint לתוכנית קיימת: אתה רואה כל מקום ש *היה* נכשל בלי שהתוכנית תמות בראשון שבהם. המשתנה לקריאה בלבד `${^TAINT}` מדווח על המצב הנוכחי: | `${^TAINT}` | משמעות | |---------------|----------------------------------------| | `1` | מצב taint דלוק, קטלני (`-T`) | | `-1` | בדיקות taint דלוקות, אזהרה בלבד (`-t`) | | `0` | מצב taint כבוי | ## מה נעשה tainted כאשר מצב taint דלוק, כל ערך שהגיע מחוץ לתוכנית מסומן כ-tainted, וסימון זה מתפשט דרך כל ביטוי שהערך נוגע בו. המקורות ה-tainted הם: - ארגומנטים של שורת הפקודה (`@ARGV`) והסביבה ([`%ENV`](perlvar/args-env-inc.md)); - כל קלט מקובץ; - התוצאות של [`readdir`](perlfunc/readdir.md) ו-`readlink`, המשתנה הנקרא על ידי `shmread`, ההודעות מ-`msgrcv`, ושדות הסיסמה, ה-gcos וה-shell המוחזרים על ידי קריאות `getpw*`; - נתוני locale (ראה [`perllocale`](perllocale.md)). אין להשתמש בנתונים tainted, במישרין או בעקיפין, בשום פעולה שמפעילה תת-shell או שמשנה קובץ, ספרייה או תהליך. שימוש בהם בפעולה כזו הוא קטלני, עם הודעה כמו `Insecure dependency in %s` או `Insecure $ENV{PATH}`. ישנם שלושה חריגים: - ארגומנטים ל-[`print`](perlfunc/print.md) ול-`syswrite` **אינם** נבדקים; - קריאות מתודה סימבוליות (`$obj->$method(@args)`) והפניות סימבוליות לשגרות (`$foo->(@args)`) **אינן** נבדקות, ולכן אמת את השמות הללו בעצמך אם הם יכולים להגיע מבחוץ; - מפתחות hash **לעולם** אינם tainted. מטעמי יעילות, taint נעקב באופן שמרני: אם חלק כלשהו של ביטוי הוא tainted, הביטוי כולו מטופל כ-tainted, אפילו במקום שבו הערך ה-tainted אינו משפיע למעשה על התוצאה. taint נצמד לערכים סקלריים בודדים, ולכן חלק מהאיברים של מערך או hash יכולים להיות tainted בעוד אחרים לא. ```perl my $arg = shift; # tainted (from @ARGV) my $hid = $arg . 'bar'; # also tainted my $line = ; # tainted (file input) my $data = 'abc'; # not tainted system "echo $arg"; # dies: Insecure dependency system "/bin/echo", $arg; # dies: tainted argument system "echo $data"; # dies until PATH is cleaned (see below) ``` החריג המבני היחיד ל-tainting של ביטוי שלם הוא האופרטור התלת-מקומי [`?:`](perlop.md). משום ש ```perl my $result = $tainted ? "untainted" : "also untainted"; ``` הוא רק בחירה מותנית בין שני קבועים, `$result` אינו tainted אף על פי שהתנאי כן. ## כיבוס וזיהוי של נתונים tainted הדרך **היחידה** לנקות את ה-taint מערך היא לחלץ ממנו תת-מחרוזת באמצעות לכידה של ביטוי רגולרי. PetaPerl מניחה שאם טרחת לכתוב תבנית ולשלוף את `$1`, חשבת על מה שאתה מוכן לקבל. בטל את ה-taint על ידי אימות מול רשימה לבנה של תווים קבילים, לעולם לא על ידי ניסיון להסיר את הרעים: ```perl if ($data =~ /^([-\@\w.]+)$/) { $data = $1; # $data is now untainted } else { die "rejected suspicious input"; } ``` זה בטוח באופן סביר משום ש-`\w`, נקודה, מקף וסימן at אינם נושאים משמעות מיוחדת ל-shell. תבנית כמו `/(.+)/` הייתה ”מכבסת“ את הערך תוך קבלת כל דבר, מה שמסכל את המטרה. היה מכוון לגבי התבנית. ישנה הסתייגות הנוגעת ל-locale. תחת [`use locale`](perllocale.md) קבוצת התווים ש-`\w` מתאים אליהם נשאבת בעצמה מה-locale, שהוא נתון חיצוני ש-PetaPerl אינה מהימנה עליו, ולכן התאמת `\w` תחת `use locale` **אינה** מבטלת taint. אם עליך לכבס עם `\w` בתוכנית מודעת-locale, מקם `no locale` לפני ההתאמה באותו הבלוק. To test whether a value is tainted, use `tainted` from the [`Scalar::Util`](../Scalar/Util.md) module. Under `-T` it correctly reports the taint state of its argument. ## ניקוי של `%ENV` ושל `$ENV{PATH}` `PATH` שהוא tainted הוא ההפתעה הנפוצה ביותר. משום ש-PetaPerl אינה יכולה לדעת שתוכנית שהיא מריצה לא תיוועץ בעצמה ב-`PATH`, היא מסרבת להתחיל תת-תהליך כל עוד `$ENV{PATH}` הוא tainted, עם `Insecure $ENV{PATH}`. כל ספרייה בנתיב חייבת להיות מוחלטת וניתנת לכתיבה רק על ידי בעליה וקבוצתה; ספרייה ניתנת לכתיבה מניבה `Insecure directory in %s`. שים לב שרכיב נתיב ריק מטופל כ-`.` (הספרייה הנוכחית) וגם הוא מפעיל את ההודעה. `PATH` אינו האשם היחיד. משום ש-shell עשוי להיוועץ ב-`IFS`, `CDPATH`, `ENV` ו-`BASH_ENV`, PetaPerl דורשת שאלה יהיו ריקים או לא-tainted לפני שתתחיל תת-תהליך, ומדווחת `Insecure $ENV{%s}` אחרת. הגדר `PATH` ידוע כתקין ומחק את משתני ה-shell מוקדם: ```perl $ENV{PATH} = '/bin:/usr/bin'; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; ``` כאשר עליך לפתוח קובץ או צינור מתוכנית מורשית (setuid או setgid), הדפוס הבטוח הוא לבצע fork לתהליך-בן, לגרום לתהליך-הבן לזנוח את הרשאותיו בחזרה למשתמש ולקבוצה הממשיים, ולתת לתהליך-הבן הבלתי-מורשה לבצע את העבודה ולהעביר את התוצאה בחזרה דרך צינור: ```perl use English; die "can't fork: $!" unless defined(my $pid = open(my $kid, "-|")); if ($pid) { # parent while (<$kid>) { # consume the child's output } close $kid; } else { # child my @priv = ($EUID, $EGID); my $real_uid = $UID; my $real_gid = $GID; $EUID = $UID; $EGID = $GID; $UID = $real_uid; # drop privileges $GID = $real_gid; ($EUID, $EGID) = @priv; # make sure they are gone die "can't drop privileges" unless $UID == $EUID && $GID == $EGID; $ENV{PATH} = '/bin:/usr/bin'; exec 'myprog', 'arg1', 'arg2' or die "can't exec myprog: $!"; } ``` שים לב ש-[`exec`](perlfunc/exec.md) נקרא עם רשימה, לעולם לא עם מחרוזת בודדת, ולכן אין shell מעורב כדי להרחיב מחדש את הארגומנטים. זוהי הדרך הבטוחה ביותר להפעיל פקודה חיצונית. אותה אסטרטגיית fork-and-drop עובדת עבור [`glob`](perlfunc/glob.md) ו-backticks, אשר (בניגוד ל-`system` ו-`exec`) אינם מציעים מוסכמת קריאה בצורת רשימה משלהם. ## מצב taint ו-`@INC` תחת `-T`, PetaPerl מתעלמת ממשתני הסביבה `PERL5LIB`, `PERLLIB` ו-`PERL_USE_UNSAFE_INC`. מתעלמים מהם משום שקל להגדירם מבלי שמשתמש ישים לב, מה שהיה מאפשר לסביבה להזריק ספריות ספרייה בחשאי. עדיין תוכל להתאים את נתיב חיפוש המודולים באמצעות המתג הגלוי `-I` או הפרגמה `lib` (`-Mlib=/path`), משום שאלה מופיעים בבירור בשורת הפקודה. הספרייה הנוכחית `.` **אינה** ב-`@INC` של ברירת המחדל, בין אם מצב taint דלוק ובין אם לא. (`PERL_USE_UNSAFE_INC=1` היה מוסיף אותה מחדש, אך משתנה זה עצמו נמצא בהתעלמות תחת `-T`, ולכן `.` נשאר מחוץ ל-`@INC` עבור כל תוכנית tainted.) ## סקריפטים של set-id ומרוץ ה-shebang PetaPerl מזהה את תנאי ה-setuid/setgid (מזהה משתמש או קבוצה ממשי השונה מהאפקטיבי) ומפעילה את מצב taint בהתאם; הניסוח ”while running setuid“ / ”while running setgid“ בהודעות חוסר-האבטחה נובע מהזיהוי הזה. PetaPerl **אינה**, עם זאת, מספקת התקנת `pperl` של setuid או כל מסייע set-id. אם דרושה לך תוכנית פרל מורשית, הגישה הנתמכת ב-Linux היא עוטף setuid ייעודי קטן שמחטא את הסביבה ואז מבצע exec למפרש על נתיב סקריפט קבוע ובלתי-ניתן-לכתיבה, במקום להסתמך על סקריפט setuid ישירות. מרוץ ה-shebang הקלאסי, שבו הקרנל והמפרש פותחים את קובץ הסקריפט בשני רגעים שונים ותוקף מחליף את הקובץ בינתיים, הוא תכונה של טיפול הקרנל בסקריפטי set-id, לא של המפרש, וגישת העוטף עוקפת אותו. ## התקפות סיבוכיות אלגוריתמית ניתן לדחוף אלגוריתמים פנימיים מסוימים לצרוך זמן או זיכרון לא-פרופורציונליים באמצעות קלט שנבחר בקפידה, וזהו וקטור מניעת-שירות הבלתי-תלוי במצב taint. מחלקות האיום: - **Hash collisions.** An attacker who can predict the hash seed can craft keys that all land in one bucket, degrading hash operations. pperl randomises the hash seed at process start and randomises the visible order of [`keys`](perlfunc/keys.md), [`values`](perlfunc/values.md), and [`each`](perlfunc/each.md) per hash. Two environment variables override these defaults for debugging: `PERL_HASH_SEED` pins the seed (which controls storage, not the presented order) and `PERL_PERTURB_KEYS` controls traversal-order perturbation; the [`Hash::Util`](../Hash/Util.md) function [`hash_traversal_mask`](../Hash/Util/hash_traversal_mask.md) does the same per hash. These overrides weaken the protection and are not advised in production. PetaPerl has never guaranteed any hash key ordering; do not rely on it, and do not use the pseudo-random ordering as a shuffle. - **מעקב-לאחור קטסטרופלי של regex.** מנוע הביטויים הרגולריים הוא NFA ויכול לקחת זמן או מקום שגדל בתלילות כאשר תבנית יכולה להתאים לקלט בדרכים רבות. קלט עוין המותאם מול תבנית פגיעה יכול למצות זיכרון או CPU. תחום את גודל הקלט והעדף תבניות שאינן יכולות לבצע מעקב-לאחור באופן פתולוגי. - **מיון.** [`sort`](perlfunc/sort.md) משתמש ב-mergesort, שהמקרה הגרוע שלו חסום, ולכן לא ניתן לדחוף אותו להתנהגות ריבועית באמצעות קלט עוין. hashים מסוג tied עשויים לשאת סדר משלהם ומאפייני סיבוכיות משלהם. ## הבדלים מן ה-upstream - **Linux בלבד.** PetaPerl רצה רק על Linux. הדיון בנושאי set-id ואבטחה של VMS, Windows ופלטפורמות אחרות שפרל המקורית נושאת אין לו מקבילה כאן והוא פשוט נעדר. - **אין כלי ”הגנה“.** ל-PetaPerl אין מהדר bytecode ואין מנגנון ערפול של מסנני-מקור, ולכן העצה המקורית בדבר אספקת מקור מהודר או מסונן אינה חלה. ערפול מקור מעולם לא היה אמצעי אבטחה בכל מקרה. - **תא ה-`Safe` עדיין אינו זמין.** המודול `Safe` (והמודול `Opcode` שעליו הוא נשען) עדיין אינו זמין ב-PetaPerl; טעינתו כיום נכשלת עם `dynamic loading not available in this perl`. עד שיהיה, אל תסתמך על הגישה ”הרץ קוד בלתי-מהימן בתא מוגבל“: התייחס ל *קוד* בלתי-מהימן (להבדיל מנתונים בלתי-מהימנים) כאל משהו שיש להריץ ב-sandbox נפרד ברמת מערכת ההפעלה או בתהליך עם הרשאות זנוחות, לא בתוך המפרש. ## ראו גם - [`perlvar`](perlvar.md) - `${^TAINT}`, [`%ENV`](perlvar/args-env-inc.md), ומשתני המזהה הממשי/האפקטיבי `$<` `$>` `$(` `$)` שמכריעים אם מצב taint נדלק אוטומטית. - [`perllocale`](perllocale.md) - מדוע נתוני locale אינם מהימנים וכיצד `use locale` מתקשר עם כיבוס `\w`. - [`perlop`](perlop.md) - האופרטור [`?:`](perlop.md) והחריג שלו ל-tainting של ביטוי שלם. - [Command-line switches](../../guide/oneliner/switches.md) - המתגים `-T`, `-t` ו-`-I` בהקשר. - [Command-line options](../../cli_options.md) - כיצד pperl מפרשת את מתגיה, כולל בורר זמן הריצה. - [`Scalar::Util`](../Scalar/Util.md) - `tainted`, לבחינת מצב ה-taint של ערך. - [`Hash::Util`](../Hash/Util.md) - [`hash_traversal_mask`](../Hash/Util/hash_traversal_mask.md), לשליטה ברמת ה-hash בהגרלת סדר-המעבר.