JavaRush /בלוג Java /Random-HE /אבטחה ב-Java: שיטות עבודה מומלצות
Roman Beekeeper
רָמָה

אבטחה ב-Java: שיטות עבודה מומלצות

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

1. ספק אבטחה ברמת שפת Java

קודם כל, האבטחה ב-Java מתחילה ממש ברמת תכונת השפה. זה מה שהיינו עושים אם לא היו משנים גישה?... אנרכיה, לא פחות. שפת תכנות עוזרת לנו לכתוב קוד מאובטח וגם לנצל תכונות אבטחה מרומזות רבות:
  1. הקלדה חזקה. Java היא שפה בהקלדה סטטית המספקת את היכולת לזהות שגיאות הקלדה בזמן ריצה.
  2. משנה גישה. הודות להם, אנו יכולים להגדיר גישה למחלקות, שיטות ושדות מחלקות בצורה שאנו צריכים.
  3. ניהול זיכרון אוטומטי. למטרה זו, יש לנו (ג'אביסטים ;)) את Garbage Collector, שמשחרר אותך מתצורה ידנית. כן, לפעמים מתעוררות בעיות.
  4. בדיקת קוד בייט: Java מבצעת קומפילציה לקוד בייט, שנבדק על ידי זמן ריצה לפני הפעלתו.
בין היתר ישנן המלצות של אורקל בנושא אבטחה. כמובן, זה לא כתוב ב"סגנון גבוה" ואתה עלול להירדם כמה פעמים בזמן הקריאה, אבל זה שווה את זה. מסמך חשוב במיוחד הוא הנחיות הקידוד המאובטח עבור Java SE , המספק עצות כיצד לכתוב קוד מאובטח. מסמך זה מכיל מידע שימושי רב. אם אפשר, זה בהחלט שווה קריאה. כדי לעורר עניין בחומר זה, הנה כמה טיפים מעניינים:
  1. הימנע מסידרה של מחלקות רגישות לאבטחה. במקרה זה, אתה יכול לקבל את ממשק הכיתה מהקובץ בסידרה, שלא לדבר על הנתונים שעוברים בסידרה.
  2. נסה להימנע משיעורי נתונים הניתנים לשינוי. זה נותן את כל היתרונות של מחלקות בלתי ניתנות לשינוי (למשל בטיחות חוטים). אם יש אובייקט בר שינוי, זה עלול להוביל להתנהגות בלתי צפויה.
  3. צור עותקים של אובייקטים הניתנים לשינוי שהוחזרו. אם שיטה מחזירה הפניה לאובייקט פנימי שניתן לשינוי, אז קוד הלקוח יכול לשנות את המצב הפנימי של האובייקט.
  4. וכולי…
באופן כללי, הנחיות הקידוד המאובטח עבור Java SE מכילות קבוצה של טיפים וטריקים כיצד לכתוב קוד ב-Java בצורה נכונה ומאובטחת.

2. הסר את פגיעות הזרקת SQL

פגיעות ייחודית. הייחודיות שלו טמונה בעובדה שהיא גם אחת הפגיעות המפורסמות וגם אחת הפגיעות הנפוצות ביותר. אם אתה לא מתעניין בנושא האבטחה, אז לא תדע על זה. מהי הזרקת SQL? זוהי התקפה על מסד נתונים על ידי הזרקת קוד SQL נוסף במקום בו הוא אינו צפוי. נניח שיש לנו שיטה שלוקחת פרמטר כלשהו כדי לבצע שאילתות במסד הנתונים. לדוגמה, שם משתמש. הקוד עם הפגיעות ייראה בערך כך:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Пишем sql request в базу данных с нашим firstName
   String query = "SELECT * FROM USERS WHERE firstName = " + firstName;

   // выполняем request
   Statement statement = connection.createStatement();
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводит ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводит в коллекцию юзеров
}
בדוגמה זו, השאילתה sql מוכנה מראש בשורה נפרדת. נראה שמה הבעיה, נכון? אולי הבעיה היא שעדיף להשתמש בו String.format? לא? מה אז? בואו נשים את עצמנו במקום בודק ונחשוב מה אפשר להעביר בערך firstName. לדוגמה:
  1. אתה יכול להעביר את המצופה - שם המשתמש. אז מסד הנתונים יחזיר את כל המשתמשים עם השם הזה.
  2. אתה יכול להעביר מחרוזת ריקה: אז כל המשתמשים יוחזרו.
  3. או שאתה יכול להעביר את הדברים הבאים: "''; זרוק משתמשי שולחן;". וכאן יהיו בעיות גדולות יותר. שאילתה זו תסיר את הטבלה ממסד הנתונים. עם כל הנתונים. כל אחד.
