JavaRush /وبلاگ جاوا /Random-FA /امنیت در جاوا: بهترین شیوه ها
Roman Beekeeper
مرحله

امنیت در جاوا: بهترین شیوه ها

در گروه منتشر شد
در برنامه های سرور یکی از مهمترین شاخص ها امنیت است. این یکی از انواع نیازهای غیر عملکردی است . امنیت در جاوا: بهترین شیوه ها - 1امنیت مولفه های زیادی دارد. البته، برای پوشش کامل تمام آن اصول و اقدامات محافظتی شناخته شده، باید بیش از یک مقاله بنویسید، بنابراین بیایید روی مهمترین آنها تمرکز کنیم. فردی که در این مبحث به خوبی مسلط باشد، می‌تواند تمامی فرآیندها را راه‌اندازی کند و اطمینان حاصل کند که حفره‌های امنیتی جدیدی ایجاد نمی‌کنند، در هر تیمی مورد نیاز خواهد بود. البته، نباید فکر کنید که اگر این روش ها را دنبال کنید، برنامه کاملاً ایمن خواهد بود. نه! اما قطعا با آنها امن تر خواهد بود. برو

1. تامین امنیت در سطح زبان جاوا

اول از همه، امنیت در جاوا درست از سطح ویژگی زبان شروع می شود. اگر اصلاح کننده های دسترسی وجود نداشت، این کاری است که ما انجام می دادیم؟... هرج و مرج، نه کمتر. یک زبان برنامه نویسی به ما کمک می کند کد ایمن بنویسیم و همچنین از بسیاری از ویژگی های امنیتی ضمنی استفاده کنیم:
  1. تایپ قوی جاوا یک زبان تایپ ایستا است که توانایی تشخیص خطاهای نوع را در زمان اجرا فراهم می کند.
  2. اصلاح کننده های دسترسی به لطف آنها، می توانیم دسترسی به کلاس ها، متدها و فیلدهای کلاس را به روشی که نیاز داریم پیکربندی کنیم.
  3. مدیریت خودکار حافظه برای این منظور، ما (جاوائیست ها ؛)) Garbage Collector را داریم که شما را از پیکربندی دستی رها می کند. بله، گاهی اوقات مشکلاتی پیش می آید.
  4. بررسی بایت کد: جاوا به بایت کد کامپایل می شود که قبل از اجرای آن توسط زمان اجرا بررسی می شود .
در میان چیزهای دیگر، توصیه هایی از Oracle در مورد امنیت وجود دارد . البته، به «سبک بالا» نوشته نشده است و ممکن است در حین خواندن آن چندین بار به خواب بروید، اما ارزشش را دارد. یک سند مهم، دستورالعمل‌های کدگذاری امن برای جاوا SE است که نکاتی در مورد نحوه نوشتن کد امن ارائه می‌دهد. این سند حاوی اطلاعات مفید زیادی است. در صورت امکان حتما ارزش خواندن را دارد. برای برانگیختن علاقه به این مواد، در اینجا چند نکته جالب وجود دارد:
  1. از سریال سازی کلاس های حساس به امنیت خودداری کنید. در این حالت، می توانید رابط کلاس را از فایل سریالی دریافت کنید، البته به داده هایی که در حال سریال سازی هستند اشاره نمی کنیم.
  2. سعی کنید از کلاس های داده قابل تغییر اجتناب کنید. این همه مزایای کلاس های تغییرناپذیر (به عنوان مثال ایمنی نخ) را به همراه دارد. اگر یک شیء قابل تغییر وجود داشته باشد، ممکن است منجر به رفتار غیرمنتظره شود.
  3. از اشیاء قابل تغییر برگشتی کپی تهیه کنید. اگر یک متد یک مرجع به یک شیء داخلی قابل تغییر برگرداند، کد مشتری می تواند وضعیت داخلی شی را تغییر دهد.
  4. و غیره…
به طور کلی، Secure Coding Guidelines for Java SE حاوی مجموعه ای از نکات و ترفندها در مورد نحوه صحیح و ایمن نوشتن کد در جاوا است.

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 امنیت در جاوا: بهترین شیوه ها - 2

3. وابستگی ها را اسکن کرده و به روز نگه دارید

