Saya telah mencari algoritma Java sederhana untuk menghasilkan string alfa-numerik pseudo-acak. Dalam situasi saya ini akan digunakan sebagai pengidentifikasi sesi / kunci unik yang "cenderung" unik dari 500K+
generasi ke generasi (kebutuhan saya tidak benar-benar membutuhkan sesuatu yang jauh lebih canggih).
Idealnya, saya dapat menentukan panjang tergantung pada kebutuhan keunikan saya. Misalnya, string yang dihasilkan dengan panjang 12 mungkin terlihat seperti "AEYGF7K0DM1X"
.
java
string
random
alphanumeric
Todd
sumber
sumber
Long.toHexString(Double.doubleToLongBits(Math.random()));
UUID.randomUUID().toString();
RandomStringUtils.randomAlphanumeric(12);
Jawaban:
Algoritma
Untuk menghasilkan string acak, karakter gabungan digambar secara acak dari himpunan simbol yang dapat diterima sampai string mencapai panjang yang diinginkan.
Penerapan
Berikut adalah beberapa kode yang cukup sederhana dan sangat fleksibel untuk menghasilkan pengidentifikasi acak. Baca informasi berikut untuk catatan aplikasi penting.
Contoh penggunaan
Buat generator tidak aman untuk pengidentifikasi 8 karakter:
Buat generator aman untuk pengidentifikasi sesi:
Buat generator dengan kode yang mudah dibaca untuk dicetak. String lebih panjang dari string alfanumerik penuh untuk mengimbangi penggunaan simbol lebih sedikit:
Gunakan sebagai pengidentifikasi sesi
Membuat pengidentifikasi sesi yang cenderung unik tidak cukup baik, atau Anda bisa menggunakan penghitung sederhana. Penyerang membajak sesi ketika pengidentifikasi yang dapat diprediksi digunakan.
Ada ketegangan antara panjang dan keamanan. Pengidentifikasi yang lebih pendek lebih mudah ditebak, karena ada lebih sedikit kemungkinan. Tetapi pengidentifikasi yang lebih lama mengkonsumsi lebih banyak penyimpanan dan bandwidth. Serangkaian simbol yang lebih besar membantu, tetapi dapat menyebabkan masalah penyandian jika pengidentifikasi dimasukkan dalam URL atau dimasukkan kembali dengan tangan.
Sumber yang mendasari keacakan, atau entropi, untuk pengidentifikasi sesi harus berasal dari generator nomor acak yang dirancang untuk kriptografi. Namun, menginisialisasi generator ini terkadang mahal atau lambat secara komputasi, sehingga harus dilakukan upaya untuk menggunakannya kembali jika memungkinkan.
Gunakan sebagai pengidentifikasi objek
Tidak semua aplikasi membutuhkan keamanan. Penugasan acak dapat menjadi cara yang efisien bagi banyak entitas untuk menghasilkan pengidentifikasi di ruang bersama tanpa koordinasi atau partisi. Koordinasi bisa lambat, terutama di lingkungan yang berkerumun atau terdistribusi, dan pemisahan ruang menyebabkan masalah ketika entitas berakhir dengan saham yang terlalu kecil atau terlalu besar.
Pengidentifikasi yang dihasilkan tanpa mengambil tindakan untuk membuatnya tidak dapat diprediksi harus dilindungi dengan cara lain jika penyerang mungkin dapat melihat dan memanipulasinya, seperti yang terjadi pada sebagian besar aplikasi web. Harus ada sistem otorisasi terpisah yang melindungi objek yang pengenalnya dapat ditebak oleh penyerang tanpa izin akses.
Kehati-hatian juga harus diambil untuk menggunakan pengidentifikasi yang cukup lama untuk membuat tabrakan tidak mungkin mengingat jumlah total pengidentifikasi yang diantisipasi. Ini disebut sebagai "paradoks ulang tahun." Probabilitas tabrakan, p , kira-kira n 2 / (2q x ), di mana n adalah jumlah pengidentifikasi yang sebenarnya dihasilkan, q adalah jumlah simbol yang berbeda dalam alfabet, dan x adalah panjang pengidentifikasi. Ini harus menjadi jumlah yang sangat kecil, seperti 2-50 atau kurang.
Mengatasi hal ini menunjukkan bahwa peluang tabrakan di antara 500k 15-karakter pengidentifikasi adalah sekitar 2-52 , yang kemungkinan lebih kecil dari kesalahan yang tidak terdeteksi dari sinar kosmik, dll.
Perbandingan dengan UUID
Menurut spesifikasinya, UUID tidak dirancang untuk tidak dapat diprediksi, dan tidak boleh digunakan sebagai pengidentifikasi sesi.
UUID dalam format standar membutuhkan banyak ruang: 36 karakter hanya untuk 122 bit entropi. (Tidak semua bit UUID "acak" dipilih secara acak.) String alfanumerik yang dipilih secara acak mengemas lebih banyak entropi hanya dalam 21 karakter.
UUID tidak fleksibel; mereka memiliki struktur dan tata letak standar. Ini adalah kebajikan utama mereka serta kelemahan utama mereka. Ketika berkolaborasi dengan pihak luar, standardisasi yang ditawarkan oleh UUID mungkin membantu. Untuk penggunaan internal murni, mereka bisa tidak efisien.
sumber
.replaceAll("\\d", " ");
menempel ke ujungreturn new BigInteger(130, random).toString(32);
garis untuk melakukan pertukaran regex. Ini menggantikan semua digit dengan spasi. Bekerja sangat baik untuk saya: Saya menggunakan ini sebagai pengganti front-end Lorem Ipsumsymbols
dan menggunakan spasi sebagai gantinya; Anda dapat mengontrol panjang "kata" rata-rata dengan mengubah jumlah spasi dalam simbol (lebih banyak kejadian untuk kata yang lebih pendek). Untuk solusi teks palsu yang benar-benar berlebihan, Anda dapat menggunakan rantai Markov!SecureRandom
instance yang ditetapkan kerandom
variabel.Java memasok cara untuk melakukan ini secara langsung. Jika Anda tidak ingin tanda hubung, strip mudah dihapus. Gunakan saja
uuid.replace("-", "")
Keluaran:
sumber
UUID.randomUUID().toString().replaceAll("-", "");
membuat string alpha-numeric, seperti yang diminta.sumber
SecureRandom
alih-alihRandom
kelas. Jika kata sandi dibuat di server, ini mungkin rentan terhadap serangan waktu.AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
dan beberapa karakter yang diizinkan lainnya.static Random rnd = new Random();
ke dalam metode?Random
objek di setiap pemanggilan metode? Saya kira tidak.Jika Anda senang menggunakan kelas Apache, Anda bisa menggunakan
org.apache.commons.text.RandomStringGenerator
(commons-text).Contoh:
Sejak commons-lang 3.6,
RandomStringUtils
tidak digunakan lagi.sumber
Apache Commons Lang 3.3.1
perpustakaan - dan itu hanya menggunakanjava.util.Random
untuk memberikan urutan acak, sehingga menghasilkan urutan tidak aman .public static java.lang.String random(int count, int start, int end, boolean letters, boolean numbers, @Nullable char[] chars, java.util.Random random)
Anda dapat menggunakan perpustakaan Apache untuk ini: RandomStringUtils
sumber
compile 'commons-lang:commons-lang:2.6'
SecureRandom
dan Anda baik-baik saja.Dalam satu baris:
sumber
AEYGF7K0DM1X
yang tidak heksadesimal. Itu membuatku khawatir betapa sering orang mengira alfanumerik dengan heksadesimal. Mereka bukan hal yang sama.Math.random()
menghasilkandouble
antara 0 dan 1, sehingga bagian eksponen sebagian besar tidak digunakan. Gunakanrandom.nextLong
untuk acaklong
bukan hack jelek ini.Ini mudah dicapai tanpa perpustakaan eksternal.
1. Pembuatan Data Acak Kriptografis Pseudo
Pertama, Anda membutuhkan PRNG kriptografi. Java memiliki
SecureRandom
untuk itu dan biasanya menggunakan sumber entropi terbaik pada mesin (misalnya/dev/random
). Baca lebih lanjut di sini.Catatan:
SecureRandom
adalah cara paling lambat, tetapi paling aman di Jawa untuk menghasilkan byte acak. Namun saya sarankan TIDAK mempertimbangkan kinerja di sini karena biasanya tidak memiliki dampak nyata pada aplikasi Anda kecuali jika Anda harus menghasilkan jutaan token per detik.2. Ruang yang Diperlukan dari Nilai yang Mungkin
Selanjutnya Anda harus memutuskan "seberapa unik" token Anda. Keseluruhan dan satu-satunya titik pertimbangan entropi adalah untuk memastikan bahwa sistem dapat menahan serangan brute force: ruang nilai yang mungkin harus sangat besar sehingga penyerang hanya bisa mencoba proporsi nilai yang diabaikan dalam waktu non-menggelikan 1 . Pengidentifikasi unik seperti acak
UUID
memiliki entbit 122bit (mis. 2 ^ 122 = 5.3x10 ^ 36) - peluang tabrakan adalah "* (...) karena ada peluang duplikasi satu dalam miliar, versi 103 triliun 4 UUID harus dibuat 2 ". Kami akan memilih 128 bit karena cocok persis menjadi 16 byte dan dipandang sangat memadaiuntuk menjadi unik pada dasarnya setiap, tetapi yang paling ekstrim, gunakan kasing dan Anda tidak perlu memikirkan duplikat. Berikut ini adalah tabel perbandingan entropi sederhana termasuk analisis sederhana masalah ulang tahun .Untuk persyaratan sederhana, panjang 8 atau 12 byte mungkin cukup, tetapi dengan 16 byte Anda berada di "sisi aman".
Dan pada dasarnya itu. Hal terakhir adalah memikirkan encoding sehingga dapat direpresentasikan sebagai teks yang dapat dicetak (baca, a
String
).3. Binary to Text Encoding
Pengkodean umum meliputi:
Base64
setiap karakter mengkodekan 6bit menciptakan overhead 33%. Untungnya ada implementasi standar di Java 8+ dan Android . Dengan Java yang lebih lama Anda dapat menggunakan salah satu dari banyak perpustakaan pihak ketiga . Jika Anda ingin token Anda menjadi url aman, gunakan versi RFC4648 yang aman-url (yang biasanya didukung oleh sebagian besar implementasi). Contoh pengkodean 16 byte dengan bantalan:XfJhfv3C0P6ag7y9VQxSbw==
Base32
setiap karakter mengkodekan 5bit menciptakan overhead 40%. Ini akan menggunakanA-Z
dan2-7
menjadikannya ruang yang efisien, sementara alfa-numerik tidak peka terhadap huruf besar-kecil. Tidak ada implementasi standar di JDK . Contoh penyandian 16 byte tanpa bantalan:WUPIL5DQTZGMF4D3NX5L7LNFOY
Base16
(hex) setiap karakter mengkode 4bit yang membutuhkan 2 karakter per byte (mis. 16 byte membuat string dengan panjang 32). Oleh karena itu hex kurang efisien daripada ruangBase32
tetapi aman untuk digunakan dalam kebanyakan kasus (url) karena hanya menggunakan0-9
danA
untukF
. Misalnya encoding 16 bytes:4fa3dd0f57cb3bf331441ed285b27735
. Lihat diskusi SO tentang konversi ke hex di sini.Pengkodean tambahan seperti Base85 dan eksotis Base122 ada dengan efisiensi ruang yang lebih baik / buruk. Anda dapat membuat encoding sendiri (yang pada dasarnya sebagian besar jawaban di utas ini lakukan) tetapi saya akan menyarankan untuk tidak melakukannya, jika Anda tidak memiliki persyaratan yang sangat spesifik. Lihat lebih banyak skema penyandian di artikel Wikipedia.
4. Ringkasan dan Contoh
SecureRandom
hex
ataubase32
jika Anda membutuhkannya menjadi alpha-numeric)Jangan
Contoh: Hex Token Generator
Contoh: Generator Token Base64 (Aman Url)
Contoh: Alat Java CLI
Jika Anda ingin alat cli yang siap digunakan, Anda dapat menggunakan dadu: https://github.com/patrickfav/dice
Contoh: Masalah terkait - Lindungi Id Anda Saat Ini
Jika Anda sudah memiliki id yang dapat Anda gunakan (misalnya sintetis
long
di entitas Anda), tetapi tidak ingin mempublikasikan nilai internal , Anda dapat menggunakan perpustakaan ini untuk mengenkripsi dan mengaburkannya: https://github.com/patrickfav / id-masksumber
BigInteger
menggunakan parameter konstruktor:BigInteger(1, token)
alih-alihBigInteger(token)
.import java.security.SecureRandom;
danimport java.math.BigInteger;
diperlukan untuk menjadikan contoh itu berhasil, tetapi itu berhasil hebat!new SecureRandom()
menggunakan/dev/urandom
menggunakan Dollar harus sederhana seperti:
ini menghasilkan sesuatu seperti itu:
sumber
Ini dia di Jawa:
Inilah contoh menjalankan:
sumber
Random#nextInt
ataunextLong
. Beralih keSecureRandom
jika perlu.Tidak ada yang mengejutkan di sini yang menyarankannya tetapi:
Mudah.
Manfaat dari hal ini adalah UUID bagus dan panjang dan dijamin hampir tidak mungkin bertabrakan.
Wikipedia memiliki penjelasan yang bagus tentang hal itu:
http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates
4 bit pertama adalah tipe versi dan 2 untuk varian sehingga Anda mendapatkan 122 bit secara acak. Jadi jika Anda mau, Anda bisa memotong dari ujung untuk mengurangi ukuran UUID. Ini tidak disarankan tetapi Anda masih memiliki banyak keacakan, cukup untuk catatan 500k Anda mudah.
sumber
Solusi singkat dan mudah, tetapi hanya menggunakan huruf kecil dan angka:
Ukurannya sekitar 12 digit hingga basis 36 dan tidak dapat ditingkatkan lebih lanjut, dengan cara itu. Tentu saja Anda dapat menambahkan beberapa instance.
sumber
Long.toString(Math.abs(r.nextLong()), 36);
abs
diselesaikan dengan menggunakan operator bitwise untuk menghapus bit yang paling signifikan. Ini akan bekerja untuk semua nilai.<< 1 >>> 1
.Alternatif di Java 8 adalah:
sumber
Menggunakan UUID tidak aman, karena sebagian UUID tidak acak sama sekali. Prosedur @erickson sangat rapi, tetapi tidak membuat string dengan panjang yang sama. Cuplikan berikut harus cukup:
Mengapa memilih
length*5
. Mari kita asumsikan kasus sederhana dari string acak dengan panjang 1, jadi satu karakter acak. Untuk mendapatkan karakter acak yang berisi semua angka 0-9 dan karakter az, kita membutuhkan angka acak antara 0 dan 35 untuk mendapatkan satu dari setiap karakter.BigInteger
menyediakan konstruktor untuk menghasilkan angka acak, didistribusikan secara merata pada rentang0 to (2^numBits - 1)
. Sayangnya 35 adalah tidak ada nomor yang dapat diterima oleh 2 ^ numBits - 1. Jadi kita memiliki dua opsi: Baik dengan2^5-1=31
atau2^6-1=63
. Jika kita memilih,2^6
kita akan mendapatkan banyak angka "tidak perlu" / "lebih lama". Karena itu2^5
adalah pilihan yang lebih baik, bahkan jika kita kehilangan 4 karakter (wz). Untuk sekarang menghasilkan string dengan panjang tertentu, kita cukup menggunakan a2^(length*numBits)-1
jumlah. Masalah terakhir, jika kita menginginkan string dengan panjang tertentu, acak dapat menghasilkan sejumlah kecil, sehingga panjangnya tidak terpenuhi, jadi kita harus mengisi string dengan panjang nol yang diperlukan.sumber
sumber
Jadi apa yang dilakukan adalah menambahkan kata sandi ke dalam string dan ... ya berfungsi dengan baik memeriksanya ... sangat sederhana. Saya menulisnya
sumber
+ 0
sering menambahkannya ? Mengapa Anda membagi deklarasi tempat dan inisialisasi? Apa keuntungan dari indeks 1,2,3,4 daripada 0,1,2,3? Yang paling penting: Anda mengambil nilai acak, dan membandingkan dengan if-else 4 kali nilai baru, yang selalu bisa tidak cocok, tanpa mendapatkan lebih banyak keacakan. Tapi jangan ragu untuk mengembalikan.Saya menemukan solusi ini yang menghasilkan string hex disandikan acak. Tes unit yang disediakan tampaknya sesuai dengan kasus penggunaan utama saya. Meskipun, ini sedikit lebih kompleks daripada beberapa jawaban lain yang diberikan.
sumber
Ubah karakter String sesuai kebutuhan Anda.
String tidak dapat diubah. Di sini
StringBuilder.append
lebih efisien daripada penggabungan string.sumber
Random
instance baru di setiap iterasi dari loop tidak efisien.sumber
sumber
Tidak terlalu menyukai jawaban ini mengenai solusi "sederhana": S
Saya akan pergi untuk sederhana;), java murni, satu liner (entropi didasarkan pada panjang string acak dan set karakter yang diberikan):
atau (cara lama sedikit lebih mudah dibaca)
Tetapi di sisi lain Anda juga bisa menggunakan UUID yang memiliki entropi yang cukup bagus ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):
Semoga itu bisa membantu.
sumber
Anda menyebutkan "sederhana", tetapi kalau-kalau ada orang lain mencari sesuatu yang memenuhi persyaratan keamanan yang lebih ketat, Anda mungkin ingin melihat jpwgen . jpwgen dimodelkan setelah pwgen di Unix, dan sangat dapat dikonfigurasi.
sumber
Anda dapat menggunakan kelas UUID dengan pesan getLeastSignificantBits () untuk mendapatkan 64bit data Acak, kemudian mengonversinya menjadi nomor radix 36 (yaitu string yang terdiri dari 0-9, AZ):
Ini menghasilkan sebuah String hingga 13 karakter. Kami menggunakan Math.abs () untuk memastikan tidak ada tanda minus yang menyelinap masuk.
sumber
random.nextLong()
? Atau bahkanDouble.doubleToLongBits(Math.random())
?Anda dapat menggunakan kode berikut, jika kata sandi Anda wajib berisi angka karakter khusus alfabet:
sumber
Ini adalah kode satu baris oleh AbacusUtil
Acak tidak berarti itu harus unik. untuk mendapatkan string unik, menggunakan:
sumber
Ini dia solusi Scala:
sumber
menggunakan perpustakaan apache itu bisa dilakukan dalam satu baris
di sini adalah doc http://commons.apache.org/lang/api-2.3/org/apache/commons/lang/RandomStringUtils.html
sumber
sumber
Saya pikir ini adalah solusi terkecil di sini, atau hampir salah satu yang terkecil:
Kode berfungsi dengan baik. Jika Anda menggunakan metode ini, saya sarankan Anda menggunakan lebih dari 10 karakter. Tabrakan terjadi pada 5 karakter / 30362 iterasi. Ini membutuhkan waktu 9 detik.
sumber
sumber
length
bukanchars.length
di loop for:for (int i = 0; i < length; i++)
sumber