מדיניות HMAC

מדיניות רגילה

הדף הזה רלוונטי ל-Apigee ול-Apigee Hybrid.

לעיון במסמכי התיעוד של Apigee Edge

מדיניות ה-HMAC מחשבת ומאמתת באופן אופציונלי קוד אימות הודעות מבוסס-גיבוב (HMAC). לפעמים נקרא גם קוד אימות הודעה עם מפתח או גיבוב עם מפתח. ב-HMAC נעשה שימוש בפונקציית גיבוב קריפטוגרפית כמו SHA-1,‏ SHA-224,‏ SHA-256,‏ SHA-384,‏ SHA-512 או MD-5, שמוחלת על 'הודעה', יחד עם מפתח סודי, כדי ליצור חתימה או קוד אימות הודעה על ההודעה הזו. המונח 'הודעה' כאן מתייחס לכל זרם של בייטים. בשימוש רגיל ב-HMAC, שולח ההודעה שולח הודעה ואת ה-HMAC שלה לנמען, והנמען יכול להשתמש ב-HMAC יחד עם סוד לשימוש עם טוקן צרכן כדי לאמת את ההודעה.

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

מידע נוסף על HMAC זמין במאמר HMAC: Keyed-Hashing for Message Authentication (rfc2104).

דוגמאות

יצירת HMAC

<HMAC name='HMAC-1'>

  <Algorithm>SHA256</Algorithm>

  <!-- the default encoding of the SecretKey is UTF-8 -->
  <SecretKey encoding='base64' ref='private.secretkey'/>

  <IgnoreUnresolvedVariables>true|false</IgnoreUnresolvedVariables> <!-- optional -->

  <!--
    The Message element accepts a template, which means the "message" the policy operates
    on can include fixed and multiple variable parts, including newlines and static functions.
    Whitespace, such as newlines and space characters, is significant.
   -->
  <Message>Fixed Part
{a_variable}
{timeFormatUTCMs(timeFormatString1,system.timestamp)}
{nonce}</Message>

  <!-- default encoding is base64 -->
  <Output encoding='base16'>name_of_variable</Output>

</HMAC>

אימות HMAC

<HMAC name='HMAC-1'>

  <Algorithm>SHA256</Algorithm>

  <!-- the default encoding of the SecretKey is UTF-8 -->
  <SecretKey encoding='base16' ref='private.secretkey'/>

  <IgnoreUnresolvedVariables>true|false</IgnoreUnresolvedVariables> <!-- optional -->

  <!--
     The Message element accepts a template. This policy verifies an HMAC on the request content.
   -->
  <Message>{request.content}</Message>

  <!--
    VerificationValue is optional.
    Include it to perform an HMAC check.
  -->
  <VerificationValue encoding='base16' ref='expected_hmac_value'/>

  <!-- default encoding of the output is base64 -->
  <Output encoding='base16'>name_of_variable</Output>

</HMAC>

חישוב החתימה ואימות החתימה מתבצעים בדיוק באותו תהליך. מדיניות ה-HMAC מחשבת HMAC, ויכולה לאמת את החתימה המחושבת מול ערך צפוי. האלמנט האופציונלי VerificationValue (אם הוא קיים) מכוון את המדיניות לבדוק את הערך המחושב מול ערך ידוע או נתון.


הפניה לרכיב של HMAC

במאמר בנושא הפניות למדיניות מפורטים האלמנטים והמאפיינים של מדיניות HMAC.

מאפיינים שחלים על הרכיב ברמה העליונה

<HMAC name="HMAC" continueOnError="false" enabled="true" async="false">

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

מאפיין תיאור ברירת מחדל נוכחות
name השם הפנימי של המדיניות. התווים שאפשר להשתמש בהם בשם מוגבלים ל:A-Z0-9._\-$ %. עם זאת, בממשק המשתמש של Apigee יש הגבלות נוספות, כמו הסרה אוטומטית של תווים שהם לא אלפאנומריים.

אופציונלי, אפשר להשתמש ברכיב <DisplayName> כדי לתת למדיניות שם אחר בשפה טבעית בכלי לעריכת ה-proxy בממשק המשתמש של Apigee.

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

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

FALSE אופציונלי
מופעל כדי לאכוף את המדיניות, צריך להגדיר את הערך true.

