CodeGym /وبلاگ جاوا /Random-FA /امنیت در جاوا: بهترین شیوه ها
John Squirrels
مرحله
San Francisco

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

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

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

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

2. آسیب پذیری های تزریق SQL را از بین ببرید

این نوع خاصی از آسیب پذیری است. این خاص است زیرا هم یکی از معروف ترین و هم یکی از رایج ترین آسیب پذیری ها است. اگر هرگز به امنیت کامپیوتر علاقه نداشته اید، در مورد آن چیزی نمی دانید. تزریق SQL چیست؟ این یک حمله پایگاه داده است که شامل تزریق کد SQL اضافی در جایی است که انتظار نمی رود. فرض کنید متدی داریم که نوعی پارامتر را برای پرس و جو از پایگاه داده می پذیرد. به عنوان مثال، یک نام کاربری. کد آسیب پذیر چیزی شبیه به این خواهد بود:
// This method retrieves from the database all users with a certain name
public List findByFirstName(String firstName) throws SQLException {
   // Connect to the database
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Compose a SQL database query with our firstName
   String query = "SELECT * FROM USERS WHERE firstName = " + firstName;

   // Execute the query
   Statement statement = connection.createStatement();
   ResultSet result = statement.executeQuery(query);

   // Use mapToUsers to convert the ResultSet into a collection of users.
   return mapToUsers(result);
}

private List mapToUsers(ResultSet resultSet) {
   // Converts to a collection of users
}
در این مثال، یک پرس و جو SQL از قبل در یک خط جداگانه آماده شده است. پس مشکل چیست، درست است؟ شاید مشکل این باشد که بهتر است از String.format استفاده کنیم ؟ نه؟ خب پس چی؟ بیایید خودمان را به جای یک آزمایش‌کننده قرار دهیم و به این فکر کنیم که چه چیزی می‌تواند به‌عنوان مقدار firstName در نظر گرفته شود . مثلا:
  1. ما می‌توانیم آنچه را که انتظار می‌رود منتقل کنیم - یک نام کاربری. سپس پایگاه داده همه کاربران را با آن نام برمی گرداند.
  2. می توانیم یک رشته خالی را پاس کنیم. سپس همه کاربران برگردانده خواهند شد.
  3. اما می توانیم موارد زیر را نیز پاس کنیم: "'; DROP TABLE USERS;". و در اینجا ما اکنون مشکلات huuuuuuge داریم. این کوئری یک جدول را از پایگاه داده حذف می کند. همراه با تمام داده ها. همه اش.
آیا می توانید تصور کنید که این چه مشکلاتی ایجاد می کند؟ فراتر از آن، شما می توانید هر چه می خواهید بنویسید. شما می توانید نام همه کاربران را تغییر دهید. می توانید آدرس آنها را حذف کنید. دامنه خرابکاری بسیار زیاد است. برای جلوگیری از این امر، باید از تزریق یک پرس و جو آماده جلوگیری کنید و در عوض با استفاده از پارامترها، پرس و جو را تشکیل دهید. این باید تنها راه ایجاد پرس و جوهای پایگاه داده باشد. اینگونه می توانید این آسیب پذیری را از بین ببرید. مثلا:
// This method retrieves from the database all users with a certain name
public List findByFirstName(String firstName) throws SQLException {
   // Connect to the database
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Create a parameterized query.
   String query = "SELECT * FROM USERS WHERE firstName = ?";

   // Create a prepared statement with the parameterized query
   PreparedStatement statement = connection.prepareStatement(query);

   // Pass the parameter's value
   statement.setString(1, firstName);

   // Execute the query
   ResultSet result = statement.executeQuery(query);

   // Use mapToUsers to convert the ResultSet into a collection of users.
   return mapToUsers(result);
}

private List mapToUsers(ResultSet resultSet) {
   // Converts to a collection of users
}
به این ترتیب از آسیب پذیری جلوگیری می شود. برای کسانی که می خواهند عمیق تر در این مقاله غوطه ور شوند، در اینجا یک مثال عالی آورده شده است . چگونه متوجه می شوید که این آسیب پذیری را درک کرده اید؟ اگر شوخی را در کمیک زیر دریافت کردید، احتمالاً درک واضحی از این آسیب پذیری دارید:Dامنیت در جاوا: بهترین شیوه ها - 2

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

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

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

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

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

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

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

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

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

ممکن است از عبارت "داده های حساس" استفاده کنیم. افشای اطلاعات شخصی، شماره کارت اعتباری و سایر اطلاعات حساس مشتری می تواند صدمات جبران ناپذیری را به همراه داشته باشد. اول از همه، به طراحی اپلیکیشن خود نگاه دقیقی بیندازید و تعیین کنید که آیا واقعاً به این یا آن داده نیاز دارید یا خیر. شاید شما واقعاً به برخی از داده‌هایی که دارید نیاز نداشته باشید - داده‌هایی که برای آینده‌ای که نیامده و بعید است که بیاید اضافه شده است. علاوه بر این، بسیاری از شما به طور ناخواسته چنین داده هایی را از طریق ورود به سیستم درز می کنید. یک راه آسان برای جلوگیری از ورود داده‌های حساس به گزارش‌های شما، پاک کردن متدهای toString() موجودیت‌های دامنه (مانند User، Student، Teacher و غیره) است. این از خروج تصادفی فیلدهای محرمانه جلوگیری می کند. اگر از Lombok برای تولید متد toString() استفاده می کنید ، می توانید از حاشیه نویسی @ToString.Exclude برای جلوگیری از استفاده از یک فیلد در خروجی متد toString() استفاده کنید . همچنین هنگام ارسال داده ها به دنیای خارج بسیار مراقب باشید. فرض کنید یک نقطه پایانی HTTP داریم که نام همه کاربران را نشان می دهد. نیازی به نشان دادن شناسه داخلی منحصر به فرد کاربر نیست. چرا؟ زیرا یک مهاجم می تواند از آن برای به دست آوردن اطلاعات حساس تر در مورد کاربر استفاده کند. به عنوان مثال، اگر از جکسون برای سریال‌سازی/از سریال‌سازی یک POJO به/از JSON استفاده می‌کنید ، می‌توانید از حاشیه‌نویسی‌های @JsonIgnore و @JsonIgnoreProperties برای جلوگیری از سریال‌سازی/از سریال‌سازی فیلدهای خاص استفاده کنید. به طور کلی، شما باید از کلاس های مختلف POJO در مکان های مختلف استفاده کنید. معنی آن چیست؟
  1. هنگام کار با پایگاه داده، از یک نوع POJO (یک موجودیت) استفاده کنید.
  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 = "Elvis lives!";
   String aad = "Buddy Holly";

   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. Guru99: آموزش تزریق SQL
  2. Oracle: Java Security Resource Center
  3. Oracle: Secure Coding Guidelines for Java SE
  4. Baeldung: مبانی امنیت جاوا
  5. متوسط: 10 نکته برای تقویت امنیت جاوا
  6. Snyk: 10 بهترین روش امنیتی جاوا
  7. GitHub: اعلام آزمایشگاه امنیتی GitHub: امنیت کدهای جهان، با هم
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION