מדריך PHP

מדריך PHP: אבטחה

‏ • Internet Israel

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

הכלל הוא:

לא לסמוך על שום דבר שמגיע מהמשתמש לעולם

וכשאנו מדברים על דברים שמגיעים מהמשתמש זה כל מה שמגיע מה: $_GET, $_POST, $_SESSION, $_COOKIE, $_REQUEST וגם חלק מה-SERVER_$. כל אלו הם דברים שהמשתמש יכול לשנות כרצונו. אנו לא נסמוך לעולם על שום משתנה שהמשתמש יכול לשנות. אימוץ הכלל הזה אולי לא יהפוך את הסקריפט שלכם לחסין, אבל הוא יהפוך אותו להרבה יותר בטוח.

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

התקפת XSS

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

<?php
    $name = $_GET['name'];
    
    print 'hello '.$name;

תחשבו שכזה דף תמים נמצא אצלכם באתר, איך אפשר לעשות נזק עם כזה דף תמים? תכנסו לדף עם ה-URL הבא:

/PHPPage.php?name=<script>alert("TEST");</script>

XSS Attack Example

מה שיקרה הוא שנקבל alert עם test. תחשבו שבמקום alert נחמד אפשר לשים שם קוד ששולח את העוגיות הקשורות לשרת למקום אחר. עכשיו אני שולח את ה-URL למנהל האתר ו… בום! הוא נכנס והעוגיות שלו נגנבות ואז לי יש יכולת להכנס.

תחשבו שבמקום alert מדובר בקוד להשתלת וירוס או ב-redirection לאתר פורנו. כל מי שיכנס לכתובת הזו ייחשף לכל מה שמי שהזריק את הקוד ירצה לעשות.

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

<?php
preg_match('/^[a-z]+$/i',$_GET['name']) ? $name = $_GET['name'] : exit('XSS is detected!');

print 'hello '.$name;

משפט התנאי המקוצר שואל באופן פשוט – האם מה שיש ב-['GET['name_$ הוא רק אותיות מתחילת המחרוזת (^) ועד סופה? ($) במידה וכן, אז משתנה name יווצר. במידה ולא, אנחנו נהרוג את הסקריפט.

זוכרים שאמרתי שצריך להזהר מכל מה שמגיע מהמשתמש? גם אם זה משהו שמגיע מה-SERVER_$ ? שימו לב לדוגמא הבאה.

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

<?php
    
    print "Not found: " . urldecode($_SERVER['REQUEST_URI']);

מה שקורה פה הוא שהסקריפט משתמש במשתנה סופר גלובלי $_SERVER ומדפיס את ה-URL שניסינו להכנס אליו עם הודעה שהמשתנה לא נמצא. האם אפשר לסמוך על כזה דבר? לא! למה לא? בואו ונשמור את הדף תחת השם page404.php וננסה להכנס אליו כך:

/page404.php?a=<script>alert("TEST");</script>

מה שנראה זה את הדבר הבא:

XSS example with SERVER super global

גם את זה קל למחוק באמצעות פילטר או preg_match.

העניין הוא לא לנסות ולמנוע פריצת XSS אלא לפלטר ולבדוק כל דבר שמגיע מהמשתמש, כל דבר.

SQL Injection

ל-SQL Injection יש שם מטיל אימה, אבל האמת היא שכמו ה-XSS מדובר בפריצה שקל מאד למנוע אם אנו לא נותנים אמון בשום דבר שמגיע מהמשתמש. שימו לב למשל לקוד ה-PHP שלוקח את הסיסמה ושם המשתמש ובודק אותם במסד הנתונים כדי לדעת אם לאפשר כניסה למערכת:

<?php
    $username = $_POST['username'];
    $password = $_POST['password'];
    
    $sql_query = "SELECT uid FROM table_users WHERE username='.$username.' AND password='.$password;
    $result = mysql_query($query , $connection);  
    $uid = mysql_fetch_array($result);

מה שיש לנו פה זו שאילתא קטנה שמשתמשת בנתונים שמגיעים מהמשתמש כדי לבצע בדיקה של שם משתמש וסיסמה. מה יכול להיות רע בה? נסו להכניס לסיסמה 'OR 1=1' – מיד תקבלו גישה למערכת.

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

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

הצפנה של סיסמאות ו-SALT

בעוד שהגנה מפני פרצותSQL Injection ו-XSS הן חשובות, חשוב גם לחשוב מה קורה גם לאחר הפריצה. הרבה מאד אנשים משתמשים באותן סיסמאות בעבור אתרים שונים. על מנת למזער את הנזק שנוצר במידה ופרצו לך לאתר (וגם על מנת להגן על עצמך מתביעות בגין רשלנות) הצפנה של סיסמאות היא חשובה ביותר.

איך מצפינים ססמאות? באופן הבא:

בואו ונניח שיש לי טבלה עם ססמאות ושמות משתמש:

+---------+-----------+------------+
| idusers | user_name | password   |
+---------+-----------+------------+
|       1 | ran       | birthday16 | 
|       2 | moshe_r   | qwertyuiop |
|       3 | avico     | q1w2e3     | 
+---------+-----------+------------+

הטבלה היא פשוטה למדי וקל לבדוק את הסיסמאות של שם משתמש מסוים. אבל שימו לב לאותה טבלה, אך הפעם מאובטחת יותר.

+---------+-----------+----------------------------------+
| idusers | user_name | password                         |
+---------+-----------+----------------------------------+
|       1 | ran       | 2f0007585e5d4584adf91fe11ab16db7 | 
|       2 | moshe_r   | 6eea9b7ef19179a06954edd0f6c05ceb | 
|       3 | avico     | 7cbb3252ba6b7e9c422fac5334d22054 | 
+---------+-----------+----------------------------------+

מה עשיתי? העברתי את הסיסמה באמצעות מנגנון הצפנה שנקרא MD5. ההצפנה הזו אמורה להיות חד כיוונית – ברגע שאני מבצע הצפנת MD5 אני לא יכול לקחת את המחרוזת שהתקבלה ולהחזיר אותה לסיסמה המקורית. ככה עושים את זה:

$hash = md5($password);

איך נאמת את זהות המשתמש מול מסד הנתונים? מאד פשוט. אם הוא מקליד סיסמה, פשוט נמיר אותה ל-md5 ואז נוודא ששתי המחרוזות זהות. איך נשחזר את הסיסמה ללקוח? לא נוכל לעשות את זה. מה שנוכל לעשות זה לשלוח מייל ללקוח עם כתובת ובה משתנה GET. במידה והוא נכנס לכתובת אפשר לייצר עבורו סיסמה נוספת שתכנס (מוצפנת כמובן) למסד הנתונים או לבקש ממנו להקליד סיסמה חדשה שגם היא תעבור הצפנה.

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

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

תגיות: , ,

רן בר-זיק

מתכנת PHP מנוסה ובעל ידע רב בפיתוח לאינטרנט על בסיס LAMP. מומחה במערכות דרופל, ג'ומלה ו-וורדפרס. שולט היטב בכל מה שקשור לפרונט אנד: JavaScript: jQuery ו-MooTools,ActionScript 2/3 וכמובן HTML 5 + CSS 3.אינטרנט ישראל: www.internet-israel.com

תגובות בפייסבוק