אני אכתוב כמה מילים על שירותי ווב מאובטחים. כשאני בונה WebService ואני רוצה שהמשתמשים יזדהו לפני השימוש, אז נכון שאני יכול לדרוש שחלק מהפרמטרים יהיו השם משתמש וסיסמה, אבל זו לא הדרך הטובה יותר, דרך יותר נכונה תהיה להשתמש ב Header שהסרביס מקבל, והוא יכיל את שם המשתמש והסיסמה.
בואו ניצור משהו פשוט:
עכשיו ניצור מחלקה חדשה שתייצג את ה Header שלנו
public class SecurityHeader : System.Web.Services.Protocols.SoapHeader
{
public string username;
public string pass;
}
כעת נגיד לשירות שלנו שהוא אמור לחכות ל Header הזה
[WebMethod]
[SoapHeader("SecurityHeader")]
public string HelloWorldSecure() {
כפי שניתן לראות מדובר בסך הכל בתוספת של שורה אחת
עכשיו עוד דבר קטן, נגדיר משתנה גלובלי מסוג המחלקה של ה Header שלנו
public class SayHello : System.Web.Services.WebService
{
public SecurityHeader secHeader;
כעת כל פעם שמישהו יקרה לסרביס שלנו, השרת יבדוק אם יש לו Header מהסוג שציינו, ואם כן הוא יכניס את הערכים שהוא קיבל למשתנה הגלובלי שלנו במחלקה. כעת בתוך השירות אנחנו רק צריכים לבדוק שאכן המשתנה הגלובלי קיבל ערך(אכן יש כזה Header) ואז ניתן לבדוק את הנתונים שלו (שם משתמש וסיסמה במקרה שלנו)
if (secHeader == null)
{
return "Please authenticate!";
}
if (secHeader.username == "tal" && secHeader.pass == "giladi")
{
return "Shalom " + secHeader.username;
}
else
{
return "Bad username or password";
}
עכשיו להשתמש בזה בצד הלקוח זה עוד יותר קל אפילו...
ניצור הפניה לשירות שלנו
קראתי להפניה הזאת server_sayHello
ניצור אובייקט של ההפניה הזאת
server_sayHello.SayHello sayHello = new server_sayHello.SayHello();
ניצור הפניה של ה Header שלנו באותה צורה
server_sayHello.SecurityHeader secHeader = new server_sayHello.SecurityHeader();
ניתן ערכים לשם והסיסמה
secHeader.UserName = "tal";
secHeader.Password = "giladi";
sayHello.SecurityHeaderValue = secHeader;
וזהו... פשוט נקרא לשירות:
string result = sayHello.HelloWorldSecure();
* אם אנחנו רוצים שהשירות יהיה מאובטח גם בעזרת SSL,(בהנחה שיש לכם תעודה והגדרתם אותה לשרת) פשוט נעשה מה שאנחנו עושים בדף ווב רגיל,
נוסיף שורה בסרביס עצמו, בתוך הפונקציה שלנו:
if (!HttpContext.Current.Request.IsSecureConnection)
{
return "Please use secure connection!";
}
להורדת הפרוייקט
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
בהצפנה סימטרית, הכוונה היא שניתן לבצע הצפנה של הטקסט(encryption) והפיכת הטקסט המוצפן לטקסט קריא שוב(decryption), בעזרת אותו מפתח. זאת אומרת שאם יש לי את הסיסמא(המפתח) אני יכול גם להצפין קוד וגם לקרוא אותו, להבדיל משיטות אחרות שבהם יש מפתח שיכול רק להצפין למשל (נראה בע"ה בפוסטים אחרים...)
טוב, אז איך זה הולך...
קודם נבחר שיטת הצפנה, דוט נט מציע מספר אלגוריתמים שמבצעים הצפנה סימטרית, אולם רק אחד מהם הוא בקוד מנוהל, ולכן נבחר אותו וניצור אובייקט חדש
using System.Security.Cryptography;
RijndaelManaged myAlg = new RijndaelManaged();
למרות שמפתח ההצפנה (סיסמא) אמור להיות סיסמא אחת, בפועל יש לנו 2 סיסמאות (הסיסמא השניה נקראת IV – וקטור האיתחול ובעצם מגבירה את רמת הבטיחות, אבל לשם הפשטות נתייחס אל שניהם כסיסמאות)
ניצור לנו 2 סיסמאות
String pass1=”mypassword1”;
String pass2=”mypassword2”;
כדי שאלגוריתם ההצפנה יוכל להשתמש בסיסמאות עלינו להפוך אותם למערך של בתים באורך קבוע של 32 ו 16 בתים בהתאמה. נהפוך את הסטרינגים למערך בתים ונשתמש בפונקציית עזר שתבצע את בדיקת האורך:
byte[] password1= Encoding.UTF8.GetBytes(pass1);
password1= SetArraySize(password1, 32);
byte[] password2= Encoding.UTF8.GetBytes(pass2);
password2= SetArraySize(password2, 16);
נהפוך המידע שברצונו להצפין למערך של בתים:
String clearText=”my credit card number”;
byte [] data= Encoding.UTF8.GetBytes(clearText);
כעת ניתן לאלגוריתם ההצפנה את הסיסמאות:
myAlg.Key = key;
myAlg.IV = IV;
כעת ניצור את האובייקט שישתמש באלגוריתם כדי להצפין את המידע :
ICryptoTransform encryptor = myAlg.CreateEncryptor();
ניצור אובייקט שיכיל את התוצאה של ההצפנה:
MemoryStream outstream = new MemoryStream ();
נגדיר לאובייקט ההצפנה לבצע הצפנה (ולא קריאה מטקסט מוצפן)
CryptoStream encryptStream = new CryptoStream (outstream, encryptor, CryptoStreamMode.Write);
ונבצע את ההצפנה:
encryptStream.Write(data, 0, data.Length);
encryptStream.Close();
והתוצאה התקבלה כמערך של בתים:
byte [] result= outstream.ToArray();
ניתן כמובן להפוך לסטרינג קריא :
String encryptedText= Convert.ToBase64String(result);
זהו... עכשיו, כדי שהצד השני יוכל לקרוא את הטקסט המוצפן, הוא צריך לקבל את הסטרינג המוצפן וכמובן את 2 הסיסמאות/מפתחות שבהם השתמשנו, שזה אחד החסרונות בהצפנה סימטרית- להעביר את המפתח לצד השני...
בהנחה שהצד השני קיבל את הטקסט המוצפן ואת המפתחות, הפעולות שהוא אמור לבצע הן כמעט זהות לתהליך ההצפנה:
ליצור את אלגוריתם ההצפנה
RijndaelManaged myAlg = new RijndaelManaged();
לאתחל אותו עם 2 המפתחות שהוא קיבל
myAlg.Key = key;
myAlg.IV = IV;
להמיר את הטקסט המוצפן למערך של בתים שוב
byte [] encrypted= Convert.Frombase64String(encryptedText);
ליצור את הזרם שממנו יקרא אובייקט ההצפנה את מערך הבתים:
MemoryStream instream = new MemoryStream (encrypted);
היכן לשמור את התוצאה:
byte [] result = new byte [instream.Length];
כעת השינוי החשוב- ניצור את אובייקט ההצפנה עם הוראה לקרוא מטקסט מוצפן:
ICryptoTransform decryptor = myAlg.CreateDecryptor();
CryptoStream decryptStream = new CryptoStream (instream, decryptor, CryptoStreamMode.Read);
ונקרא את הטקסט:
decryptStream.Read(result, 0, result.Length);
התוצאה נמצאת כעת ב result , ניתן להפוך לסטרינג שוב:
String clearText= Encoding.UTF8.GetString(result);
Download Example
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
נשאלתי לא מזמן לגבי מפתח ציבורי ופרטי, בדרך כלל השימוש הוא שבמפתח הציבורי מצפינים את המידע ובפרטי מפענחים כדי שכולם יוכלו לכתוב לי ורק אני אוכל לקרוא את ההודעות שלהם, אך מה לגבי פעולה הפוכה? שאני אצפין עם המפתח הפרטי וכולם יוכלו לקרוא? מה הטעם תשאלו? פשוט מאוד, אני רוצה לכתוב הודעה ושכולם יוכלו לקרוא, אבל שיהיו בטוחים שאני כתבתי? נשמע מוכר...? ברור כי זו בדיוק הפעולה של חתימה דיגילית... אז מתברר שמישהו במיקרוסופט שכח להוסיף את האפשרות הזאת למפתח פרטי/ציבורי, למרות שלפי ההגדרה של המפתחות זה כן אפשרי... אבל כן נשארה לנו האופציה של חתימה דיגיטלית באמצעות Sign Data
בואו נראה קצת קוד. קודם ניצור מופע מהמחלקה DSACryptoServiceProvider שבאמצעותה תבוצע ההצפנה
DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();
כעת ניצור 2 מפתחות - הפרטי שבאצעותו ניתן לחתום מידע ואותו נשמור אצלנו, וציבורי שבאמצעותו ניתן רק להשוות חתימה למידע ולוודא שאכן החתימה שייכת למידע מסוים אך לא ניתן ליצור חתימות חדשות באמצעותו את המפתח הפרטי ניצור באמצעות:
DSAParameters privateKey = dsa.ExportParameters(true);
ואת הציבורי באותה צורה, אך מבלי להכליל את המידע הדרוש כדי לבצע חתימה:
DSAParameters publicKey = dsa.ExportParameters(false);
כעת נבצע את החתימה:
byte[] clearData = Encoding.UTF8.GetBytes(args[0]);
string signedData = Convert.ToBase64String(dsa.SignData(data, 0, data.Length));
ועכשיו אני יכול לשלוח לצד השני את המידע עצמו, את החתימה שעכשיו יצרתי ואת המפתח הציבורי (כמובן שעדיף שהמפתח הציבורי יהיה אצלהם לפני כדי שיהיו בטוחים שהוא אכן שלי...)
הצד השני ינסה להשוות את החתימה למידע כדי לדעת שהמידע אכן הגיע ממני ונחתם על ידי
קודם הוא יצור שוב מופע מהמחלקה שתוודא את החתימה, אך הוא יתן לה את המפתח הציבורי ששלחנו לו בתור פרמטר:
DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();
dsa.ImportParameters(publicKey);
כעת המחלקה הזאת יכולה רק לוודא חתימה, ורק את החתימה שלי, אז בואו אכן נוודא:
dsa.VerifyData(clearData, Convert.FromBase64String( signedData));
להורדת הפרוייקט
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5
Hash, או בתרגום חופשי – ערך מספרי יחודי למידע, נותן חתימה יחודית לכל מידע. הסבר- כתבתי מכתב, קיבלתי את ערך ה Hash שלו שהוא לדוגמא 1b1e2cca18d2beec33272ea8b3f4c271, אז תיאורטית (כן, רק תיאורטית...) , שום מכתב או מידע בעולם לא יתן לי את אותו הערך מלבד אותו המכתב, מה שגם אומר שאם מישהו ישנה אפילו אות אחת במכתב, אז ערך ה Hash ישתנה.
למה זה טוב? אז קודם כל כבר מהדוגמא ניתן לראות שככה אפשר לבדוק אם מידע כלשהו שיצרתי, השתנה על ידי מישהו בדרך.
שימוש נפוץ מאוד הוא לשמירת סיסמאות בדטה בייס. במקום לשמור את הסיסמא של המשתמש כמו שהיא, ואז אם מישהו יצליח איכשהו לפרוץ לדטה בייס הוא יראה את כל הסיסמאות ויוכל להשתמש בחשבונות של אנשים עד סוף החיים, אני שם את ערך ה Hash של הסיסמאות, וכשמישהו מנסה לעשות לוגין למערכת, אני עושה Hash על הסיסמא שהוא סיפק ומשווה לזאת שבדטה בייס. דרך אגב, זוהי תכונה/דרישה נוספת של Hash - שלא ניתן יהיה באמצעות ערך ה Hash לשחזר את המידע המקורי שנתן את הערך הזה, כך שאם מישהו רואה את ערך ה Hash של סיסמא בדטה בייס, הוא לא יכול למצוא מה היתה הסיסמא המקורית (גם בתיאוריה... ישנם הרבה כגון kmd5 ו- john the ripper לשיחזור סיסמאות מ Hash ). למידע תיאורטי נוסף
אז איך משתמשים? פשוט ביותר, בוחרים אלגוריתם Hash. דוט נט מציעה מספר אלגוריתם כגון SHA1,SHA256,SHA512,MD5,HMACSHA1,MAC3DES ועוד כמה ששונים בעיקר בחוזק האלגוריתם (ככל שהוא יותר חזק כך יש יותר סיכויים שהוא ישמור על יחודיות הערך ועל קושי שיחזור המידע המקורי)
כל האלגוריתם יורשים ממחלקה אחת:
using System.Security.Cryptography;
HashAlgorithm myhash = null;
נבחר את MD5 שהוא הסטנדרט בהרבה ארגונים:
myhash = new MD5CryptoServiceProvider();
ניתן לו את המידע שאנחנו רוצים לחשב עבורו את הערך:
string mydata=”shalom”;
byte[] data = Encoding.UTF8.GetBytes(mydata);
myhash.ComputeHash(data);
נמשוך את התוצאה :
byte[] result = hash.Hash;
ניתן להפוך לסטרינג כמובן:
String strResult = Convert.ToBase64String(result);
שני סוגי ה Hash האחרונים שהצגתי שונים מעט. הם יוצרים Hash למידע אך מוסיפים גם סיסמא, מה שאומר שאם מישהו אחר יקבל את המידע ויעשה עליו Hash, הוא לא יקבל את אותה תוצאה אם אין לו את הסיסמא, כך שניתן לשלוח למישהו מידע עם ערך ה Hash ובהנחה שהצד השני יודע את הסיסמא, הוא יוכל לבדוק אם המידע במכתב לא שונה בדרך. השימוש די זהה, ההבדל היחיד הוא שביצירת האובייקט אנחנו נותנים לו את הסיסמא גם :
byte[] key=Encoding.UTF8.GetBytes(“my password”);
myhash = new HMACSHA1(key);
* אם משתמשים באלגוריתם השני שמאפשר סיסמא - MACTripleDES יש לשים לב שאורך הסיסמא אמור להיות בגדלים קבועים של 8, 16 או 24 בתים וממשיכים כרגיל.
להורדת קבצי המקור והפרוייקט
Technorati Tags:
Security,
Hash
Be the first to rate this post
- Currently 0/5 Stars.
- 1
- 2
- 3
- 4
- 5