Spring Security dan PasswordEncoder interfeysi parolni bir tomonlama o'zgartirish uchun ishlatiladi, shunda uni xavfsiz saqlash mumkin bo'ladi. PasswordEncoder bir tomonlama o'zgartirishni taklif qiladi, shuning uchun u parolni ikki tomonlama o'zgartirish kerak bo'lganda (masalan, ma'lumotlar bazasida autentifikatsiya uchun ishlatiladigan hisob ma'lumotlarini saqlashda) mo'ljallangan emas. Odatda, PasswordEncoder parolni saqlash uchun ishlatiladi, u foydalanuvchining autentifikatsiya paytida kiritgan paroli bilan solishtiriladi.
Saqlangan Parollar Tarixi
Yillar mobaynida parollarni saqlash standart mexanizmi rivojlandi. Dastlab parollar oddiy matn sifatida saqlanardi. Parollar xavfsiz hisoblanardi, chunki parollar saqlanadigan ma'lumotlar bazasiga kirish uchun hisob ma'lumotlari talab qilinadi deb o'ylashardi. Ammo xakerlar SQL-kodning kiritilishi (SQL Injection) orqali foydalanuvchilar nomlari va parolining katta "dump"larini olish yo‘llarini topishdi. Foydalanuvchi hisob ma'lumotlari ommaga tarqalayotganini ko'rib turib, xavfsizlik mutaxassislari foydalanuvchi parollarini himoyasini kuchaytirishni talab qilayotganini tushunishdi.
Dasturchilarga parollarni bir tomonlama hash algoritim, masalan SHA-256 orqali saqlash tavsiya qilindi. Foydalanuvchi autentifikatsiyadan o'tishni sinab ko'rganida, hashlangan parol foydalanuvchining kiritgan parolining hashiga solishtirilardi. Bu demak edi ki, tizim faqat parolning bir tomonlama hashini saqlashi kerak edi. Xakerlik holatida, faqat parol hashlarining bir tomonlama variantlari ochiq qolinardi. Hashlar bir tomonlama bo'lgani uchun va hash orqali parolni bir ko'rsatkichdan tan olish qiyin bo'lgani uchun tizimdagi har bir parolni tanlashga harakat qilishga mantiq yo'q edi. Ushbu yangi tizimni buzish uchun xakerlar qidirish jadvallarini yaratishga qaror qilishdi, ular rainbow table deb nomlanadi. Har bir parolni tahmin qilish o'rniga, ular parolni bir marta hisoblaydilar va uni qidiruv jadvalida saqlashadi.
Rainbow tablelarning samaradorligini kamaytirish uchun dasturchilarga "sertlangan" parollar ishlatish tavsiya qilindi. Parolni hash funktsiyasiga kirish ma'lumotlari sifatida ishlatilishiga qo'shimcha ravishda, har bir foydalanuvchining paroli uchun tasodifiy baytlar (sert deb tanilgan) generatsiya qilindi. Sert va foydalanuvchi paroli hash funktsiyasidan o'tkazilib, natijada noyob hash hosil bo'ladi. Sert foydalanuvchi parolining yonida ochiq matn sifatida saqlanadi. Keyin, agar foydalanuvchi autentifikatsiyaga harakat qilsa, hashlangan parol taqqoslanadi va foydalanuvchiga kiritilgan sertni va parolni hash bilan taqqoslanadi. Sertning noyobligi shuni anglatadiki, rainbow tablelar samaradorligini yo'qotadi, chunki hash har bir sert va parol kombinatsiyasi uchun har xil bo'ladi.
Hozirgi kunda biz kriptografik hashlar, masalan SHA-256 algoritmi, endi xavfsiz emasligini tushunamiz. Sababi shundaki, zamonaviy uskunalar yordamida soniyada milliardlab hash hisob-kitoblarini amalga oshirish mumkin. Bu har bir parolni alohida-alohida buzishni osonlashtiradi.
Dasturchilarga endi parollarni saqlash uchun adaptiv hisoblash-loyiha bo'lmagan funktsiyalarni ishlatish tavsiya qilinmoqda. Parollarni adaptiv hisoblash-loyiha bo'lmagan funktsiyalar yordamida validatsiya qilish resurslar (masalan, protsessor, xotira va hokazo) talab qiladi. Adaptiv hisoblash-loyiha bo'lmagan funktsiya vaqt o'tishi bilan apparat takomillashadigan bo'lgani uchun "mehnattalablik va xarajatlar omili"ni sozlash imkonini beradi. Parolingizni tekshirish taxminan bir soniya davom etishi uchun "mehnattalablik va xarajatlar omili"ni sozlash tavsiya etiladi. Bu muvozanat shuni anglatadiki, xakerlar parolni buzish qiyinroq bo'lgani holda, tizimingizga resurs talab qilish jihatidan katta bosim qilmaslik tavsiya etiladi. Spring Securityda "mehnattalablik va xarajatlar omili"ni adolatli boshlang'ich nuqtasini olishga urinish mavjud, ammo foydalanuvchilarga o'z tizimlariga moslashtirish tavsiya etiladi, chunki tezlik tizimdan tizimga keskin o'zgaradi. Adaptiv hisoblash-loyiha bo'lmagan funktsiyalarning namunalariga bcrypt, PBKDF2, scrypt va argon2 kiradi.
Adaptiv hisoblash-loyiha bo'lmagan funktsiyalar o'zlari katta hisoblash resurslari talab qiladiganligi sababli, har bir so'rov uchun foydalanuvchi nomi va parolni validatsiya qilish ilovaning samaradorligini sezilarli darajada pasaytiradi. Spring Security (yoki boshqa kutubxona) parolni tezlatishni hech qanday tarzda ta'sir qila olmaydi, chunki tegishli xavfsizlik darajasi erishish uchun validatsiya hisoblash resurslarini talab qiladi. Foydalanuvchilarga uzoq muddatli hisob ma'lumotlarini (masalan, foydalanuvchi nomi va parol) qisqa muddatli (masalan, sessiya, OAuth tokeni va hokazo) almashish tavsiya etiladi. Qisqa muddatli hisob ma'lumotlarini tezda xavfsizlikdan mahrum qilmaslik sharti bilan validatsiya qilish mumkin.
DelegatingPasswordEncoder
Spring Security 5.0 versiyasiga qadar NoOpPasswordEncoder dan foydalanilgan, bu esa oddiy matn shaklidagi parollarni talab qildi. "Parol tarixi" bo'limidan kelib chiqqan holda, BCryptPasswordEncoder kabi narsa default bo'lib ishlatiladi deb kutish mumkin edi. Ammo bu uchta muammoni inobatga olmaydi:
-
Eski parol kodlarini ishlatadigan ko'plab dasturning mavjudligi, ularni migratsiya qilish qiyin
-
Parollarni saqlashning eng yaxshi usuli yana o'zgaradi
-
Spring Security framework bo'lib, tez-tez kritikal o'zgarishlar kiritolmaydi
Buning o'rniga, Spring Security barcha ushbu muammolarni hal qilish uchun DelegatingPasswordEncoder ni taqdim etadi:
-
Parollarni saqlashning hozirgi tavsiyalari asosida kodlashni ta'minlash
-
Ruxsat etilgan va eski formatlarda parollarni validatsiya qilish imkoniyati
-
Kelajakda kodlash yangiliklarini ishlab chiqarish imkoniyati
PasswordEncoderFactories dan foydalanib, DelegatingPasswordEncoder ni osongina yaratishingiz mumkin.
PasswordEncoder passwordEncoder =
PasswordEncoderFactories.createDelegatingPasswordEncoder();
val passwordEncoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
Bundan tashqari, siz o'zingizning custom instansiyani yaratishingiz mumkin. Masalan:
String idForEncode = "bcrypt";
Map encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("sha256", new StandardPasswordEncoder());
PasswordEncoder passwordEncoder =
new DelegatingPasswordEncoder(idForEncode, encoders);
val idForEncode = "bcrypt"
val encoders: MutableMap<String, PasswordEncoder> = mutableMapOf()
encoders[idForEncode] = BCryptPasswordEncoder()
encoders["noop"] = NoOpPasswordEncoder.getInstance()
encoders["pbkdf2"] = Pbkdf2PasswordEncoder()
encoders["scrypt"] = SCryptPasswordEncoder()
encoders["sha256"] = StandardPasswordEncoder()
val passwordEncoder: PasswordEncoder = DelegatingPasswordEncoder(idForEncode, encoders)
Parollarni saqlash formati
Parollarning umumiy formati quyidagicha:
{id}encodedPassword
Masalan, id - foydalanishga yaroqli PasswordEncoderni topish uchun ishlatiladigan identifikatsiya, encodedPassword esa tanlangan PasswordEncoder uchun o'ziga xos kodlangan parol. id parolning boshida joylashgan bo'lishi kerak, { bilan boshlanishi va } bilan tugashi kerak. Agar id topilmasa, id null qiymatiga ega bo'ladi. Masalan, quyida har xil idlardan foydalangan holda kodlangan parollar ro'yxati misoli keltirilgan. Barcha asl parollar "password".
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{noop}password
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
- Birinchi parol uchun
PasswordEncoderning identifikatoribcryptbo'ladi va kodlangan parol$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BGsifatida ko'rsatiladi. Mos kelganidan so'ng,BCryptPasswordEncoderga o'tkaziladi - Ikkinchi parol uchun
PasswordEncoderning identifikatorinoopbo'ladi, kodlangan parol esapasswordshaklida bo'ladi. Mos kelganidan so'ng,NoOpPasswordEncoderga o'tkaziladi - Uchinchi parol uchun
PasswordEncoderning identifikatoripbkdf2bo'ladi va kodlangan parol5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dcshaklida bo'ladi. Mos kelganidan so'ng,Pbkdf2PasswordEncoderga o'tkaziladi - To'rtinchi parol uchun
PasswordEncoderning identifikatoriscryptbo'ladi va kodlangan parol$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=shaklida bo'ladi. Mos kelganidan so'ng,SCryptPasswordEncoderga o'tkaziladi - Yakuniy parol uchun
PasswordEncoderning identifikatorisha256bo'ladi va kodlangan parol97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0shaklida bo'ladi. Mos kelganidan so'ng,StandardPasswordEncoderga o'tkaziladi
Ba'zi foydalanuvchilar saqlash formatining potentsial xaker uchun ochiq ekanidan xavotirlanishi mumkin. Bu haqida xavotirlanmaslik lozim, chunki parolni saqlash algoritmning yopiq yoki ochiq ekanligiga bog'liq emas. Bundan tashqari, ko'pchilik formatlar xakerlar tomonidan prifixlarsiz ham osongina aniqlanishi mumkin. Masalan, BCrypt parollari ko'pincha $2a$ bilan boshlanadi.
Parolni kodlash
Konstruktoriga yuborilgan idForEncode parollarni kodlashda ishlatiladigan PasswordEncoderni belgilaydi. Yuqorida yaratgan DelegatingPasswordEncoderni hisobga olsak, bu password ni kodlash natijasi BCryptPasswordEncoder ga topshirilishini va {bcrypt} prifix bilan belgilanishini anglatadi. Yakuniy natija quyidagi ko'rinishda bo'ladi:
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
Parolni o'zgartirish
Parolni o'zgartirish {id} va konstruktorida kiritilgan PasswordEncoderga id ni o'zlashtirish asosida amalga oshiriladi. Oddiy holda, parol va ko'rsatilmagan id bilan matches(CharSequence, String) chaqiruv natijasi (null qiymatga ega bo'lgan identifikatorni o'z ichiga olgan holda) IllegalArgumentException komandasini keltirib chiqaradi. Ushbu ish amaliyotini DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(PasswordEncoder) yordamida sozlash mumkin.
id ni ishlatish orqali har qanday kodlashni o'zgartirish mumkin, ammo kodlash parollarni kodlashda eng zamonaviy kodlashdan foydalanishga ruxsat beradi. Bu juda muhim, chunki shifrlashga nisbatan, parol hashlarining tashkil etilmasliklari juda qiyin bo'ladi. Matnni oddiy qayta tiklash imkoniyati yo'qligi sababli, bu parollarni o'rnini almashtirishni qiyinlashtiradi. NoOpPasswordEncoderni osongina o'tkazish mumkin bo'lgan holda, foydalanuvchilar boshlang'ich punkt sifatida, ularni ishlatishni boshlash osonroq bo'ladi.
Ish boshlash
Agar siz demo-versiyangizni yoki namuna tayyorlayotgan bo'lsangiz, foydalanuvchi parollarini hash qilishga vaqt sarflash biroz noqulay bo'ladi. Ushbu jarayonni osonlashtiruvchi yordamchi mexanizmlar mavjud, ammo ular hali ham ishlab chiqarish o'rnatishlar uchun mo'ljallanmagan.
User user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("user")
.build();
System.out.println(user.getPassword());
// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
val user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("user")
.build()
println(user.password)
// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
Agar siz bir nechta foydalanuvchilar yaratishni xohlasangiz, konstruktorni qayta ishlatishingiz mumkin.
UserBuilder users = User.withDefaultPasswordEncoder();
User user = users
.username("user")
.password("password")
.roles("USER")
.build();
User admin = users
.username("admin")
.password("password")
.roles("USER","ADMIN")
.build();
val users = User.withDefaultPasswordEncoder()
val user = users
.username("user")
.password("password")
.roles("USER")
.build()
val admin = users
.username("admin")
.password("password")
.roles("USER", "ADMIN")
.build()
Bu saqlangan parolni hash qilish imkonini beradi, ammo parollar xotirada ochiq holda qoladi va kompilyatsiyalangan manba kodida ko'rinadi. Shuning uchun bu usul hali ham ishlab chiqarish muhitida xavfsiz deb hisoblanmaydi. Ishlab chiqarish o'rnatishda parollarni tashqi muhitda hash qilish kerak.
Spring Boot CLI yordamida Kodlash
Parolni to'g'ri kodlashning eng oson yo'li – foydalanish Spring Boot CLI.
Masalan, quyidagi buyruq password parolini DelegatingPasswordEncoder bilan ishlatish uchun kodlaydi:
spring encodepassword password
{bcrypt}$2a$10$X5wFBtLrL/kHcmrOGGTrGufsBX8CJ0WpQpF3pgeuxBB/H73BK1DW6
Nosozliklarni Bartaraf Etish
Keyingi xato saqlangan parollardan birida identifikator yo'q bo'lsa yuzaga keladi.
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null" at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:233) at org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:196)
Xatoni bartaraf etishning eng oddiy usuli – parollaringizni kodlaydigan PasswordEncoderni aniq ko'rsatishdir. Ushbu muammoni hal qilishning eng oson yo'li - hozirgi vaqtda parollaringiz qanday saqlanayotganini aniqlash va to'g'ri PasswordEncoderni ko'rsatishdir.
Agar siz Spring Security 4.2.x dan o'tayotgan bo'lsangiz, NoOpPasswordEncoder ni ochib, avvalgi ish mantiqiga qaytishingiz mumkin.
Bundan tashqari, barcha parollarni to'g'ri identifikator bilan ta'minlash va DelegatingPasswordEncoderni ishlatishda davom etishingiz mumkin. Masalan, agar siz BCryptdan foydalansangiz, parolingizni quyidagicha ko'chirishingiz mumkin:
$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
ga
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
To'liq xaritalash ro'yxati PasswordEncoderFactories Javadocda keltirilgan.
BCryptPasswordEncoder
BCryptPasswordEncoder realizatsiyasi parollarni hash qilish uchun keng qo'llab-quvvatlanadigan bcrypt algoritmni ishlatadi. Uni qiyinchilik bilan buzish uchun, bcrypt ataylab sekinlashtirilgan. Boshqa adaptiv hisoblash-loyiha bo'lmagan funktsiyalar singari, uning parolingizni tizimingizda tekshirish taxminan 1 soniya davom etishi uchun ehtiyotkorlik bilan sozlanishi kerak. BCryptPasswordEncoder default realizatsiyasi Javadocdagi BCryptPasswordEncoder da 10 ishonchlilik parametri bilan ishlaydi. Parolni tekshirish taxminan 1 soniy
GO TO FULL VERSION