چه مفهومی داره؟ برای آن‌هایی که نمی‌دانند وابستگی چیست، توضیح می‌دهم: این یک آرشیو jar با کد است که با استفاده از سیستم‌های ساخت خودکار (Maven، Gradle، Ant) به پروژه متصل می‌شود تا از راه‌حل شخص دیگری استفاده مجدد کند. به عنوان مثال، Project Lombok که در زمان اجرا برای ما دریافت کننده، ستتر و غیره تولید می کند. و اگر در مورد برنامه های کاربردی بزرگ صحبت کنیم، آنها از وابستگی های مختلفی استفاده می کنند. برخی گذرا هستند (یعنی هر وابستگی می تواند وابستگی های خاص خود را داشته باشد و غیره). بنابراین، مهاجمان به طور فزاینده ای به وابستگی های منبع باز توجه می کنند، زیرا آنها به طور منظم استفاده می شوند و می توانند برای بسیاری از مشتریان مشکل ایجاد کنند. مهم است که مطمئن شوید هیچ آسیب‌پذیری شناخته‌شده‌ای در کل درخت وابستگی (که دقیقاً شبیه آن است) وجود ندارد. و چندین راه برای این کار وجود دارد.

از Snyk برای نظارت استفاده کنید

ابزار Snyk تمام وابستگی های پروژه را بررسی می کند و آسیب پذیری های شناخته شده را علامت گذاری می کند. در آنجا می توانید برای مثال پروژه های خود را از طریق GitHub ثبت و وارد کنید. امنیت در جاوا: بهترین شیوه ها - 3همچنین، همانطور که در تصویر بالا می بینید، اگر نسخه جدیدتر راه حلی برای این آسیب پذیری داشته باشد، Snyk این کار را انجام داده و یک Pull-Request ایجاد می کند. می توان از آن به صورت رایگان برای پروژه های منبع باز استفاده کرد. پروژه ها در برخی از فرکانس ها اسکن می شوند: یک بار در هفته، یک بار در ماه. من ثبت نام کردم و تمام مخازن عمومی خود را به اسکن Snyk اضافه کردم (هیچ چیز خطرناکی در این مورد وجود ندارد: آنها از قبل برای همه باز هستند). سپس، Snyk نتیجه اسکن را نشان داد: امنیت در جاوا: بهترین شیوه ها - 4و پس از مدتی، Snyk-bot چندین درخواست Pull-Request را در پروژه‌هایی که وابستگی‌ها باید به‌روزرسانی شوند، آماده کرد: امنیت در جاوا: بهترین شیوه ها - 5و در اینجا یکی دیگر: امنیت در جاوا: بهترین شیوه ها - 6بنابراین این یک ابزار عالی برای جستجوی آسیب‌پذیری‌ها و نظارت بر به‌روزرسانی است. نسخه های جدید

از GitHub Security Lab استفاده کنید

کسانی که در GitHub کار می کنند نیز می توانند از ابزارهای داخلی خود استفاده کنند. شما می‌توانید در مورد این رویکرد در ترجمه من از وبلاگ آنها اطلاعیه آزمایشگاه امنیتی GitHub بیشتر بخوانید . این ابزار، البته، ساده تر از Snyk است، اما قطعاً نباید از آن غافل شوید. به علاوه، تعداد آسیب‌پذیری‌های شناخته شده تنها افزایش می‌یابد، بنابراین هم Snyk و هم GitHub Security Lab گسترش و بهبود خواهند یافت.

Sonatype DepShield را فعال کنید

اگر از GitHub برای ذخیره مخازن خود استفاده می کنید، می توانید یکی از برنامه های کاربردی را از MarketPlace - Sonatype DepShield به پروژه های خود اضافه کنید. با کمک آن می توانید پروژه ها را برای وابستگی ها نیز اسکن کنید. علاوه بر این، اگر چیزی پیدا کند، یک مشکل GitHub با توضیحات مربوطه ایجاد می شود، همانطور که در زیر نشان داده شده است: امنیت در جاوا: بهترین شیوه ها - 7

4. داده های حساس را با دقت مدیریت کنید

