1. Aklatan
  2. HTTP והרשת
  3. קודי סטטוס HTTP

Na-update 1 buwan ang nakalipas

מפרט ה-HTTP עשה בחירת שמות גרועה. 401 נקרא "Unauthorized" אך למעשה אומר unauthenticated — השרת לא יודע מי אתה. 403 "Forbidden" הוא כשל ההרשאה האמיתי — השרת יודע בדיוק מי אתה והחליט שאתה לא יכול לעשות את זה.

בלבול השמות הזה אחראי ליותר באגים ב-API מכמעט כל אי-הבנה אחרת ב-HTTP. ברגע שתראה את ההבחנה בבירור, לעולם לא תבלבל ביניהם שוב.

מטאפורת אבטחת הבניין

זו לא רק אנלוגיה — כך אימות והרשאות עובדים בפועל:

401 Unauthorized: אתה עומד בדלת בלי תג זיהוי. אבטחה לא יודעת מי אתה. הם לא יכולים לבדוק את רמת הגישה שלך כי הם לא יודעים את זהותך. הראה קודם את התג שלך.

403 Forbidden: יש לך תג זיהוי. אבטחה יודעת שאתה אליס מהנדסה. אבל אתה מנסה להיכנס לקומת המנהלים, ואליס מהנדסה לא נמצאת ברשימת הגישה. אף הצגה חוזרת של התג לא תשנה זאת.

השומר תמיד בודק זהות לפני שבודק הרשאות. אימות קודם להרשאה. תמיד.

401: "מי אתה?"

תגובת 401 אומרת:

  • לא סופקו פרטי אימות, או
  • פרטי האימות אינם תקינים (סיסמה שגויה, טוקן לא תקין), או
  • פרטי האימות פגו תוקף

הפתרון תמיד זהה: ספק פרטי אימות תקינים ונסה שוב.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"

{
    "error": "Authentication required"
}

כותרת WWW-Authenticate נדרשת על פי מפרט ה-HTTP. היא מודיעה ללקוח איזו שיטת אימות להשתמש — טוקני Bearer, אימות Basic, או משהו אחר. השמטת כותרת זו מהווה הפרה של המפרט.

מתי להחזיר 401

לא סופקו פרטי אימות:

GET /api/profile HTTP/1.1
// No Authorization header

→ 401 Unauthorized

פרטי אימות לא תקינים:

POST /api/login HTTP/1.1
{"username": "alice", "password": "wrong"}

→ 401 Unauthorized

טוקן שפג תוקפו:

GET /api/profile HTTP/1.1
Authorization: Bearer expired-token

→ 401 Unauthorized

כיצד לקוחות צריכים להגיב

כשמקבלים 401: בצע אימות מחדש. רענן את הטוקן. בקש מהמשתמש להתחבר שוב. לאחר מכן נסה שוב את הבקשה.

async function makeRequest(url) {
    const response = await fetch(url, {
        headers: { 'Authorization': `Bearer ${getToken()}` }
    });

    if (response.status === 401) {
        const newToken = await refreshToken();
        if (newToken) {
            return fetch(url, {
                headers: { 'Authorization': `Bearer ${newToken}` }
            });
        }
        redirectToLogin();
    }

    return response;
}

403: "התשובה היא לא"

תגובת 403 אומרת:

  • השרת יודע מי אתה (האימות הצליח)
  • אין לך הרשאה לבצע את הפעולה הספציפית הזו
  • אימות מחדש לא יעזור — זוהי החלטת מדיניות
HTTP/1.1 403 Forbidden

{
    "error": "You don't have permission to access this resource"
}

אין צורך בכותרת WWW-Authenticate. הבעיה אינה אימות.

מתי להחזיר 403

גישה לנתונים של משתמש אחר:

DELETE /api/users/456 HTTP/1.1
Authorization: Bearer valid-token-for-user-123

→ 403 Forbidden (you can only delete your own account)

חסר התפקיד הנדרש:

POST /api/admin/users HTTP/1.1
Authorization: Bearer valid-token-for-regular-user

→ 403 Forbidden (admin role required)

חשבון מושעה:

GET /api/data HTTP/1.1
Authorization: Bearer valid-token-for-suspended-user

→ 403 Forbidden (account suspended)

כיצד לקוחות צריכים להגיב

