לדלג לתוכן

4.3 חולשת csrf הרצאה

CSRF – Cross-Site Request Forgery

חולשת CSRF (Cross-Site Request Forgery) היא פגיעות אבטחה שבה תוקף מנצל את האמון של אתר מסוים על דפדפן המשתמש, כדי לבצע פעולות זדוניות בשם המשתמש בלי ידיעתו.

כיצד פועלת מתקפת CSRF?

  1. משתמש נכנס לאתר מהימן (למשל, אתר בנק) ומבצע התחברות.

  2. דפדפן המשתמש שומר עוגיה (cookie) שמכילה את מזהה ההתחברות.

  3. התוקף שולח למשתמש קישור זדוני (באימייל, בצ'אט או דרך אתר נגוע).

  4. המשתמש לוחץ על הקישור, שמבצע בקשה זדונית לאתר המהימן תוך שימוש בעוגיות השמורות.

  5. הפעולה מתבצעת בלי ידיעת המשתמש, כי הבקשה מגיעה מדפדפן שכבר מחובר לחשבון.


קוד PHP פגיע ל-CSRF

1. שינוי כתובת אימייל ללא הגנה

קובץ change_email.php שמאפשר למשתמשים לעדכן כתובת אימייל:

<?php
session_start();
if (!isset($_SESSION['user_id'])) {
    die("Unauthorized");
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $new_email = $_POST['email'];
    $db = new PDO("mysql:host=localhost;dbname=test", "root", "");
    $stmt = $db->prepare("UPDATE users SET email = ? WHERE id = ?");
    $stmt->execute([$new_email, $_SESSION['user_id']]);
    echo "Email updated successfully!";
}
?>

מה הבעיה?

  • אין הגנה נגד CSRF – כל אתר זדוני יכול לשלוח בקשת POST בשמו של המשתמש.

  • אם המשתמש מחובר לאתר, התוקף יכול לגרום לו לשנות את כתובת האימייל ללא ידיעתו.


דוגמה לתקיפה עם CSRF

1. HTML זדוני להחלפת אימייל

תוקף יוצר דף עם הקוד הבא ושולח קישור אליו למשתמש התמים:

<form action="https://example.com/change_email.php" method="POST">
    <input type="hidden" name="email" value="hacker@evil.com">
    <input type="submit" value="Click me!">
</form>
<script>document.forms[0].submit();</script>
  • ברגע שהמשתמש נכנס לדף, הסקריפט ישלח בקשה אוטומטית לאתר עם העוגיות השמורות.

  • כתובת האימייל תשתנה לכתובת של התוקף.

כלומר, הפעלנו על מישהו csrf רק מזה שהוא נכנס לאתר שלנו, עם קוד javascript שלנו.


כיצד למנוע CSRF?

1. שימוש ב-CSRF Token

session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        die("CSRF attack detected!");
    }
    $new_email = $_POST['email'];
    $db = new PDO("mysql:host=localhost;dbname=test", "root", "");
    $stmt = $db->prepare("UPDATE users SET email = ? WHERE id = ?");
    $stmt->execute([$new_email, $_SESSION['user_id']]);
    echo "Email updated successfully!";
}
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
?>
<form method="POST">
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
    <input type="email" name="email">
    <input type="submit" value="Update Email">
</form>
  • כל טופס מקבל csrf_token ייחודי שנדרש בכל בקשת POST.

  • התקפה CSRF לא יכולה להתבצע ללא ה-token, כי התוקף לא יכול לגשת אליו.

עכשיו, רק בקשות שיוצאות מאותו עמוד של הform יוכלו לבצע את הבקשה לאתר, כי רק אותו dom יודע מה הcsrf token שנוצר.
כלומר, רק אם יהיה לנו xss באותו dom נוכל להשיג את הcsrf token.
אם יש לנו xss באתר מסוים, נסו לחשוב איזה csrf-ים נוכל לעשות מאותו עמוד.

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