java - पासवर्ड के लिए स्ट्रिंग पर char[] पसंदीदा क्यों है?




string security (12)

  1. स्ट्रिंग्स जावा में अपरिवर्तनीय हैं यदि आप पासवर्ड को सादे पाठ के रूप में संग्रहीत करते हैं तो यह स्मृति में तब तक उपलब्ध होगा जब तक कि कचरा कलेक्टर इसे साफ़ नहीं कर लेता है और स्ट्रिंग पूल में स्ट्रिंग्स का उपयोग पुन: प्रयोज्यता के लिए किया जाता है, इसलिए यह बहुत अधिक संभावना है कि यह लंबे समय तक स्मृति में रहेगा अवधि, जो एक सुरक्षा खतरा बनता है। चूंकि मेमोरी डंप तक पहुंचने वाले किसी भी व्यक्ति को स्पष्ट टेक्स्ट में पासवर्ड मिल सकता है
  2. getPassword() विधि का उपयोग कर जावा अनुशंसा जो एक char [] और बहिष्कृत getText() विधि देता है जो सुरक्षा कारण बताते हुए स्पष्ट टेक्स्ट में पासवर्ड देता है।
  3. toString () लॉग फ़ाइल या कंसोल में सादे पाठ को मुद्रित करने का हमेशा जोखिम होता है लेकिन यदि ऐरे का उपयोग होता है तो आप सरणी की सामग्री प्रिंट नहीं करेंगे बल्कि इसकी स्मृति स्थान मुद्रित हो जाती है।

    String strPwd = "passwd";
    char[] charPwd = new char[]{'p','a','s','s','w','d'};
    System.out.println("String password: " + strPwd );
    System.out.println("Character password: " + charPwd );
    

    स्ट्रिंग पासवर्ड: passwd

    चरित्र पासवर्ड: [सी @ 110b2345

अंतिम विचार: हालांकि char [] का उपयोग करना पर्याप्त नहीं है, आपको सामग्री को और अधिक सुरक्षित होने की आवश्यकता है। मैं सादा पाठ के बजाय हैश या एन्क्रिप्टेड पासवर्ड के साथ काम करने का सुझाव देता हूं और प्रमाणीकरण पूरा होने पर इसे स्मृति से साफ़ कर देता हूं।

स्विंग में, पासवर्ड फ़ील्ड में सामान्य getText() (रिटर्न String ) विधि की बजाय getPassword() ( char[] ) विधि देता है। इसी प्रकार, मैंने एक सुझाव दिया है कि पासवर्ड को संभालने के लिए String का उपयोग न करें।

जब पासवर्ड की बात आती है तो String सुरक्षा के लिए खतरा क्यों उत्पन्न करता है? यह char[] का उपयोग करने के लिए असुविधाजनक लगता है।


आधिकारिक दस्तावेज़ को उद्धृत करने के लिए, जावा क्रिप्टोग्राफी आर्किटेक्चर गाइड यह char[] बनाम String पासवर्ड के बारे में कहता है (पासवर्ड-आधारित एन्क्रिप्शन के बारे में, लेकिन यह आमतौर पर पाठ्यक्रम के पासवर्ड के बारे में अधिक है):

यह java.lang.String प्रकार के किसी ऑब्जेक्ट में पासवर्ड एकत्र और संग्रहीत करने के लिए तार्किक प्रतीत होता है। हालांकि, यहां चेतावनी दी गई है: Object प्रकार String अपरिवर्तनीय हैं, यानी, कोई विधियां परिभाषित नहीं हैं जो आपको उपयोग के बाद String की सामग्री को बदलने (ओवरराइट) करने या शून्य करने की अनुमति देती हैं। यह सुविधा String ऑब्जेक्ट्स को उपयोगकर्ता संवेदनशील पासवर्ड जैसी सुरक्षा संवेदनशील जानकारी संग्रहीत करने के लिए अनुपयुक्त बनाती है। आपको सुरक्षा संवेदनशील जानकारी को हमेशा एक char सरणी में एकत्र और संग्रहीत करना चाहिए।

जावा प्रोग्रामिंग भाषा के लिए सुरक्षित कोडिंग दिशानिर्देशों के दिशानिर्देश 2-2, संस्करण 4.0 भी कुछ समान कहता है (हालांकि यह मूल रूप से लॉगिंग के संदर्भ में है):

दिशानिर्देश 2-2: अत्यधिक संवेदनशील जानकारी लॉग न करें

कुछ जानकारी, जैसे सोशल सिक्योरिटी नंबर (एसएसएन) और पासवर्ड, अत्यधिक संवेदनशील हैं। यह जानकारी प्रशासकों द्वारा भी आवश्यकतानुसार लंबे समय तक नहीं रखी जानी चाहिए और न ही इसे देखा जा सकता है। उदाहरण के लिए, इसे लॉग फ़ाइलों में नहीं भेजा जाना चाहिए और इसकी उपस्थिति खोजों के माध्यम से पता लगाने योग्य नहीं होनी चाहिए। कुछ क्षणिक डेटा को परिवर्तनीय डेटा संरचनाओं जैसे कि चार सरणी में रखा जा सकता है, और उपयोग के तुरंत बाद साफ़ किया जा सकता है। क्लीयरिंग डेटा स्ट्रक्चर ने सामान्य जावा रनटाइम सिस्टम पर प्रभावशीलता कम कर दी है क्योंकि ऑब्जेक्ट्स प्रोग्रामर को पारदर्शी रूप से स्मृति में स्थानांतरित कर दिए जाते हैं।

इस दिशानिर्देश में निचले स्तर के पुस्तकालयों के कार्यान्वयन और उपयोग के लिए भी प्रभाव पड़ता है जिनके साथ वे डेटा का अर्थपूर्ण ज्ञान नहीं रखते हैं। उदाहरण के तौर पर, निम्न-स्तरीय स्ट्रिंग पार्सिंग लाइब्रेरी उस पाठ को लॉग कर सकती है जिस पर यह काम करता है। एक एप्लिकेशन पुस्तकालय के साथ एक एसएसएन पार्स कर सकते हैं। यह ऐसी स्थिति बनाता है जहां एसएसएन प्रशासकों को लॉग फ़ाइलों तक पहुंच के साथ उपलब्ध हैं।


चरित्र वर्ण ( char[] ) को प्रत्येक वर्ण को शून्य और स्ट्रिंग्स पर सेट करके उपयोग के बाद साफ़ किया जा सकता है। यदि कोई मेमोरी छवि को किसी भी तरह देख सकता है, तो स्ट्रिंग्स का उपयोग होने पर वे सादा पाठ में एक पासवर्ड देख सकते हैं, लेकिन यदि 0 [0] के साथ डेटा शुद्ध करने के बाद char[] का उपयोग किया जाता है, तो पासवर्ड सुरक्षित होता है।


जबकि यहां अन्य सुझाव मान्य हैं, एक और अच्छा कारण है। सादा String साथ आपके पास लॉग , मॉनीटर या कुछ अन्य असुरक्षित जगह पर पासवर्ड को गलती से प्रिंट करने की संभावना अधिक है। char[] कम कमजोर है।

इस पर विचार करो:

public static void main(String[] args) {
    Object pw = "Password";
    System.out.println("String: " + pw);

    pw = "Password".toCharArray();
    System.out.println("Array: " + pw);
}

प्रिंटों:

String: Password
Array: [[email protected]

जॉन स्कीट के अनुसार, प्रतिबिंब का उपयोग करके छोड़कर कोई रास्ता नहीं है।

हालांकि, अगर प्रतिबिंब आपके लिए एक विकल्प है, तो आप यह कर सकते हैं।

public static void main(String[] args) {
    System.out.println("please enter a password");
    // don't actually do this, this is an example only.
    Scanner in = new Scanner(System.in);
    String password = in.nextLine();
    usePassword(password);

    clearString(password);

    System.out.println("password: '" + password + "'");
}

private static void usePassword(String password) {

}

private static void clearString(String password) {
    try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] chars = (char[]) value.get(password);
        Arrays.fill(chars, '*');
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

जब दौड़ते हैं

please enter a password
hello world
password: '***********'

नोट: यदि स्ट्रिंग के चार [] को जीसी चक्र के हिस्से के रूप में कॉपी किया गया है, तो पिछली प्रति स्मृति में कहीं भी एक मौका है।

यह पुरानी प्रति एक ढेर डंप में दिखाई नहीं देगी, लेकिन अगर आपके पास प्रक्रिया की कच्ची मेमोरी तक सीधी पहुंच है तो आप इसे देख सकते हैं। आम तौर पर आपको ऐसी पहुंच रखने वाले किसी से भी बचना चाहिए।


मुझे नहीं लगता कि यह एक वैध सुझाव है, लेकिन, मैं कम से कम कारण पर अनुमान लगा सकता हूं।

मुझे लगता है कि प्रेरणा यह सुनिश्चित करना चाहती है कि आप तुरंत स्मृति में पासवर्ड के सभी निशान मिटा सकते हैं और इसके उपयोग के बाद निश्चितता के साथ। एक char[] साथ char[] आप सरणी के प्रत्येक तत्व को रिक्त या कुछ निश्चित रूप से ओवरराइट कर सकते हैं। आप उस तरह से एक String के आंतरिक मूल्य को संपादित नहीं कर सकते हैं।

लेकिन वह अकेला अच्छा जवाब नहीं है; क्यों न केवल सुनिश्चित करें कि char[] या String संदर्भ नहीं बचता है? फिर कोई सुरक्षा समस्या नहीं है। लेकिन बात यह है कि String ऑब्जेक्ट intern() एड सिद्धांत में हो सकते हैं और निरंतर पूल के अंदर जीवित रहते हैं। मुझे लगता है कि char[] का उपयोग इस संभावना को रोकता है।


संपादित करें: सुरक्षा अनुसंधान के एक वर्ष के बाद इस जवाब पर वापस आकर, मुझे एहसास हुआ कि यह दुर्भाग्यपूर्ण निहितार्थ बनाता है कि आप वास्तव में सादे टेक्स्ट पासवर्ड की तुलना करेंगे। कृपया मत करो एक नमक और पुनरावृत्तियों की उचित संख्या के साथ एक सुरक्षित एक तरफा हैश का प्रयोग करें । लाइब्रेरी का उपयोग करने पर विचार करें: यह सामान सही हो जाना मुश्किल है!

मूल उत्तर: इस तथ्य के बारे में क्या है कि String.equals () शॉर्ट सर्किट मूल्यांकन का उपयोग करता है, और इसलिए एक समय के हमले के लिए कमजोर है? यह असंभव हो सकता है, लेकिन आप पात्रों के सही अनुक्रम को निर्धारित करने के लिए सैद्धांतिक रूप से पासवर्ड तुलना कर सकते हैं।

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // Quits here if Strings are different lengths.
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // Quits here at first different character.
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

समय पर हमलों पर कुछ और संसाधन:


स्ट्रिंग्स अपरिवर्तनीय हैं । इसका मतलब है कि String , यदि कोई अन्य प्रक्रिया मेमोरी को डंप कर सकती है, तो कचरा संग्रह से पहले डेटा से छुटकारा पाने के लिए कोई रास्ता नहीं है ( reflection से अलग)।

एक सरणी के साथ, आप इसके साथ किए जाने के बाद डेटा को स्पष्ट रूप से मिटा सकते हैं। आप अपनी पसंद की किसी भी चीज़ के साथ सरणी को ओवरराइट कर सकते हैं, और कचरा संग्रह से पहले भी, सिस्टम सिस्टम में कहीं भी मौजूद नहीं होगा।

तो हाँ, यह एक सुरक्षा चिंता है - लेकिन यहां तक ​​कि char[] का उपयोग करने से हमलावर के लिए अवसर की खिड़की कम हो जाती है, और यह केवल इस विशिष्ट प्रकार के हमले के लिए है।

जैसा कि टिप्पणियों में उल्लेख किया गया है, यह संभव है कि कचरा कलेक्टर द्वारा स्थानांतरित किए जाने वाले सरणी डेटा में डेटा की भयानक प्रतियां छोड़ दें। मेरा मानना ​​है कि यह कार्यान्वयन-विशिष्ट है - कचरा कलेक्टर इस तरह की चीज से बचने के लिए सभी स्मृति को साफ़ कर सकता है। यहां तक ​​कि अगर ऐसा होता है, तब भी वह समय है जब char[] में वास्तविक वर्ण एक हमले विंडो के रूप में होते हैं।


ऐसा कुछ भी नहीं है जो चार सरणी आपको बनाम स्ट्रिंग देता है जब तक कि आप इसे उपयोग के बाद मैन्युअल रूप से साफ़ नहीं करते हैं, और मैंने वास्तव में ऐसा करने वाले किसी को भी नहीं देखा है। तो मेरे लिए char [] बनाम स्ट्रिंग की वरीयता थोड़ा अतिरंजित है।

hereव्यापक रूप से उपयोग की जाने वाली स्प्रिंग सुरक्षा लाइब्रेरी पर एक नज़र डालें और खुद से पूछें - क्या स्प्रिंग सिक्योरिटी लोग अक्षम हैं या चार [] पासवर्ड सिर्फ ज्यादा समझ में नहीं आते हैं। जब कुछ बुरा हैकर आपकी रैम की मेमोरी डंप पकड़ लेते हैं तो सुनिश्चित करें कि उन्हें सभी पासवर्ड मिलेंगे, भले ही आप उन्हें छुपाने के परिष्कृत तरीकों का उपयोग करें।here

हालांकि, जावा हर समय बदलता है, और जावा 8 की स्ट्रिंग डीडुप्लिकेशन फीचर जैसी कुछ डरावनी विशेषताएं आपके ज्ञान के बिना इंस्ट्रिंग स्ट्रिंग ऑब्जेक्ट्स हो सकती हैं। लेकिन यह अलग बातचीत है।


जावा में स्ट्रिंग अपरिवर्तनीय है। तो जब भी एक स्ट्रिंग बनाई जाती है, तब तक यह स्मृति में तब तक रहेगी जब तक कि यह कचरा इकट्ठा न हो जाए। तो जो भी स्मृति तक पहुंच प्राप्त करता है वह स्ट्रिंग के मान को पढ़ सकता है।
यदि स्ट्रिंग का मान संशोधित किया गया है तो यह एक नई स्ट्रिंग बनाने समाप्त हो जाएगा। इसलिए मूल मूल्य और संशोधित मान स्मृति में तब तक रहते हैं जब तक कि यह कचरा एकत्र न हो जाए।

वर्ण सरणी के साथ, पासवर्ड के उद्देश्य के बाद सरणी की सामग्री को संशोधित या मिटाया जा सकता है। सरणी की मूल सामग्री संशोधित होने के बाद स्मृति में नहीं मिलेगी और कचरा संग्रह में आने से पहले भी।

सुरक्षा चिंता के कारण पासवर्ड को एक वर्ण सरणी के रूप में स्टोर करना बेहतर होता है।


स्ट्रिंग अपरिवर्तनीय है और यह स्ट्रिंग पूल में जाती है। एक बार लिखा, इसे अधिलेखित नहीं किया जा सकता है।

char[] एक सरणी है जिसे आप पासवर्ड इस्तेमाल करने के बाद ओवरराइट करना चाहिए और इस तरह यह किया जाना चाहिए:

char[] passw = request.getPassword().toCharArray()
if (comparePasswords(dbPassword, passw) {
 allowUser = true;
 cleanPassword(passw);
 cleanPassword(dbPassword);
 passw=null;
}

private static void cleanPassword (char[] pass) {
 for (char ch: pass) {
  ch = null;
 }
}

एक परिदृश्य जहां हमलावर इसका उपयोग कर सकता है एक क्रैशडंप है - जब JVM क्रैश हो जाता है और मेमोरी डंप उत्पन्न करता है - तो आप पासवर्ड देख पाएंगे।

यह एक दुर्भावनापूर्ण बाहरी हमलावर नहीं है। यह एक सहायक उपयोगकर्ता हो सकता है जिसके पास निगरानी उद्देश्यों के लिए सर्वर तक पहुंच है। वह एक दुर्घटनाग्रस्त हो सकता है और पासवर्ड ढूंढ सकता है।


स्ट्रिंग्स अपरिवर्तनीय हैं और उन्हें बनाए जाने के बाद बदला नहीं जा सकता है। एक स्ट्रिंग के रूप में पासवर्ड बनाना ढेर या स्ट्रिंग पूल पर पासवर्ड के भटक संदर्भ छोड़ देगा। अब अगर कोई जावा प्रक्रिया का ढेर डंप लेता है और ध्यान से स्कैन करता है तो वह पासवर्ड अनुमान लगाने में सक्षम हो सकता है। निस्संदेह इन गैर-इस्तेमाल किए गए तार कचरा एकत्र होंगे, लेकिन यह उस पर निर्भर करता है जब जीसी किक करता है।

दूसरी तरफ char [] प्रमाणीकरण के तुरंत बाद उत्परिवर्तनीय होते हैं, आप उन्हें सभी एम या बैकस्लाश जैसे किसी भी चरित्र से ओवरराइट कर सकते हैं। अब भी अगर कोई ढेर डंप लेता है तो वह उस पासवर्ड को प्राप्त करने में सक्षम नहीं हो सकता है जो वर्तमान में उपयोग में नहीं है। इससे आपको ऑब्जेक्ट सामग्री को स्वयं साफ़ करने जैसे जीसी के लिए इंतजार कर रहे हैं, इस अर्थ में आपको अधिक नियंत्रण मिलता है।






char