כשמקבלים 403: אל תנסה שוב. הבעיה אינה בפרטי האימות שלך — אלא בהרשאות שלך. הצג למשתמש הודעת שגיאה. הפנה אותו למקום שאליו הוא יכול לגשת.

if (response.status === 403) {
    showError("You don't have permission to do this");
    redirectToHome();
    // Do NOT retry. It won't help.
}

ההחלטה

כאשר מגיעה בקשה:

  1. האם פרטי אימות קיימים? לא → 401
  2. האם פרטי האימות תקינים? לא → 401
  3. האם למשתמש יש הרשאה? לא → 403
  4. אחרת → עבד את הבקשה

אימות מתרחש לפני הרשאה. לא ניתן לבדוק הרשאות למישהו שלא זיהית.

דוגמה מלאה

נקודת קצה המוגבלת למנהלים בלבד:

// ללא פרטי אימות
POST /api/admin/users HTTP/1.1
→ 401 Unauthorized

// פרטי אימות תקינים, משתמש רגיל
POST /api/admin/users HTTP/1.1
Authorization: Bearer token-for-alice
→ 403 Forbidden

// פרטי אימות תקינים, משתמש מנהל
POST /api/admin/users HTTP/1.1
Authorization: Bearer token-for-admin
→ 201 Created

מימוש בצד השרת

function requireAuth(request, response, next) {
    const token = request.headers.authorization?.split(' ')[1];

    if (!token) {
        return response.status(401)
            .set('WWW-Authenticate', 'Bearer realm="api"')
            .json({ error: 'Authentication required' });
    }

    try {
        request.user = verifyToken(token);
        next();
    } catch (error) {
        return response.status(401)
            .set('WWW-Authenticate', 'Bearer realm="api"')
            .json({ error: 'Invalid token' });
    }
}

function requireAdmin(request, response, next) {
    // This middleware assumes requireAuth already ran
    if (!request.user.isAdmin) {
        return response.status(403)
            .json({ error: 'Admin role required' });
    }
    next();
}

// שימוש: אימות קודם, לאחר מכן הרשאה
app.post('/api/admin/users', requireAuth, requireAdmin, handleCreateUser);

טעויות נפוצות

שימוש ב-401 כשהכוונה היא 403:

DELETE /api/users/456
Authorization: Bearer valid-token-for-user-123

❌ 401 Unauthorized  // שגוי — המשתמש כן מאומת
✓ 403 Forbidden

שימוש ב-403 כשהכוונה היא 401:

GET /api/profile
// No Authorization header

❌ 403 Forbidden  // שגוי — אינך יודע מי הם עדיין
✓ 401 Unauthorized

שכחת WWW-Authenticate:

❌ HTTP/1.1 401 Unauthorized
   {"error": "Not authenticated"}

✓ HTTP/1.1 401 Unauthorized
   WWW-Authenticate: Bearer realm="api"
   {"error": "Authentication required"}

שיקול אבטחה: הסתרת קיום המשאב

לעיתים תרצה להסתיר אם משאב קיים:

GET /api/documents/secret-id HTTP/1.1
Authorization: Bearer valid-token

// אפשרות 1: שקופה
→ 404 Not Found (חושפת שהמשאב לא קיים)

// אפשרות 2: עמומה
→ 403 Forbidden (מסתירה אם הוא קיים)

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

שאלות נפוצות על 401 ו-403

מדוע 401 נקרא "Unauthorized" אם הכוונה היא unauthenticated?

תאונה היסטורית. כאשר HTTP/1.0 הוגדר ב-1996, המינוח לא היה מדויק. עד שהבינו ש-"Unauthenticated" היה מבהיר יותר, השם כבר היה משובץ במערכות אינספור. המפרט תקוע עם השם המבלבל — אבל אתה לא חייב להתבלבל ממנו.

האם להחזיר 401 או 403 עבור טוקן שפג תוקפו?

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

מה לגבי מפתחות API? האם הם אימות או הרשאה?

מפתחות API הם אימות — הם מוכיחים זהות ("בקשה זו היא מאפליקציה X"). אם מפתח ה-API חסר או לא תקין, החזר 401. אם מפתח ה-API תקין אך אין לו הרשאה לפעולה זו, החזר 403.

האם אני יכול להשתמש ב-403 כדי להסתיר שמשאב לא קיים?

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

Nakatulong ba ang pahinang ito?

😔
🤨
😃