امنیت در جاوا: بهترین شیوه ها - 8در گفتار انگلیسی عبارت «داده‌های حساس» رایج‌تر است. افشای اطلاعات شخصی، شماره کارت اعتباری و سایر اطلاعات شخصی مشتری ممکن است صدمات جبران ناپذیری را به همراه داشته باشد. اول از همه، باید به طراحی اپلیکیشن نگاهی دقیق بیندازید و مشخص کنید که آیا واقعاً به داده ای نیاز است یا خیر. شاید نیازی به برخی از آنها نباشد اما برای آینده ای که نیامده و بعید است اضافه شده اند. علاوه بر این، در طول گزارش پروژه، چنین داده هایی ممکن است نشت کند. یک راه ساده برای جلوگیری از ورود داده‌های حساس به گزارش‌های شما، پاک کردن روش‌های toString()موجودیت‌های دامنه (مانند کاربر، دانش‌آموز، معلم و غیره) است. این کار از چاپ تصادفی فیلدهای حساس جلوگیری می کند. اگر از Lombok برای تولید یک متد استفاده می کنید toString()، می توانید از یک حاشیه نویسی @ToString.Excludeبرای جلوگیری از استفاده از فیلد در خروجی از طریق متد استفاده کنید toString(). همچنین هنگام به اشتراک گذاری داده ها با دنیای خارج بسیار مراقب باشید. به عنوان مثال، یک نقطه پایانی http وجود دارد که نام همه کاربران را نشان می دهد. نیازی به نشان دادن شناسه منحصر به فرد داخلی کاربر نیست. چرا؟ زیرا با استفاده از آن، مهاجم می‌تواند اطلاعات محرمانه‌تری درباره هر کاربر به دست آورد. برای مثال، اگر از جکسون برای سریال‌سازی و سریال‌زدایی POJO‌ها در JSON استفاده می‌کنید ، می‌توانید از @JsonIgnoreو حاشیه‌نویسی @JsonIgnorePropertiesبرای جلوگیری از سریال‌سازی و غیر سریالی شدن فیلدهای خاص استفاده کنید. به طور کلی برای مکان های مختلف باید از کلاس های مختلف POJO استفاده کنید. چه مفهومی داره؟
  1. برای کار با پایگاه داده، فقط از POJO - Entity استفاده کنید.
  2. برای کار با منطق تجاری، Entity را به Model منتقل کنید.
  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 برای این منظور مناسب هستند. هر دو توابع یک طرفه (هش رمزنگاری) با الگوریتم های محاسباتی پیچیده هستند که زمان زیادی را صرف می کنند. این دقیقاً همان چیزی است که شما به آن نیاز دارید، زیرا رمزگشایی مستقیم آن برای همیشه طول خواهد کشید. به عنوان مثال، Spring Security طیف وسیعی از الگوریتم ها را پشتیبانی می کند. SCryptPasswordEncoderهمچنین می توانید استفاده کنید BCryptPasswordEncoder. آنچه اکنون یک الگوریتم رمزگذاری قوی است ممکن است سال آینده ضعیف باشد. در نتیجه نتیجه می گیریم که بررسی الگوریتم های مورد استفاده و به روز رسانی کتابخانه ها با الگوریتم ضروری است.

به جای خروجی

امروز در مورد ایمنی صحبت کردیم و البته خیلی چیزها در پشت صحنه رها شد. من فقط در را به روی دنیای جدیدی برای شما باز کردم: جهانی که زندگی خودش را دارد. در مورد امنیت نیز مانند سیاست است: اگر شما درگیر سیاست نباشید، سیاست درگیر شما خواهد شد. به طور سنتی، پیشنهاد می کنم در حساب Github من مشترک شوید . در آنجا کارهایم را در مورد فناوری های مختلفی که مطالعه می کنم و در محل کار به کار می برم پست می کنم.

لینک های مفید

بله تقریبا تمام مقالات سایت به زبان انگلیسی نوشته شده اند. چه بخواهیم چه نخواهیم، ​​انگلیسی زبانی است که برنامه نویسان می توانند با هم ارتباط برقرار کنند. تمامی جدیدترین مقالات، کتاب ها و مجلات در زمینه برنامه نویسی به زبان انگلیسی نوشته شده اند. به همین دلیل است که پیوندهای من به توصیه‌ها بیشتر به زبان انگلیسی هستند:
  1. Habr: تزریق SQL برای مبتدیان
  2. Oracle: Java Security Resource Center
  3. Oracle: Secure Coding Guidelines for Java SE
  4. Baeldung: مبانی امنیت جاوا
  5. متوسط: 10 نکته برای تقویت امنیت جاوا
  6. Snyk: 10 بهترین شیوه امنیت جاوا
  7. JR: اعلام آزمایشگاه امنیتی GitHub: از همه کدهای شما با هم محافظت می کند
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION