स्रोत स्तर पर memory safety कमजोरियों को खत्म करना
(security.googleblog.com)memory safety कमजोरियों की जड़ समाप्त करना
विरोधाभासी परिणाम
- जब memory-unsafe भाषाओं में लिखे गए codebase बढ़ते हैं, तब नए features को memory-safe भाषाओं में शिफ्ट करने से memory safety कमजोरियां काफी कम हो जाती हैं
- इसका कारण यह है कि समय के साथ कमजोरियां घातीय रूप से घटती हैं
गणितीय व्याख्या
- कमजोरियों का जीवनकाल exponential distribution का पालन करता है
- कमजोरियां मुख्य रूप से नए code में उत्पन्न होती हैं, और समय के साथ code अधिक सुरक्षित होता जाता है
- 5 साल पुराने code की vulnerability density नए code की तुलना में 3.4 गुना से 7.4 गुना कम होती है
Android में वास्तविक उदाहरण
- 2019 से Android टीम ने नए development को memory-safe भाषाओं में शिफ्ट करना शुरू किया
- 2024 तक memory safety कमजोरियां 76% से घटकर 24% रह गईं
- memory safety कमजोरियां घटने के साथ कुल security risk भी कम हुआ
memory safety रणनीति का विकास
- पहली पीढ़ी: reactive patching - कमजोरियों को खोजकर ठीक करने का तरीका
- दूसरी पीढ़ी: proactive mitigation - कमजोरियों के exploit को कठिन बनाने का तरीका
- तीसरी पीढ़ी: proactive vulnerability discovery - कमजोरियों को पहले से खोज निकालने का तरीका
- चौथी पीढ़ी: high-assurance prevention - memory-safe भाषाओं में शिफ्ट करके कमजोरियों के पैदा होने को ही रोकने का तरीका
high-assurance prevention के फायदे
- defender और attacker के बीच की अंतहीन प्रतिस्पर्धा को तोड़ता है
- memory-safe भाषाओं के जरिए security बढ़ती है और लागत घटती है
- code की correctness और developer productivity बढ़ती है
सीख से अमल तक
- मौजूदा memory-unsafe code को पूरी तरह छोड़ने या फिर से लिखने की जरूरत नहीं है
- interoperability बेहतर बनाकर memory-safe भाषाओं की ओर बदलाव को तेज किया जा सकता है
- Rust और C++, तथा Rust और Kotlin के बीच interoperability बेहतर बनाने वाले tools विकसित किए जा रहे हैं
पिछली पीढ़ियों की भूमिका
- proactive mitigation और detection का चयनात्मक उपयोग
- memory-safe code की ओर शिफ्ट होने के साथ mitigation और detection की जरूरत कम होती जाती है
निष्कर्ष
- नए code में memory-safe भाषाओं का उपयोग करने से कमजोरियां घातीय रूप से घटती हैं
- Android में 6 साल से अधिक के लगातार परिणाम इस approach की प्रभावशीलता साबित करते हैं
GN⁺ का सार
- memory safety कमजोरियों को कम करने के लिए memory-safe भाषाओं की ओर शिफ्ट करना महत्वपूर्ण है
- Android टीम के उदाहरण से पुष्टि होती है कि memory safety कमजोरियां काफी कम हुईं
- मौजूदा code को पूरी तरह फिर से लिखने के बजाय interoperability सुधारना अधिक व्यावहारिक है
- Rust जैसी memory-safe भाषाओं का उपयोग security और productivity दोनों को एक साथ बढ़ा सकता है
1 टिप्पणियां
Hacker News की राय
दिलचस्प लेख है। मुख्य बात यह है कि दुनिया की हर चीज़ को फिर से लिखने की ज़रूरत नहीं है
सिर्फ नए development को memory-safe language में ले जाना भी सार्थक सुधार ला सकता है, और असर देखने के लिए पूरे सिस्टम को port करने से यह कहीं आसान और सस्ता है
साथ ही, unsafe legacy code के साथ मज़बूती से integrate कर सकने वाली languages और tools की अहमियत भी बढ़ जाती है
यह भी सोचने वाली बात है कि बहुत पुराने code में “bathtub curve” जैसा कोई असर होता है या नहीं। गंभीर OpenSSL vulnerabilities को लेकर, शायद Heartbleed के समय कई लोग code देखकर हैरान रह गए थे
लेकिन 2019 से 2023 तक बिना rewrite किए सिर्फ नया code memory-safe languages में, शायद ज़्यादातर Rust और कुछ Kotlin में, जोड़ने भर से समस्याएँ लगभग 60% कम हुईं — यह प्रभावशाली है। 2021 से 2023 के बीच ठहराव क्यों आया, यह भी जानने की उत्सुकता है
2024 के आँकड़ों को extrapolate करके इस्तेमाल करना अच्छे नंबर दिखाने की मंशा जैसा लगता है; बेहतर होता कि 2022 से 2023 के बीच बढ़ोतरी क्यों हुई, इसका विश्लेषण और स्पष्टीकरण दिया जाता
यह भी नहीं बताया गया कि नया memory-safe code किस language में कितना लिखा गया। लगता है Rust और Kotlin दोनों इस्तेमाल हो रहे हैं, लेकिन Rust 95% है या 50%, और अगर Kotlin का हिस्सा बड़ा है तो किन क्षेत्रों में उसे Rust की जगह इस्तेमाल किया जा रहा है — यह जानना दिलचस्प होता
इस लेख का chart स्पष्टता और संक्षिप्तता के कारण अलग दिखता है। यह अच्छी तरह दिखाता है कि सावधानी से किया गया data selection और labeling इच्छित विचार को prose में स्वाभाविक रूप से पिरो सकता है
vulnerabilities के exponential रूप से घटने से जो निष्कर्ष निकलता है, वह यह है कि ध्यान पूरी तरह नए code पर होना चाहिए। बड़े, अंधाधुंध “Rust में rewrite” projects पर resources लगाना, अधिकतम memory safety के लक्ष्य के लिए भी, अप्रभावी है
सबसे आसान strategy, और व्यावहारिक Rust experts की सुझाई हुई strategy, data के अनुसार memory vulnerabilities को न्यूनतम करने की सबसे अच्छी strategy भी है — यह काफ़ी अच्छी तरह मेल खाता है
“Android टीम ने देखा कि Rust changes का rollback rate C++ के आधे से भी कम है” — यह हिस्सा चौंकाने वाला है
“vulnerabilities exponential रूप से घटती हैं। उनका half-life होता है। [...] 2022 Usenix Security में प्रस्तुत बड़े पैमाने के vulnerability lifetime study ने भी इस phenomenon की पुष्टि की। शोधकर्ताओं ने पाया कि vulnerabilities का अधिकांश हिस्सा नए code या हाल में बदले गए code में होता है”
अगर ऐसा है, तो security के लिए ज़रूरी न होने वाले नए features जोड़ना बंद करना बेहतर हो सकता है। Windows LTSC शायद Windows का सबसे सुरक्षित version होगा
लेकिन जो vulnerabilities अभी तक exploit नहीं हुईं, उनके लिए ऐसा कोई reduction mechanism नहीं होता। वे user complaints या bug reports पैदा किए बिना बस पड़ी रहती हैं, जब तक कि पर्याप्त resources और motivation वाला कोई adversary उन्हें खोजकर exploit न कर ले
और पहले की तुलना में अब ऐसे स्तर के adversaries ज़्यादा हैं
बेशक इसका मतलब यह नहीं कि सारे bugs सिर्फ हाल के code में ही होते हैं। आपने ऐसे bugs भी ज़रूर देखे होंगे जो कई साल तक बिना detect हुए बने रहे। ऐसे bugs के मामले में पूछना चाहिए कि tests उन्हें क्यों चूक गए
इसलिए tests का ध्यान हाल में बदले गए code पर होना चाहिए। खासकर mutation testing को बदले गए code या उससे मज़बूती से जुड़े code पर बहुत केंद्रित ढंग से लागू किया जा सकता है, जिससे इस testing का बोझ काफ़ी कम हो जाता है
Google में code review के साथ mutation testing को इस तरह इस्तेमाल करने वाला एक system था
बेशक यह हर जगह व्यावहारिक हो, ऐसा नहीं है, लेकिन यह तरीका आज से कहीं ज़्यादा आम होना चाहिए
bleeding-edge versions में नई vulnerabilities बहुत होती हैं। आम तौर पर supported versions में सबसे पुरानी release ही सबसे सुरक्षित होती है
हाँ, अगर नई version की features का मूल्य ज़्यादा हो तो बात अलग है, लेकिन कुल मिलाकर “.0” पर खत्म होने वाले versions से बचना ही बेहतर है
नए code और memory vulnerability के बीच सहसंबंध है। ब्लॉग पोस्ट में यह संभावित व्याख्या भी दी गई है कि vulnerabilities की half-life तेज़ी से घटती है। लेकिन यह क्यों दिखाया गया है कि इन दोनों के बीच causation है, यह समझ नहीं आता
इस सहसंबंध के कई उचित स्पष्टीकरण हो सकते हैं। नया code अक्सर नए features से जुड़ा होता है, और लोग नए features में vulnerabilities ढूँढने पर ज़्यादा ध्यान देते हैं। साथ ही, पुराने code का वास्तविक उपयोग अधिक हो चुका हो सकता है, इसलिए memory vulnerabilities छिपी हुई boundary conditions अधिक बार execute हुई होंगी
यह कहना सहज नहीं लगता कि नया code memory vulnerabilities पैदा करता है और vulnerabilities की half-life तेज़ी से घटती है। सिर्फ संख्या के हिसाब से ऐसा हो सकता है, लेकिन अगर Heartbleed जैसी open source की high-impact vulnerabilities या CPU cache invalidation bugs को याद करें, तो impact के नज़रिए से यह सही नहीं लगता
मेरी मौजूदा company भी ऐसा ही उदाहरण है। शुरुआती code founders ने लिखा था, और कुछ नया code तंग deadlines वाले contractors लिखते हैं
अगर यह निष्कर्ष सही है कि “उदाहरण के लिए, average vulnerability lifetime के आधार पर 5 साल पुराना code नए code की तुलना में vulnerability density में 3.4 गुना कम है, और Android व Chromium में देखी गई lifetime का उपयोग करने पर 7.4 गुना कम है”, तो क्या इसका मतलब यह है कि bug-filled C code लिखकर उसे 5 साल offline रख दें तो वह सुरक्षित हो जाएगा?
इस research में महत्वपूर्ण data है, और जो साझा किया गया है उसके नीचे कुछ सच्चाई भी है, लेकिन अपर्याप्त आधार वाला confidence और अत्यधिक विस्तार बहुत खटकता है
“उत्पादकता में सुधार: safe coding bug detection को code check-in से पहले की ओर और बाएँ खिसका देती है, जिससे code correctness और developer productivity बढ़ती है। यह बदलाव rollback rate जैसे metrics में दिखता है, जैसे unexpected bugs के कारण emergency code revert करना”
“Android team ने देखा कि Rust changes की rollback rate C++ की तुलना में आधे से भी कम है”
मैंने 20 साल तक कई भाषाओं में बड़े production codebases पर काम किया है, लेकिन 2016 में जब मैंने Rust खोजा, तब मुझे पता चल गया था कि यही वह भाषा है जिसमें मैं डूब जाऊँगा। मैंने उसी दिन Klabnik और Carol की किताब भी खरीद ली थी, और वह paper copy आज भी मेरे पास है
सच कहूँ तो इसने programming के लिए मेरा प्यार फिर से जगा दिया
लेख में “memory safe languages (MSL)” बहुवचन में कहा गया है, लेकिन migration target और interoperability सुधारने वाली भाषा के रूप में स्पष्ट रूप से सिर्फ Rust का नाम है
Kotlin का भी Rust<>Kotlin interoperability सुधारने के संदर्भ में उल्लेख है, और उसमें कुछ हद तक memory safety features भी हैं, लेकिन क्या वह Rust के स्तर का है, यह पता नहीं। जिज्ञासा है कि क्या Google सिर्फ इन्हीं दो का उपयोग करता है, या वह अन्य भाषाओं की ओर भी इशारा कर रहा है
मूल कारण यही है कि कोई चीज़ default रूप से safe है या नहीं। किसी खास भाषा की सिफारिश या नाम लेने के बजाय, यह अभिव्यक्ति root cause पर ध्यान केंद्रित करने के लिए है
Android के मामले में, जो इस लेख का विषय है, मुझे इन दो के अलावा किसी और memory safe language में migration की कोशिशों की जानकारी नहीं है। मैं Android development को पूरी तरह follow नहीं करता, लेकिन ऐसे लेख काफ़ी पढ़ता हूँ, और Rust या Kotlin के अलावा कुछ याद नहीं पड़ता
असली बात यह है कि आपका अपना code memory safety bugs के संपर्क में न आए। अगर सख्त ज़रूरत न हो, तो शायद ऐसी भाषा चुनना बेहतर हो जिसमें Rust-शैली की manual memory management न हो
Google ने memory safety पर अपना दृष्टिकोण https://security.googleblog.com/2024/03/secure-by-design-goo... में भी प्रकाशित किया है, जिसमें Java, Go, Rust जैसी उपयोग में लाई जा रही memory safe languages का उल्लेख है
Google के पूरे ecosystem में Go कुछ system software के लिए उपयोग होता है, लेकिन Android में इसका उपयोग मैंने नहीं देखा
अगर vulnerability lifetime exponential distribution का पालन करती है, तो तर्क यह है कि नए code में memory safety जैसे safe defaults पर ध्यान देना सैद्धांतिक रूप से भी, और Android codebase के 6 साल के data के आधार पर भी, अनुपातहीन रूप से अधिक मूल्यवान है
यह चौंकाने वाला है। मैंने पहली बार देखा है कि इस तरह का तर्क security को shift-left करने वाले safety guardrails के समर्थन में इस्तेमाल हो रहा है, और यह शानदार है। यह खास तौर पर उन बड़े legacy codebases के लिए उपयोगी है जहाँ लोग कह सकते हैं, “C++ के 100 million lines वाले legacy code में memory safety का फायदा नहीं मिल सकता, तो इसे करें ही क्यों?”
इससे यह भी लगता है कि हल्का-फुल्का vulnerability detection भी अनुपातहीन रूप से बड़ा लाभ दे सकता है। यानी सिर्फ backlog नहीं, नए code और नई dependencies को देखने से भी ऐसा हो सकता है
यहाँ निकाला गया निष्कर्ष थोड़ा असहज लगता है। एक स्पष्ट आपत्ति, यानी यह कि कमज़ोरियाँ कम मिलती हैं क्योंकि पुराने कोड को उतनी मेहनत से देखा ही नहीं जाता, इस पर बात नहीं की गई है
20 साल से नहीं बदली हुई लाइब्रेरी को देखने की तुलना में हाल के commit logs देखना कहीं अधिक आम है
पुराने कोड में bugs कम होने की कोई theory लेख में नहीं है, लेकिन मेरी राय सीधी है: वे पहले ही मिल चुके हैं
अगर मान लें कि किसी भी कोड में हर 1000 lines पर अज्ञात bugs की एक निश्चित संख्या होती है, तो समय के साथ वह production में अलग-अलग inputs के साथ ज़्यादा बार चलता है और उनके मिलने की संभावना बढ़ती जाती है. fixes और उस प्रक्रिया के code review से औसतन कोड बेहतर होने की उम्मीद की जा सकती है
इसलिए समय के साथ मौजूदा कोड की हर 1000 lines पर bugs की संख्या घटती जाती है। यानी वह वास्तविक उपयोग में परखा हुआ है
जैसा लेख में कहा गया है, अगर नए bugs उसी रफ्तार से आते रहें तो कोई प्रगति नहीं होगी, लेकिन अगर memory-safe language नई features में कम bugs लाती है, तो समय के साथ कुल bugs की संख्या घटेगी
commits का उपयोग सिर्फ attribution के लिए होता है। अगर 20 साल से न बदली हुई पुरानी library 20 साल तक fuzzing और manual code inspection से गुज़र चुकी है, तो उसके काफ़ी मज़बूत होने की संभावना है
उदाहरण के लिए, पिछले कुछ वर्षों में engineers ने सबसे जोखिमभरे हिस्सों को memory-safe languages में फिर से लिखने पर ध्यान दिया हो, और कम जोखिम वाले पुराने कोड में कम बदलाव किए हों
या processes या staffing में बदलाव से defects बढ़े हों
फिर भी, यह व्याख्या तर्कसंगत लगती है कि हर bug के मिलने की एक प्रति-समय-इकाई संभावना होती है, और समय के साथ बचे हुए defects कम होते जाते हैं। अगर maintainers नई लाई गई कमज़ोरियों से ज़्यादा कमज़ोरियाँ ठीक कर रहे हैं, तो पुराने कोड में कमज़ोरियाँ घटेंगी और जो बचेंगी वे शायद ढूँढने में कठिन होंगी
यह तर्क Mac और Windows पर कैसे लागू होता है, यह जानने की जिज्ञासा है, क्योंकि नया Mac code ज़्यादातर memory-safe Swift में लिखा जाता है, जबकि Windows अब भी मुख्य रूप से C या C++ का उपयोग करता है
हाल के Windows versions के लिए language usage के आँकड़े मुझे नहीं मिले, लेकिन Microsoft नई development और मौजूदा features के rewrite—दोनों में Rust का उपयोग कर रहा है [1] [2]
[0] “Evolution of the programming languages” section देखें https://blog.timac.org/2023/1128-state-of-appkit-catalyst-sw...
[1] https://www.theregister.com/2023/04/27/microsoft_windows_rus...
[2] https://www.theregister.com/2024/01/31/microsoft_seeks_rust_...
यहाँ अगर endgame के बारे में सोचें, तो जैसे-जैसे कमज़ोरियाँ दुर्लभ होंगी, वे उतनी ही मूल्यवान बनेंगी। बची हुई कमज़ोरियों को nation-state actors बहुत सावधानी से stockpile करेंगे और उन्हें सिर्फ high-value targets पर इस्तेमाल करेंगे
अगर यह ब्लॉग पोस्ट चौथी पीढ़ी का वर्णन करती है, तो पाँचवीं पीढ़ी iOS के Lockdown Mode जैसी हो सकती है। यानी security को लेकर चिंतित users performance में गिरावट स्वीकार करके सुरक्षा बढ़ाने वाला checkbox ऑन कर सकें
आदर्श checkbox virtualization जैसी किसी तकनीक से हमले का पता लगाए और उसे capture करके security team के analysis के लिए भेजे। इससे attackers के लिए deterrence पैदा होगा। वे अपनी दुर्लभ कमज़ोरी यूँ ही खर्च नहीं करना चाहेंगे, जब यह संभव हो कि user ने वह security checkbox ऑन किया हो, और कई high-value targets उसे ऑन करेंगे
यह जैविक pathogens के बजाय software vulnerabilities के लिए herd immunity है
security-conscious users के privacy-conscious होने की भी संभावना ज़्यादा है। इसलिए सभी user activities को चुपचाप भेजने के बजाय, हमला detect होने पर user को alert दिखाना चाहिए। कुछ KB की असामान्य network activity security team के लिए हमले को reconstruct करने के लिए काफ़ी हो सकती है, और share करने से पहले user की approval ली जा सकती है