האם אתה יכול לדמיין לאילו בעיות זה יכול לגרום? ואז אתה יכול לכתוב מה שאתה רוצה. אתה יכול לשנות את השם של כל המשתמשים, אתה יכול למחוק את הכתובות שלהם. היקף החבלה הוא עצום. כדי להימנע מכך, עליך להפסיק להזריק שאילתה מוכנה ובמקום זאת לבנות אותה באמצעות פרמטרים. זו צריכה להיות הדרך היחידה לבצע שאילתות במסד הנתונים. כך תוכל לבטל את הפגיעות הזו. דוגמא:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Создаем параметризированный request.
   String query = "SELECT * FROM USERS WHERE firstName = ?";

   // Создаем подготовленный стейтмент с параметризованным requestом
   PreparedStatement statement = connection.prepareStatement(query);

   // Передаем meaning параметра
   statement.setString(1, firstName);

   // выполняем request
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводим ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводим в коллекцию юзеров
}
כך נמנעת פגיעות זו. למי שרוצה לצלול עמוק יותר לתוך מאמר זה, הנה דוגמה מצוינת . איך אתה יודע אם אתה מבין את החלק הזה? אם הבדיחה למטה התבררה, אז זה סימן בטוח שמהות הפגיעות ברורה :D אבטחה ב-Java: שיטות עבודה מומלצות - 2

3. סרוק ושמור על התלות מעודכנת

מה זה אומר? למי שלא יודע מהי תלות, אסביר: זה ארכיון jar עם קוד שמחובר לפרויקט באמצעות מערכות בנייה אוטומטיות (Maven, Gradle, Ant) על מנת לעשות שימוש חוזר בפתרון של מישהו אחר. למשל, Project Lombok , שמייצר עבורנו גטרים, מגדירים וכו' בזמן ריצה. ואם אנחנו מדברים על יישומים גדולים, הם משתמשים בתלותיות רבות ושונות. חלקם טרנזיטיביים (כלומר, לכל תלות יכולות להיות תלות משלה, וכן הלאה). לכן, תוקפים שמים לב יותר ויותר לתלות בקוד פתוח, מכיוון שהם נמצאים בשימוש קבוע ועלולים לגרום לבעיות עבור לקוחות רבים. חשוב לוודא שאין נקודות תורפה ידועות בכל עץ התלות (שזה בדיוק נראה). ויש כמה דרכים לעשות זאת.

השתמש ב-Snyk לניטור

