1. Təhlükəsiz sereallaşdırma üçün əsas ən yaxşı təcrübələr
Sereallaşdırma — hava limanında baqajı qablaşdırmağa bənzəyir: içəridə nə olduğunu və çantanı kimə etibar etdiyinizi bilmirsinizsə, təhlükəsizlik nəzarətində xoşagəlməz sürprizlə qarşılaşa bilərsiniz. Java-da sereallaşdırma obyektləri asanlıqla saxlamağa və bərpa etməyə imkan verir, lakin məlumatlar etibarsız mənbələrdən gəlirsə, hücumlar üçün geniş bir qapı da açır.
Klassik təhlükə:
Java-da sereallaşdırma bəzən təhlükəsiz olmaya bilər. Əgər xaker zərərli axın təlqin edərsə, desereallaşdırma zamanı ən xoşagəlməz nəticələr mümkündür: sahələrin dəyişdirilməsindən tutmuş arzuolunmaz kodun icrasına qədər. Bu, dərslik qorxutması deyil — Java tarixində məhz bu mexanizm üzərində qurulan hücum hallarına həqiqətən rast gəlinib.
Niyə belə olur?
Məsələ ondadır ki, desereallaşdırma sadəcə sahə dəyərlərinin bərpası deyil. Proses zamanı tamhüquqlu obyekt yaradılır: xüsusi metodlar çağırıla bilər (məsələn, readObject, readResolve), bəzən isə refleksiya vasitəsilə koddakı həssas yerlərə toxunulur. Xüsusilə üçüncü tərəf kitabxanalarından olan siniflər təhlükəlidir: bəziləri desereallaşdırma mərhələsində artıq hərəkətlər edirlər. Buna görə də heç vaxt xaricdən alınan sereallaşdırılmış məlumatlara etibar etməyin.
Həssas məlumatlar üçün transient istifadə edin
Sinfinizdə parollar, tokenlər, şəxsi açarlar və ya digər həssas məlumatları saxlayan sahələr varsa, onları transient kimi elan edin. Bu məlumatlar sereallaşdırılmış axına düşməyəcək.
import java.io.Serializable;
public class User implements Serializable {
private String username;
private transient String password; // sereallaşdırılmır
// ...konstruktorlar, getter-lər, setter-lər...
}
Desereallaşdırma zamanı nə baş verəcək? password sahəsi defolt dəyərə malik olacaq (null sətirlər üçün). Bu yaxşıdır: parollar fayllarda saxlanılmayacaq və ya şəbəkə ilə göndərilməyəcək.
serialVersionUID-i açıq şəkildə təyin edin
Həmişə serialVersionUID-i açıq şəkildə göstərin. Bu, uyğunluq xətalarının ehtimalını azaldır və desereallaşdırma zamanı siniflərin podməni riskini minimuma endirir.
private static final long serialVersionUID = 1L;
Bu təhlükəsizlik üçün niyə önəmlidir? Əgər serialVersionUID göstərilməsə, kompilyator onu sinfin strukturuna əsasən avtomatik yaradacaq. Bu, gözlənilməz uyğunsuzluqlara və nəzəri olaraq, eyni ada malik, lakin fərqli strukturlu siniflərin podməni ilə sui-istifadəyə gətirə bilər.
Desereallaşdırmada obyektlərin tiplərini yoxlayın
Şəbəkədən və ya fayldan gələnlərə kor-koranə inanmayın. Desereallaşdırmadan sonra obyektlə işləmədən öncə onun gözlənilən tipdə olduğuna əmin olun.
Object obj = objectInputStream.readObject();
if (obj instanceof User) {
User user = (User) obj;
// user ilə təhlükəsiz işləyirik
} else {
// gözlənilməyən tip — istisna atın və ya səhvi emal edin
}
Buna niyə ehtiyac var? Zərərli axın biznes məntiqinizə uyğun gəlməyən, lakin Serializable reallaşdıran başqa sinfin obyektini ehtiva edə bilər.
Desereallaşdırıla bilən sinifləri məhdudlaşdırın (ObjectInputFilter)
Java 9-dan başlayaraq ObjectInputFilter filtrlərindən istifadə edin ki, desereallaşdırılmasına icazə verilən siniflərin dəstini məhdudlaşdırasınız. Bu, girişdə feys-kontrol kimidir.
Nümunə: filtrin quraşdırılması
import java.io.*;
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"com.example.User;com.example.Address;!*"
);
ObjectInputStream in = new ObjectInputStream(inputStream);
in.setObjectInputFilter(filter);
Object obj = in.readObject(); // indi yalnız User və Address desereallaşdırılacaq
Bu filtr tətbiqinizin yalnız User və Address siniflərinə icazə verir. Qalanlarının hamısı bloklanacaq — istisna atılacaq. Bu, zərərli obyektin keçmə riskini əhəmiyyətli dərəcədə azaldır.
Etibarsız mənbələrdən məlumatları desereallaşdırmayın
Qızıl qayda: mənbəyə əmin deyilsinizsə — desereallaşdırmayın. Parslama zamanı kod icra etməyən formatlara üstünlük verin (məsələn, JSON, təhlükəsiz parsləyicilərlə XML).
Pis praktika nümunəsi:
// Internetdən gələn məlumatlarla bunu heç vaxt etməyin!
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Object obj = in.readObject(); // təhlükəlidir!
Daha yaxşı nə etmək olar?
- JSON parsləyicilərindən (məsələn, Gson/Jackson) və ya validasiyalı XML parsləyicilərindən istifadə edin.
- Əgər binar sereallaşdırma qaçılmazdırsa — sinifləri ObjectInputFilter ilə filtr edin və tipləri (instanceof) yoxlayın.
Xarici sistemlərlə mübadilə üçün alternativ formatlardan istifadə edin
İnteqrasiyalar üçün parslama zamanı kod icra etməyən formatlardan istifadə edin: JSON, XML, Protocol Buffers və s. Bu, desereallaşdırma üzərindən hücumları demək olar ki, istisna edir.
// ObjectInputStream əvəzinə JSON parsləyicisindən istifadə edin
User user = gson.fromJson(jsonString, User.class);
Sereallaşdırılmış obyektləri ictimai yerlərdə saxlamayın
Sereallaşdırılmış obyektləri olan fayllar həssas məlumatlar ehtiva edə bilər. Onları ictimai qovluqlarda saxlamayın və fayl sistemi səviyyəsində giriş hüquqlarını məhdudlaşdırın.
Bütövlüyə nəzarət üçün sereallaşdırmaya güvənməyin
Sereallaşdırma məlumatların bütövlüyünü və ya həqiqiliyini təmin etmir. Dəyişikliklər yolverilməzdirsə, rəqəmsal imzalardan, nəzarət cəmlərindən və ya şifrələmədən istifadə edin.
2. Praktika: ObjectInputFilter nümunəsi və zəifliyin nümayişi
Siniflərin filtrasiyası nümunəsi
Tutaq ki, bizdə User sinfi var:
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private transient String password;
// ...konstruktorlar, getter-lər, setter-lər...
}
Filtr yalnız User-ə icazə verir:
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"com.example.User;!*"
);
in.setObjectInputFilter(filter);
İndi kimsə başqa sinfin obyektini sırımağa çalışsa, desereallaşdırma səhvlə nəticələnəcək.
Potensial zəifliyin nümayişi
Zərərli sinif:
// Təsəvvür edin ki, kimsə belə bir sinif sırıdı
public class Evil implements java.io.Serializable {
static {
System.out.println("Zərərli kod icra olundu!");
// burada istənilən şey ola bilər...
}
}
Siniflər filtr edilməsə, desereallaşdırma zamanı Evil obyektinin yaradılması mümkündür və sinif yüklənərkən statik inisializator işləyəcək — bu artıq real hücumdur.
4. Sereallaşdırmanın təhlükəsizliyini təmin edərkən tipik səhvlər
Səhv №1: Filtrasiya və tip yoxlaması olmadan desereallaşdırma. Tez-tez tərtibatçılar obyektləri axından oxuyur və dərhal lazım olan tipə kast edirlər. Bu, hücumlar üçün qapı açır. ObjectInputFilter-dən istifadə edin və tipi instanceof ilə yoxlayın.
Səhv №2: Həssas məlumatların transient olmadan saxlanması. Parolları/açarları transient kimi elan etməyi unutsanız, onlar axına düşəcək və faylla birlikdə sızdırıla bilər.
Səhv №3: serialVersionUID-in olmaması. Açıq serialVersionUID olmadan gözlənilməz uyğunluq xətaları və siniflərin podməni ilə bağlı risklər yarana bilər.
Səhv №4: Xarici sistemlərlə mübadilə üçün sereallaşdırmadan istifadə. Binar sereallaşdırma tətbiq daxilində (məsələn, keş) rahatdır, amma xarici mübadilə üçün təhlükəlidir. Təhlükəsiz parsləyicilərlə JSON/XML/Proto-ya üstünlük verin.
Səhv №5: Məlumat bütövlüyünün nəzərə alınmaması. Sereallaşdırılmış faylın baytlarının dəyişdirilməsi gözə dəyməyə bilər. Rəqəmsal imzalardan, nəzarət cəmlərindən və ya şifrələmədən istifadə edin.
GO TO FULL VERSION