Tink, पासकोड से जुड़े गलत तरीकों का इस्तेमाल करने से रोकता है. जैसे:
- उपयोगकर्ता के पास पासकोड का ऐक्सेस होना – इसके बजाय, जब भी संभव हो, पासकोड को केएमएस में सेव किया जाना चाहिए. इसके लिए, पहले से तय किए गए उन तरीकों में से किसी एक का इस्तेमाल करें जिनमें Tink ऐसे सिस्टम के साथ काम करता है.
- कुंजियों के कुछ हिस्सों का उपयोगकर्ता ऐक्सेस – ऐसा करने से अक्सर गड़बड़ियां होती हैं.
असल में, कुछ मामलों में इन सिद्धांतों का उल्लंघन करना ज़रूरी होता है. Tink, ऐसा करने के लिए सुरक्षित तरीके उपलब्ध कराता है. इन तरीकों के बारे में नीचे दिए गए सेक्शन में बताया गया है.
सीक्रेट कुंजी ऐक्सेस टोकन
सीक्रेट कुंजी मटीरियल को ऐक्सेस करने के लिए, उपयोगकर्ताओं के पास एक टोकन होता है. टोकन आम तौर पर किसी क्लास का ऑब्जेक्ट होता है. इसमें कोई फ़ंक्शन नहीं होता. आम तौर पर, टोकन
InsecureSecretKeyAccess.get()
जैसे तरीके से दिया जाता है. Google में, उपयोगकर्ताओं को इस फ़ंक्शन का इस्तेमाल करने से रोका जाता है. इसके लिए, उन्हें Baज़र BUILD का ऐक्सेस देने की सुविधा का इस्तेमाल करना होता है. Google के बाहर, सुरक्षा समीक्षक इस फ़ंक्शन के इस्तेमाल के बारे में
अपना कोड बेस खोज सकते हैं.
इन टोकन की एक मददगार सुविधा यह है कि इन्हें पास किया जा सकता है. उदाहरण के लिए, मान लें कि आपके पास एक ऐसा फ़ंक्शन है जो किसी आर्बिट्रेरी Tink कुंजी को क्रम में लगाता है:
String serializeKey(Key key, @Nullable SecretKeyAccess secretKeyAccess);
जिन पासकोड में सीक्रेट पासकोड का कॉन्टेंट होता है उनके लिए, इस फ़ंक्शन के लिए secretKeyAccess
ऑब्जेक्ट का नॉल-वैल्यू न होना ज़रूरी है. साथ ही, उसमें SecretKeyAccess
टोकन सेव होना चाहिए. जिन कुंजियों में कोई सीक्रेट कॉन्टेंट नहीं होता है उनके लिए, secretKeyAccess
को अनदेखा किया जाता है.
इस तरह के फ़ंक्शन को देखते हुए, ऐसा फ़ंक्शन लिखा जा सकता है जो पूरे कीसेट को क्रम में लगाता है:
String serializeKeyset(KeysetHandle keyset, @Nullable SecretKeyAccess
secretKeyAccess);
यह फ़ंक्शन, अंदरूनी तौर पर कीसेट में मौजूद हर कुंजी के लिए serializeKey
को कॉल करता है और दिए गए secretKeyAccess
को दिए गए फ़ंक्शन में पास करता है. इसके बाद, जो उपयोगकर्ता गुप्त कुंजी के कॉन्टेंट को सीरियलाइज़ किए बिना serializeKeyset
को कॉल करते हैं वे दूसरे आर्ग्युमेंट के तौर पर null
का इस्तेमाल कर सकते हैं. जिन उपयोगकर्ताओं को सीक्रेट कुंजी के कॉन्टेंट को क्रम से लगाना है उन्हें InsecureSecretKeyAccess.get()
का इस्तेमाल करना चाहिए.
पासकोड के कुछ हिस्सों का ऐक्सेस
टिंक कुंजियों में सिर्फ़ मूल कुंजी नहीं होती, बल्कि ऐसा मेटाडेटा भी होता है जो यह बताता है कि कुंजी का इस्तेमाल कैसे किया जाना चाहिए. साथ ही, इसमें यह भी बताया जाता है कि इसका इस्तेमाल किसी और तरीके से नहीं किया जाना चाहिए. उदाहरण के लिए, Tink में आरएसए एसएसए पीएसएस कुंजी से पता चलता है कि इस आरएसए कुंजी का इस्तेमाल, सिर्फ़ PSS सिग्नेचर एल्गोरिदम के साथ किया जा सकता है. इसके लिए, तय किए गए हैश फ़ंक्शन और सॉल्ट लंबाई का इस्तेमाल किया जा सकता है.
कभी-कभी, Tink पासकोड को ऐसे अलग-अलग फ़ॉर्मैट में बदलना ज़रूरी होता है जिनमें यह सारा मेटाडेटा साफ़ तौर पर शामिल न हो. आम तौर पर, इसका मतलब है कि कुंजी का इस्तेमाल करते समय मेटाडेटा देना ज़रूरी है. दूसरे शब्दों में, यह मानते हुए कि कुंजी का इस्तेमाल हमेशा एक ही एल्गोरिदम के साथ किया जाता है, जैसे कि किसी कुंजी का मेटाडेटा अब भी एक ही है. ऐसे में, उसे किसी दूसरी जगह सेव करके रखा जाता है.
किसी Tink पासकोड को किसी दूसरे फ़ॉर्मैट में बदलते समय, आपको यह पक्का करना होगा कि Tink पासकोड का मेटाडेटा, दूसरे पासकोड फ़ॉर्मैट के मेटाडेटा से मैच करता हो. अगर यह मेल नहीं खाता है, तो कन्वर्ज़न पूरा नहीं होना चाहिए.
अक्सर, ये जांच नहीं की जाती हैं या अधूरी की जाती हैं. इसलिए, Tink उन एपीआई के ऐक्सेस पर पाबंदी लगाता है जो सिर्फ़ कुछ हिस्से का ऐक्सेस देने वाले पासकोड को ऐक्सेस करते हैं. हालांकि, इन पासकोड को पूरी पासकोड के तौर पर गलत तरीके से इस्तेमाल किया जा सकता है. Java में, Tink इसके लिए RestrictedApi का इस्तेमाल करता है, C++ और Golang में, यह सीक्रेट कुंजी ऐक्सेस टोकन से मिलते-जुलते टोकन का इस्तेमाल करता है.
इन एपीआई का इस्तेमाल करने वाले लोगों की ज़िम्मेदारी है कि वे फिर से इस्तेमाल किए जाने वाले मुख्य हमलों और काम न करने वालों, दोनों को रोक सकें.
आपको ज़्यादातर ऐसे तरीके मिलते हैं जो Tink से कुंजियां एक्सपोर्ट करने या उन्हें इंपोर्ट करने के मामले में, "पार्शियल बटन के ऐक्सेस" को प्रतिबंधित करते हैं. यहां दिए गए सबसे सही तरीकों से, इन स्थितियों में सुरक्षित तरीके से काम करने का तरीका बताया गया है.
सबसे सही तरीका: एक्सपोर्ट की गई कुंजी के सभी पैरामीटर की पुष्टि करना
उदाहरण के लिए, अगर कोई ऐसा फ़ंक्शन लिखा जाता है जो HPKE सार्वजनिक पासकोड को एक्सपोर्ट करता है:
सार्वजनिक पासकोड एक्सपोर्ट करने का गलत तरीका:
/** Provide the key to our users which don't have Tink. */ byte[] exportTinkHpkeKey(HpkePublicKey key) { return key.getPublicKeyBytes().toByteArray(); }
इससे समस्या होती है. पासकोड मिलने के बाद, उसका इस्तेमाल करने वाला तीसरा पक्ष, पासकोड के पैरामीटर के बारे में कुछ अनुमान लगाता है: उदाहरण के लिए, वह यह अनुमान लगाएगा कि इस 256-बिट पासकोड के लिए इस्तेमाल किया गया एचपीकेई एईएडी एल्गोरिदम, एईएस-जीसीएम था.
सुझाव: पुष्टि करें कि मुख्य एक्सपोर्ट के दौरान पैरामीटर आपकी उम्मीद के मुताबिक हैं.
सार्वजनिक पासकोड एक्सपोर्ट करने का बेहतर तरीका:
/** Provide the key to our users which don't have Tink. */ byte[] exportTinkHpkeKeyForOurUsers(HpkePublicKey key) { // Our users assume we use KEM_P256_HKDF_SHA256 for the KEM. if (!key.getParameters().getKemId().equals(HpkeParameters.KemId.KEM_P256_HKDF_SHA256)) { throw new IllegalArgumentException("Bad parameters"); } // Our users assume we use HKDF SHA256 to create the key material. if (!key.getParameters().getKdfId().equals(HpkeParameters.KdfId.HKDF_SHA256)) { throw new IllegalArgumentException("Bad parameters"); } // Our users assume that we use AES GCM with 256 bit keys. if (!key.getParameters().getAeadId().equals(HpkeParameters.AeadId.AES_256_GCM)) { throw new IllegalArgumentException("Bad parameters"); } // Our users assume we follow the standard and don't add a Tink style prefix if (!key.getParameters().getVariant().equals(HpkeParameters.Variant.NO_PREFIX)) { throw new IllegalArgumentException("Bad parameters"); } return key.getPublicKeyBytes().toByteArray(); }
सबसे सही तरीका: कुंजी इंपोर्ट करते समय, Tink ऑब्जेक्ट का इस्तेमाल जल्द से जल्द करें
इससे, कुंजी के गलत इस्तेमाल से जुड़े हमलों का जोखिम कम हो जाता है. ऐसा इसलिए होता है, क्योंकि Tink Key ऑब्जेक्ट, सही एल्गोरिदम के बारे में पूरी जानकारी देता है और कुंजी के साथ-साथ सारा मेटाडेटा भी सेव करता है.
नीचे दिया गया उदाहरण देखें:
टाइप न किए गए उपयोगकर्ताओं का इस्तेमाल:
void verifyEcdsaSignature(ECPoint ecPoint, byte[] signature, byte[] message) throws Exception { EcdsaParameters parameters = EcdsaParameters.builder() .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) .setCurveType(EcdsaParameters.CurveType.NIST_P256) .setHashType(EcdsaParameters.HashType.SHA256) .setVariant(EcdsaParameters.Variant.NO_PREFIX) .build(); EcdsaPublicKey key = EcdsaPublicKey.builder() .setParameters(parameters) .setPublicPoint(ecPoint) .build(); KeysetHandle handle = KeysetHandle.newBuilder() .addEntry(KeysetHandle.importKey(key).withFixedId(1).makePrimary()) .build(); PublicKeyVerify publicKeyVerify = handle.getPrimitive(RegistryConfiguration.get(), PublicKeyVerify.class); publicKeyVerify.verify(signature, message); }
इस तरह की गड़बड़ी होने की संभावना है: कॉल साइट पर यह भूलना बहुत आसान है कि आपको
किसी दूसरे एल्गोरिदम के साथ एक ही ecPoint
का इस्तेमाल कभी नहीं करना चाहिए. उदाहरण के लिए, अगर encryptWithECHybridEncrypt
नाम का कोई मिलता-जुलता फ़ंक्शन मौजूद है, तो कॉलर किसी मैसेज को एन्क्रिप्ट करने के लिए, उसी कर्व पॉइंट का इस्तेमाल कर सकता है. इससे आसानी से सुरक्षा से जुड़ी समस्याएं हो सकती हैं.
इसके बजाय, verifyEcdsaSignature
को बदलना बेहतर है, ताकि पहला तर्क EcdsaPublicKey
हो. असल में, जब भी कुंजी को डिस्क या नेटवर्क से पढ़ा जाता है, तो उसे तुरंत EcdsaPublicKey
ऑब्जेक्ट में बदल दिया जाना चाहिए: इस समय आपको पहले से पता होता है कि कुंजी का इस्तेमाल किस तरह किया जाता है. इसलिए, बेहतर होगा कि आप इसका इस्तेमाल करें.
पिछले कोड को और बेहतर बनाया जा सकता है. EcdsaPublicKey
में पास होने के बजाय, KeysetHandle
में पास होना बेहतर है. यह बिना किसी अतिरिक्त काम के, कोड को पासकोड बदलने के लिए तैयार करता है. इसलिए, इस एट्रिब्यूट को प्राथमिकता दी जानी चाहिए.
सुधार नहीं किए गए हैं. हालांकि: PublicKeyVerify
ऑब्जेक्ट में पास करना और भी बेहतर होता है: यह इस फ़ंक्शन के लिए काफ़ी है, इसलिए PublicKeyVerify
ऑब्जेक्ट में पास होने से, उन जगहों की संख्या बढ़ सकती है जहां इस फ़ंक्शन का इस्तेमाल किया जा सकता है. हालांकि, इस समय फ़ंक्शन काफ़ी आसान हो जाता है और इसे इनलाइन किया जा सकता है.
सुझाव: जब डिस्क या नेटवर्क से पहली बार मुख्य कॉन्टेंट पढ़ा जाता है, तो जल्द से जल्द इससे जुड़े Tink ऑब्जेक्ट बनाएं.
टाइप किए गए इस्तेमाल:
KeysetHandle readEcdsaKeyFromFile(Path fileWithEcdsaKey) throws Exception { byte[] content = Files.readAllBytes(fileWithEcdsaKey); BigInteger x = new BigInteger(1, Arrays.copyOfRange(content, 0, 32)); BigInteger y = new BigInteger(1, Arrays.copyOfRange(content, 32, 64)); ECPoint point = new ECPoint(x, y); EcdsaParameters parameters = EcdsaParameters.builder() .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) .setCurveType(EcdsaParameters.CurveType.NIST_P256) .setHashType(EcdsaParameters.HashType.SHA256) .setVariant(EcdsaParameters.Variant.NO_PREFIX) .build(); EcdsaPublicKey key = EcdsaPublicKey.builder() .setParameters(parameters) .setPublicPoint(ecPoint) .build(); return KeysetHandle.newBuilder() .addEntry(KeysetHandle.importKey(key).withFixedId(1).makePrimary()) .build(); }
ऐसे कोड का इस्तेमाल करके, हम बाइट-अरे को पढ़ने के तुरंत बाद, उसे Tink ऑब्जेक्ट में बदल देते हैं. साथ ही, हम पूरी तरह से बताते हैं कि किस एल्गोरिदम का इस्तेमाल किया जाना चाहिए. इस तरीके से, पासवर्ड को गच्चा देने वाले हमलों की संभावना कम हो जाती है.