הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

פרילאנסרים? תתחילו בלהבין מה אתם מוכרים

25/05/2019

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

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

וזה לא שלא היה לי תיק עבודות, הבעיה היתה במה שהיה בתיק.

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

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

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

בעיה של עומס

23/05/2019

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

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

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

מהי בעיית Key Reuse בעבודה עם מצפין זרם

22/05/2019

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

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

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

def rc4(data, key):
    """RC4 encryption and decryption method."""
    S, j, out = list(range(256)), 0, []

    for i in range(256):
        j = (j + S[i] + ord(key[i % len(key)])) % 256
        S[i], S[j] = S[j], S[i]

    i = j = 0
    for ch in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        out.append(chr(ord(ch) ^ S[(S[i] + S[j]) % 256]))

    return "".join(out)

הטעות הכי גדולה של אנשים שמשתמשים במצפין זרם נקראת Key Reuse. זה אומר להשתמש באותו Key Stream כדי להצפין מספר הודעות. ולמה זה רע? כי בגלל הטבע של XOR במצב כזה המפתחות "מתבטלים". במתמטיקה זה אומר ש:

C1 = Message1 ^ Key
C2 = Message2 ^ Key
=>

C1 ^ C2 = M1 ^ Key ^ M2 ^ Key
C1 ^ C2 = M1 ^ M2

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

אז הנה דוגמא בשפת פייתון של תוכנית שמצפינה שתי הודעות ושומרת 3 קבצים: קובץ אחד מכיל את ההודעה הראשונה מוצפנת, קובץ שני את ההודעה השניה והקובץ השלישי את ה XOR בין שני הקבצים המוצפנים:

from rc4 import rc4

enc1 = rc4('hello world', 'secret')
enc2 = rc4('\x00' * 10, 'secret')

with open('msg1.bin', 'w') as f:
    f.write(enc1)

with open('msg2.bin', 'w') as f:
    f.write(enc2)

with open('msg_xor.bin', 'w') as f:
    xor_enc = ''.join([chr(ord(a) ^ ord(b)) for a,b in zip(enc1,enc2)])
    f.write(xor_enc)

נמשיך לראות את הקבצים שנוצרו:

$ xxd msg1.bin
00000000: c285 53c2 be70 c3ad c284 c2a1 c389 40c2  ..S..p........@.
00000010: a7c3 9f                                  ...

$ xxd msg2.bin 
00000000: c3ad 36c3 921c c282 c2a4 c396 c2a6 32c3  ..6...........2.
00000010: 8b                                       .

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

$ xxd msg_xor.bin 
00000000: 6865 6c6c 6f20 776f 726c                 hello worl

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

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

from rc4 import rc4

enc1 = rc4('hello world', 'secret1')
enc2 = rc4('\x00' * 10, 'secret2')

with open('msg1.bin', 'w') as f:
    f.write(enc1)

with open('msg2.bin', 'w') as f:
    f.write(enc2)

with open('msg_xor.bin', 'w') as f:
    xor_enc = ''.join([chr(ord(a) ^ ord(b)) for a,b in zip(enc1,enc2)])
    f.write(xor_enc)

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

$ xxd msg_xor.bin 
00000000: c398 6ac2 9070 0b0e 3257 c3bb 5d         ..j..p..2W..]

שאלות מראיונות עבודה - מיון רשימה

21/05/2019

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

https://www.hackerrank.com/challenges/new-year-chaos/problem

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

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

[2, 1, 5, 3, 4]

נצטרך לפחות 3 פעולות החלפה כדי למיין אותה:

  1. קודם נחליף את ה-1 וה-2

  2. אחרי זה נחליף את ה-3 וה-5

  3. אחרי זה נחליף את ה-4 וה-5 (ליד ה-4 היה פעם 3, אבל אחרי ההחלפה הקודמת יש שם עכשיו 5).

ובשביל לסבך קצת את החיים יש גם תוספת קטנה - כל פעם שמחליפים שני מספרים אנחנו רושמים את המספר השמאלי ביניהם בצד ברשימת ה"עוקפים" (כי בשביל להגיע למצב המבולגן הוא היה צריך לעקוף את המספר שמימינו). אם אחד המספרים "עקף" יותר מפעמיים עלינו להפסיק ולהדפיס את הפלט "Too chaotic".

המשך קריאה

העתקת קוד ואבטחת מידע

20/05/2019

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

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

https://shasaurabh.blogspot.com/2017/07/virtual-machine-detection-techniques.html

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

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

סט-אפים

19/05/2019

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

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

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

אני לא ממליץ על הגישה הזאת.

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

איך אתרים נפרצים?

18/05/2019

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

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

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

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

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

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

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

https://www.tocode.co.il/workshops/76

נתראה ינון

עיבוד נתונים מקבילי עם תבנית Producer/Consumer

17/05/2019

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

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

תבנית Producer/Consumer עוזרת לנו להתמודד עם עיבוד מקבילי של מידע ומומלצת בתרחישים כאלה ורבים נוספים. נמשיך לראות איך זה בנוי.

המשך קריאה

אינטואיציה

16/05/2019

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

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

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

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

  2. לשמוע הרצאות של כותבי אותם פרויקטים - לראות מה "דוחף" אותם, מה גרם להם לבחור בשיטה מסוימת ולא בשיטה אחרת, מה היו הדילמות שלהם.

  3. לכתוב "יומן אינטואיציה" שאומר שאם נראה לכם שתבנית מסוימת הולכת להצליח (או להיכשל) אז תכתבו את זה גם בקוד וגם ביומן. אחרי כמה חודשים חיזרו למה שרשמתם ובידקו אם צדקתם.

  4. לקחת פרויקט קוד פתוח ולנסות לנחש איך הם התמודדו עם בעיה מסוימת (למשל איך gon מעביר את המשתנים מריילס ל JS). אחרי זה מסתכלים בקוד שלהם ורואים אם צדקתם.

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

יש לכם רעיונות נוספים? מוזמנים לשתף בתגובות.