מגדירים את הערך false כדי להשבית את המדיניות. המדיניות לא תיאכף גם אם היא תישאר מצורפת לזרימה.

TRUE אופציונלי
אסינכרוני המאפיין הזה הוצא משימוש. FALSE הוצא משימוש

<Algorithm>

<Algorithm>algorithm-name</Algorithm>

מציין את אלגוריתם הגיבוב שבו יש להשתמש כשמחשבים את ה-HMAC.

ברירת מחדל לא רלוונטי
נוכחות חובה
סוג String
ערכים אפשריים SHA-1, SHA-224, SHA-256, SHA-384, SHA-512 וגם MD-5

ההגדרה של המדיניות מקבלת שמות של אלגוריתמים בלי להבחין בין אותיות רישיות לאותיות קטנות, ועם או בלי מקף בין האותיות והמספרים. לדוגמה, SHA256 ו-SHA-256 ו-sha256 שווים זה לזה.

<DisplayName>

<DisplayName>Policy Display Name</DisplayName>

משתמשים בו בנוסף למאפיין name כדי לתת למדיניות תווית בכלי לעריכת ה-proxy בממשק המשתמש של Apigee, עם שם אחר בשפה טבעית.

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

<Message>

<Message>message_template_here</Message>
or
<Message ref='variable_here'/>

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

<Message>Fixed Part
    {a_variable}
    {timeFormatUTCMs(timeFormatString1,system.timestamp)}
    {nonce}
</Message>

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

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

<Output>

<Output encoding='encoding_name'>variable_name</Output>

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

ברירת מחדל

משתנה הפלט שמוגדר כברירת מחדל הוא hmac.POLICYNAME.output.

ערך ברירת המחדל של מאפיין encoding הוא base64.

נוכחות זה שינוי אופציונלי. אם הרכיב הזה לא קיים, המדיניות מגדירה את משתנה הזרימה hmac.POLICYNAME.output עם ערך בקידוד Base64.
סוג String
ערכים אפשריים

לקידוד, hex, base16, ‏base64, ‏base64url.

הערכים לא תלויי-רישיות. hex ו-base16 הם מילים נרדפות.

ערך הטקסט של רכיב Output יכול להיות כל שם חוקי של משתנה זרימה.

<SecretKey>

<SecretKey encoding='encoding_name' ref='private.secretkey'/>

קובעת את המפתח הסודי שמשמש לחישוב ה-HMAC. המפתח מתקבל מהמשתנה שאליו מתבצעת ההפניה, והוא מפוענח בהתאם לקידוד הספציפי.

ברירת מחדל

אין ערך ברירת מחדל למשתנה שאליו יש הפניה; המאפיין ref הוא חובה.

אם לא מצוין מאפיין encoding, המדיניות מפענחת כברירת מחדל את מחרוזת המפתח הסודי באמצעות UTF-8 כדי לקבל את בייטי המפתח.

נוכחות חובה
סוג String
ערכים אפשריים

בפרמטר encoding, הערכים התקינים הם hex,‏ base16,‏ base64 ו-utf8. ברירת המחדל היא UTF8. הערכים לא תלויי-רישיות, והמקפים לא משמעותיים. הערך Base16 זהה לערכים base-16 ו-bAse16. הערכים Base16 ו-Hex הם מילים נרדפות.

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

 <SecretKey encoding='hex' ref='private.encodedsecretkey'/>

נניח ש-private.encodedsecretkey מכיל את המחרוזת 536563726574313233.

במקרה הזה, הבייטים המרכזיים יפוענחו כך: [53 65 63 72 65 74 31 32 33] (כל בייט מיוצג בפורמט הקסדצימלי). דוגמה נוספת: אם משתמשים ב-encoding='base64', ואם private.encodedsecretkey מכיל את המחרוזת U2VjcmV0MTIz, התוצאה תהיה אותו מערך של בייטים עבור המפתח. אם לא מציינים מאפיין קידוד, או אם מציינים מאפיין קידוד עם הערך UTF8, ערך המחרוזת Secret123 יניב את אותה קבוצת בייטים. בדוגמה הזו מוצגות שלוש דרכים שונות לייצוג של אותו מקש.

<VerificationValue>

<VerificationValue encoding='encoding_name' ref='variable_name'/>
or
<VerificationValue encoding='encoding_name'>string_value</VerificationValue>

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

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