הכלי Snyk בודק את כל התלות בפרויקט ומסמן פגיעויות ידועות. שם תוכל להירשם ולייבא את הפרויקטים שלך דרך GitHub, למשל. אבטחה ב-Java: שיטות עבודה מומלצות - 3כמו כן, כפי שניתן לראות מהתמונה למעלה, אם לגרסה חדשה יותר יש פתרון לפגיעות זו, Snyk יציע לעשות זאת וליצור Pull-Request. ניתן להשתמש בו ללא תשלום עבור פרויקטים בקוד פתוח. פרויקטים ייסרקו בתדירות מסוימת: פעם בשבוע, פעם בחודש. נרשמתי והוספתי את כל המאגרים הציבוריים שלי לסריקת Snyk (אין בזה שום דבר מסוכן: הם כבר פתוחים לכולם). לאחר מכן, Snyk הראה את תוצאת הסריקה: אבטחה ב-Java: שיטות עבודה מומלצות - 4ולאחר זמן מה, Snyk-bot הכין מספר Pull-Requests בפרויקטים שבהם יש צורך לעדכן תלות: אבטחה ב-Java: שיטות עבודה מומלצות - 5והנה עוד אחד: אבטחה ב-Java: שיטות עבודה מומלצות - 6אז זהו כלי מצוין לחיפוש נקודות תורפה ומעקב אחר עדכון גרסאות חדשות.

השתמש במעבדת האבטחה של GitHub

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

הפעל את Sonatype DepShield

אם אתה משתמש ב-GitHub כדי לאחסן את המאגרים שלך, אתה יכול להוסיף אחד מהיישומים לפרויקטים שלך מ-MarketPlace - Sonatype DepShield. בעזרתו תוכלו גם לסרוק פרויקטים לאיתור תלות. יתר על כן, אם הוא מוצא משהו, תיווצר בעיית GitHub עם תיאור מתאים, כפי שמוצג להלן: אבטחה ב-Java: שיטות עבודה מומלצות - 7

4. טפל בנתונים רגישים בזהירות

אבטחה ב-Java: שיטות עבודה מומלצות - 8בדיבור באנגלית, הביטוי "נתונים רגישים" נפוץ יותר. חשיפת מידע אישי, מספרי כרטיסי אשראי ומידע אישי אחר של הלקוח עלול לגרום לנזק בלתי הפיך. קודם כל, עליך לבחון מקרוב את עיצוב האפליקציה ולקבוע אם יש צורך בנתונים. אולי אין צורך בחלקם, אבל הם נוספו לעתיד שלא הגיע וספק אם יגיע. בנוסף, במהלך רישום הפרויקט, נתונים כאלה עלולים לדלוף. דרך פשוטה למנוע ממידע רגיש להיכנס ליומנים שלך היא לנקות את השיטות toString()של ישויות דומיין (כגון משתמש, תלמיד, מורה וכדומה). זה ימנע הדפסת שדות רגישים בטעות. אם אתה משתמש ב-Lombok כדי ליצור שיטה toString(), אתה יכול להשתמש בהערה @ToString.Excludeכדי למנוע שימוש בשדה בפלט באמצעות השיטה toString(). כמו כן, היזהר מאוד בעת שיתוף נתונים עם העולם החיצון. לדוגמה, ישנה נקודת קצה http המציגה את שמות כל המשתמשים. אין צורך להציג את המזהה הייחודי הפנימי של המשתמש. למה? מכיוון שהשימוש בו, תוקף יכול להשיג מידע אחר וסודי יותר על כל משתמש. לדוגמה, אם אתה משתמש בג'קסון כדי להסיד ולהעביר POJOs בסידרה ל- JSON , אתה יכול להשתמש בהערות @JsonIgnoreו @JsonIgnorePropertiesכדי למנוע משדות ספציפיים להיות מסודרים והדרדרים. באופן כללי, אתה צריך להשתמש בשיעורי POJO שונים עבור מקומות שונים. מה זה אומר?
  1. כדי לעבוד עם מסד הנתונים, השתמש רק ב-POJO - Entity.
  2. כדי לעבוד עם לוגיקה עסקית, העבר את הישות למודל.
  3. כדי לעבוד עם העולם החיצון ולשלוח בקשות http, השתמש בישויות שלישיות - DTO.
כך ניתן להגדיר בצורה ברורה אילו שדות יהיו גלויים מבחוץ ואילו לא.

השתמש באלגוריתמי הצפנה וגיבוב חזקים