הערכים התקפים של מאפיין הקידוד הם: hex, ‏ base16,‏ base64, ‏ base64url. הערכים לא תלויי רישיות. hex ו-base16 הם מילים נרדפות.

הקידוד של VerificationValue לא צריך להיות זהה לקידוד שמשמש לרכיב Output.

<IgnoreUnresolvedVariables>

<IgnoreUnresolvedVariables>true|false</IgnoreUnresolvedVariables>

מגדירים את הערך false אם רוצים שהמדיניות תציג שגיאה כשלא ניתן לפתור משתנה כלשהו שמוגדר במדיניות. הגדרה ל-true תגרום להתייחסות לכל משתנה שלא ניתן לפתור כמחרוזת ריקה (null).

הערך הבוליאני IgnoreUnresolvedVariables משפיע רק על משתנים שההפניה אליהם מופיעה בתבנית ההודעה. אפשר להפנות למשתנה גם ב-SecretKey וגם ב-VerificationValue, אבל צריך להיות אפשר לפתור את שניהם, ולכן ההגדרה ignore לא חלה עליהם.

ברירת מחדל לא נכון
נוכחות אופציונלי
סוג בוליאני
ערכים אפשריים true or false

משתנים בתהליך

המדיניות יכולה להגדיר את המשתנים האלה במהלך ההפעלה.

משתנה תיאור דוגמה
hmac.policy_name.message המדיניות מגדירה את המשתנה הזה עם ההודעה האפקטיבית, שהיא התוצאה של הערכת תבנית ההודעה שצוינה ברכיב Message. hmac.HMAC-Policy.message = "Hello, World"
hmac.policy_name.output מקבל את התוצאה של חישוב ה-HMAC, כשאלמנט Output לא מציין שם משתנה. hmac.HMAC-Policy.output = /yyRjydfP+fBHTwXFgc5AZhLAg2kwCri+e35girrGw4=
hmac.policy_name.outputencoding מקבל את השם של קידוד הפלט. hmac.HMAC-Policy.outputencoding = base64

בעיות נפוצות

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

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

הבדלים ברווחים הלבנים

הבדלים שאולי נראים לא חשובים לבני אדם משפיעים על ערך ה-HMAC של הפלט. לדוגמה, נניח שיש מפתח סודי שאפשר לייצג אותו כ-Secret123. אחרי פענוח UTF-8, בייטי המפתח יהיו: [53 65 63 72 65 74 31 32 33]. שימוש במפתח הזה כדי לחשב HMAC-SHA256 בהודעה abc יניב את התוצאה a7938720fe5749d31076e6961360364c0cd271443f1b580779932c244293bc94. הוספת רווח יחיד להודעה, כך שהיא תהיה abc<SPACE>, כאשר <SPACE> מייצג ASCII 32, תניב את התוצאה 274669b2a85d2532da48e2ce3d8e52ee17346d1bcd1a606d87db1934b5ab294b.

באופן דומה, אם ההודעה היא abc<NEWLINE>, כאשר <NEWLINE> מרמז ASCII 10, ה-HMAC הוא 0780370844ca07f896066837e8230d3b6a775f678a4ae03e6b5e864c674831f5. שינויים קלים בהודעה יוצרים ערכי HMAC שונים מאוד. זהו עיצוב מכוון. זוהי התנהגות מכוונת ורצויה של HMAC.

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

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

<HMAC name='HMAC-1'>
    ...
    <!-- the result of this message template will include surrounding whitespace -->
    <Message>
        {request.content}
    </Message>
    ...

</HMAC>

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

<HMAC name='HMAC-1'>
    ...
    <Message>{request.content}</Message>
    ...

</HMAC>

הבדלים בקידוד

פענוחים שונים של אותו חומר מפתח יוצרים מפתחות שונים. נניח שיש מפתח סודי שאפשר לייצג אותו בתור U2VjcmV0S2V5MTIz. אחרי פענוח בקידוד UTF-8, בייטי המפתח כמוצג בקידוד base16 יהיו: [55 32 56 6a 63 6d 56 30 53 32 56 35 4d 54 49 7a]. אחרי פענוח בקידוד Base64, בייטי המפתח יהיו [53 65 63 72 65 74 4b 65 79 31 32 33]. פענוח שונה של חומר המקור יוביל למפתחות שונים, וכתוצאה מכך לערכי HMAC שונים.

המסקנה: חשוב לוודא שחומר המפתח שמשמש לחישוב ה-HMAC המקורי והמפתח שמשמש לאימות ה-HMAC זהים לחלוטין. המשמעות היא כנראה שצריך לוודא שמשתמשים באותו קידוד של המפתח בשני הצדדים. במדיניות HMAC, אפשר להשתמש במאפיין encoding ברכיב SecretKey כדי לציין את קידוד המפתח.

כדאי גם לשקול קידודים בפלט. ‫HMAC-SHA256 בקידוד base16 או הקסדצימלי, שמוצג כ-27f17e11c8ece93844c5eb5e55161d993368628a214f9a51c25d0185e8ea06e2, זהה ל-HMAC-SHA256 בקידוד base64, שמוצג כ-J/F+Ecjs6ThExeteVRYdmTNoYoohT5pRwl0BhejqBuI=. הן נראות שונות, אבל שתי המחרוזות האלה מייצגות את אותו ערך. מוודאים שנעשה שימוש באותו קידוד כדי לקודד את ה-HMAC שחושב במקור ואת ה-HMAC שמאמת. במדיניות HMAC, אפשר להשתמש במאפיין encoding ברכיב Output כדי לציין את קידוד הפלט הרצוי, ובאותו מאפיין ברכיב VerificationValue כדי לציין איך לפענח את מאמת החשבון.

הפניה לשגיאה

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

שגיאות זמן ריצה

השגיאות האלה יכולות להתרחש כשהמדיניות מופעלת.

קוד תקלה סטטוס HTTP מתרחש כאשר
steps.hmac.UnresolvedVariable 401

השגיאה הזו מתרחשת אם משתנה שצוין במדיניות HMAC הוא:

  • לא רלוונטי (לא זמין בתהליך הספציפי שבו המדיניות מופעלת)

    או

  • אי אפשר לפתור (לא מוגדר)
steps.hmac.HmacVerificationFailed 401 אימות ה-HMAC נכשל. ערך האימות שסופק לא תואם לערך המחושב.
steps.hmac.HmacCalculationFailed 401 המדיניות לא הצליחה לחשב את ה-HMAC.
steps.hmac.EmptySecretKey 401 הערך של משתנה המפתח הסודי ריק.
steps.hmac.EmptyVerificationValue 401 המשתנה שמכיל את ערך האימות ריק.

שגיאות בהטמעה

השגיאות האלה יכולות להתרחש כשפורסים שרת proxy שמכיל את המדיניות הזו.

שם השגיאה סטטוס HTTP מתרחש כאשר
steps.hmac.MissingConfigurationElement 401 השגיאה הזו מתרחשת כשחסר אלמנט או מאפיין נדרש.
steps.hmac.InvalidValueForElement 401 השגיאה הזו מתרחשת אם הערך שצוין ברכיב Algorithm הוא לא אחד מהערכים הבאים: SHA-1,‏ SHA-224,‏ SHA-256,‏ SHA-512 או MD-5.
steps.hmac.InvalidSecretInConfig 401 השגיאה הזו מתרחשת אם יש ערך טקסט שצוין במפורש עבור SecretKey.
steps.hmac.InvalidVariableName 401 השגיאה הזו מתרחשת אם המשתנה SecretKey לא מכיל את הקידומת private (private.).

משתני תקלות

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

משתנים כאשר: דוגמה
fault.name="fault_name" fault_name הוא שם התקלה, כפי שמופיע בטבלה שגיאות בזמן ריצה שלמעלה. שם התקלה הוא החלק האחרון של קוד התקלה. fault.name Matches "UnresolvedVariable"
hmac.policy_name.failed המדיניות מגדירה את המשתנה הזה במקרה של כשל. hmac.HMAC-Policy.failed = true

דוגמה לתגובת שגיאה

לצורך טיפול בשגיאות, מומלץ ללכוד את החלק errorcode בתגובת השגיאה. אל תסתמכו על הטקסט ב-faultstring, כי הוא עשוי להשתנות.

דוגמה לכלל שגיאה

<FaultRules>
    <FaultRule name="HMAC Policy Errors">
        <Step>
            <Name>AM-Unauthorized</Name>
            <Condition>(fault.name Matches "HmacVerificationFailed")</Condition>
        </Step>
        <Condition>hmac.HMAC-1.failed = true</Condition>
    </FaultRule>
</FaultRules>