נתוני לקוחות סודיים חייבים להישמר בצורה מאובטחת. לשם כך עליך להשתמש בהצפנה. בהתאם למשימה, עליך להחליט באיזה סוג של הצפנה להשתמש. יתרה מכך, הצפנה חזקה יותר לוקחת יותר זמן, אז שוב אתה צריך לשקול עד כמה הצורך בה מצדיק את הזמן המושקע בה. כמובן, אתה יכול לכתוב את האלגוריתם בעצמך. אבל זה מיותר. ניתן לנצל את הפתרונות הקיימים בתחום זה. לדוגמה, Google Tink :
<!-- https://mvnrepository.com/artifact/com.google.crypto.tink/tink -->
<dependency>
   <groupId>com.google.crypto.tink</groupId>
   <artifactId>tink</artifactId>
   <version>1.3.0</version>
</dependency>
בוא נראה איך משתמשים בזה, בעזרת הדוגמה של איך להצפין בדרך זו או אחרת:
private static void encryptDecryptExample() {
   AeadConfig.register();
   KeysetHandle handle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_CTR_HMAC_SHA256);

   String plaintext = "Цой жив!";
   String aad = "Юрий Клинских";

   Aead aead = handle.getPrimitive(Aead.class);
   byte[] encrypted = aead.encrypt(plaintext.getBytes(), aad.getBytes());
   String encryptedString = Base64.getEncoder().encodeToString(encrypted);
   System.out.println(encryptedString);

   byte[] decrypted = aead.decrypt(Base64.getDecoder().decode(encrypted), aad.getBytes());
   System.out.println(new String(decrypted));
}

הצפנת סיסמא

עבור משימה זו, הכי בטוח להשתמש בהצפנה אסימטרית. למה? מכיוון שהאפליקציה באמת לא צריכה לפענח סיסמאות בחזרה. זו הגישה הכללית. במציאות, כאשר משתמש מזין סיסמה, המערכת מצפינה אותה ומשווה אותה למה שנמצא בכספת הסיסמאות. ההצפנה מתבצעת באותם אמצעים, כך שתוכלו לצפות שיתאימו (אם תזין את הסיסמה הנכונה ;), כמובן). BCrypt ו- SCrypt מתאימות למטרה זו. שתיהן פונקציות חד-כיווניות (hashs קריפטוגרפיים) עם אלגוריתמים מורכבים מבחינה חישובית שלוקחים הרבה זמן. זה בדיוק מה שאתה צריך, שכן פענוחו חזיתי ייקח לנצח. לדוגמה, Spring Security תומך במגוון אלגוריתמים. SCryptPasswordEncoderאתה יכול גם להשתמש BCryptPasswordEncoder. מהו אלגוריתם הצפנה חזק כעת עשוי להיות חלש בשנה הבאה. כתוצאה מכך, אנו מסיקים כי יש צורך לבדוק את האלגוריתמים בהם נעשה שימוש ולעדכן ספריות באלגוריתמים.

במקום פלט

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

קישורים שימושיים

כן, כמעט כל המאמרים באתר כתובים באנגלית. בין אם נרצה ובין אם לא, אנגלית היא השפה של מתכנתים לתקשר. כל המאמרים, הספרים והמגזינים החדשים ביותר בנושא תכנות כתובים באנגלית. לכן הקישורים שלי להמלצות הם בעיקר באנגלית:
  1. Habr: הזרקת SQL למתחילים
  2. אורקל: Java Security Resource Center
  3. אורקל: הנחיות קידוד מאובטח עבור Java SE
  4. Baeldung: היסודות של אבטחת Java
  5. בינוני: 10 טיפים לחיזוק אבטחת Java שלך
  6. Snyk: 10 שיטות עבודה מומלצות לאבטחת Java
  7. JR: הכרזה על GitHub Security Lab: הגנה על כל הקוד שלך ביחד
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION