"चेतावनी: HTML ने कुछ सामग्री छीन ली" और इससे सही तरीके से कैसे निपटा जाए

उन सभी लोगों को जिन्हें DOM में कोणीय में HTML सामग्री को एम्बेड करना पड़ा है, उन्होंने यह संदेश देखा है। बेशक, हम सभी अपने स्वयं के सर्वर से जाँच की गई सामग्री प्राप्त करते हैं और बस त्रुटि संदेश को कवर करना चाहते हैं। या हम अपने स्थिरांक से एचटीएमएल को एम्बेड करते हैं, हमारे एसवीजी आइकनों को इनलाइन करते हैं, क्योंकि हमें केवल उन्हें पाठ के रंग में रंगने की आवश्यकता है। सब के बाद, कुछ भी बुरा नहीं होगा अगर हम सिर्फ कोणीय को कहते हैं - एक बहाव नहीं, तो वहां सब कुछ साफ है।


अक्सर ऐसा हो सकता है, लेकिन स्वतंत्र प्रोजेक्ट लिखने वाले डेवलपर्स के एक बड़े प्रोजेक्ट के साथ, आप कभी भी यह नहीं जानते कि आपका कोड कहां हो सकता है। और अगर आप, मेरी तरह, पुन: प्रयोज्य घटकों का एक पुस्तकालय विकसित कर रहे हैं, तो इस स्थिति को कली में हल करने की आवश्यकता है।



Sanitize और DomSanitizer


कोणीय में सार कक्षाएं हैं जिनके कार्यान्वयन को दुर्भावनापूर्ण कबाड़ की सामग्री को साफ करने के लिए डिज़ाइन किया गया है:


abstract class Sanitizer { abstract sanitize(context: SecurityContext, value: string | {}): string | null } 

 abstract class DomSanitizer implements Sanitizer { abstract sanitize(context: SecurityContext, value: string | SafeValue): string | null abstract bypassSecurityTrustHtml(value: string): SafeHtml abstract bypassSecurityTrustStyle(value: string): SafeStyle abstract bypassSecurityTrustScript(value: string): SafeScript abstract bypassSecurityTrustUrl(value: string): SafeUrl abstract bypassSecurityTrustResourceUrl(value: string): SafeResourceUrl } 

DOM के निर्माण में विशिष्ट आंतरिक वर्ग DomSanitizerImpl Angular का उपयोग किया जाता है। यह उनके लोग हैं जो रूपरेखा को बताने के लिए निर्देशों, घटकों और पाइपों को जोड़ते हैं कि इस सामग्री पर भरोसा किया जा सकता है।


इस वर्ग के लिए स्रोत कोड बहुत सरल है:
https://github.com/angular/angular/blob/8.1.0/packages/platform-browser/src/security/dom_sanitization_service.ts#L148


यह सेवा SafeValue की अवधारणा का परिचय SafeValue । संक्षेप में, यह एक स्ट्रिंग के लिए सिर्फ एक आवरण वर्ग है। जब Renderer बाइंडिंग के माध्यम से एक मान सम्मिलित करता है, तो यह @HostBinding , @HostBinding , style या src , मान को sanitize विधि के माध्यम से चलाया जाता है। यदि SafeValue पहले से ही वहां मौजूद है, तो यह केवल लिपटे स्ट्रिंग को वापस करता है, अन्यथा यह सामग्री को अपने आप साफ करता है।


कोणीय एक विशेष मैलवेयर कोड सफाई पुस्तकालय नहीं है। इसलिए, रूपरेखा काफी सही तरीके से कम से कम जोखिम के मार्ग का अनुसरण करती है और चिंता का कारण बनती है। एसवीजी कोड खाली लाइनों में बदल जाता है, इनलाइन शैलियों को हटा दिया जाता है, आदि। हालाँकि, केवल DOM को सुरक्षित करने के लिए डिज़ाइन किए गए पुस्तकालय हैं, जिनमें से एक DOMPurify है:
https://github.com/cure53/DOMPurify



सही SafeHtml पाइप


DOMPurify को जोड़ने के बाद, हम एक पाइप बना सकते हैं जो न केवल सामग्री को सुरक्षित रूप से चिह्नित करता है, बल्कि इसे साफ भी करता है। ऐसा करने के लिए, DOMPurify.sanitize विधि के माध्यम से इनपुट मान DOMPurify.sanitize , और फिर इसे उपयुक्त संदर्भ के साथ सुरक्षित चिह्नित करें:


 @Pipe({name: 'dompurify'}) export class NgDompurifyPipe implements PipeTransform { constructor(private readonly domSanitizer: DomSanitizer) {} transform( value: {} | string | null, context: SecurityContext = SecurityContext.HTML, ): SafeValue | null { return this.bypassSecurityTrust(context, DOMPurify.sanitize(value)); } private bypassSecurityTrust( context: SecurityContext, purifiedValue: string, ): SafeValue | null { switch (context) { case SecurityContext.HTML: return this.domSanitizer.bypassSecurityTrustHtml(purifiedValue); case SecurityContext.STYLE: return this.domSanitizer.bypassSecurityTrustStyle(purifiedValue); case SecurityContext.SCRIPT: return this.domSanitizer.bypassSecurityTrustScript(purifiedValue); case SecurityContext.URL: return this.domSanitizer.bypassSecurityTrustUrl(purifiedValue); case SecurityContext.RESOURCE_URL: return this.domSanitizer.bypassSecurityTrustResourceUrl(purifiedValue); default: return null; } } } 

HTML में पेज को डालने पर यह सब सुरक्षित है। DOMPurify और micropipe से + 7KB गज़िप। हालांकि, जब से हम यहां चढ़े, हम आगे बढ़ने की कोशिश करेंगे। कक्षाओं की सारगर्भितता का मतलब है कि कोणीय आपकी खुद की कार्यान्वयन बनाने की संभावना का सुझाव देता है। यदि आप पाइप बनाते हैं और सामग्री को सुरक्षित रखते हैं तो क्या आप DOMSuritizer के रूप में तुरंत DOMPurify का उपयोग कर सकते हैं?



DomPurifyDomSanitizer


एक वर्ग बनाएं जो DomSanitizer से विरासत में DomSanitizer और DomSanitizer के लिए मूल्यों के समाशोधन को दर्शाता है। भविष्य के लिए, हम तुरंत Sanitizer सेवा को लागू करेंगे और हम इसे पाइप में और DomSanitizer दोनों में उपयोग करेंगे। यह भविष्य में हमारी मदद करेगा, क्योंकि DOMPurify का एकल प्रवेश बिंदु दिखाई देगा। SafeValue का कार्यान्वयन और इस अवधारणा से संबंधित सब कुछ SafeValue का निजी कोड है, इसलिए हमें इसे स्वयं लिखना होगा। हालाँकि, जैसा कि हमने सोर्स कोड में देखा, यह मुश्किल नहीं है।


यह ध्यान देने योग्य है कि HTML के अलावा, अन्य SecurityContext और विशेष रूप से, शैलियों की सफाई के लिए DOMPurify जैसे कि यह उपयुक्त नहीं है। हालांकि, हुक के उपयोग से इसके कार्यों का विस्तार किया जाता है, बाद में और अधिक। इसके अलावा, एक कॉन्फ़िगरेशन ऑब्जेक्ट DOMPurify को पारित किया जा सकता है, और निर्भरता इंजेक्शन इसके लिए एकदम सही है। हमारे कोड में एक टोकन जोड़ें जो हमें DOMPurify के लिए पैरामीटर प्रदान करने की अनुमति देता है:


 @NgModule({ // ... providers: [ { provide: DOMPURIFY_CONFIG, useValue: {FORBID_ATTR: ['id']}, }, ], // ... }) export class AppModule {} 

 @Injectable({ providedIn: 'root', }) export class NgDompurifySanitizer extends Sanitizer { constructor( @Inject(DOMPURIFY_CONFIG) private readonly config: NgDompurifyConfig, ) { super(); } sanitize( _context: SecurityContext, value: {} | string | null, config: NgDompurifyConfig = this.config, ): string { return sanitize(String(value || ''), config); } } 

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


 export const SANITIZE_STYLE = new InjectionToken<SanitizeStyle>( 'A function that sanitizes value for a CSS rule', { factory: () => () => '', providedIn: 'root', }, ); 

  sanitize( context: SecurityContext, value: {} | string | null, config: NgDompurifyConfig = this.config, ): string { return context === SecurityContext.STYLE ? this.sanitizeStyle(String(value)) : sanitize(String(value || ''), config); } 

इस तरह हम उन शैलियों को साफ़ कर देंगे जो सीधे [style.*] या @HostBinding('style.*') माध्यम से बाइंडिंग में शामिल हैं। जैसा कि हम याद करते हैं, बिल्ट-इन कोणीय सैनिटाइज़र का व्यवहार सभी इनलाइन शैलियों को दूर करना है। ईमानदारी से, मुझे नहीं पता कि उन्होंने ऐसा करने का फैसला क्यों किया, क्योंकि उन्होंने संदिग्ध सीएसएस नियमों को हटाने के लिए तरीके लिखे थे, लेकिन अगर आपको एक WYSIWYG संपादक को लागू करने, कहने की आवश्यकता है, तो आप इनलाइन शैलियों के बिना नहीं कर सकते। सौभाग्य से, DOMPurify आपको पुस्तकालय की क्षमताओं का विस्तार करने के लिए हुक जोड़ने की अनुमति देता है। उदाहरण अनुभाग में भी कोड है जो DOM तत्वों और संपूर्ण HTMLStyleElement टैग्स पर शैलियों को साफ़ करता है।


hooky


DOMPurify.addHook() का उपयोग करके DOMPurify.addHook() आप उन तरीकों को पंजीकृत कर सकते हैं जो सफाई के एक अलग चरण में वर्तमान तत्व प्राप्त करते हैं। उन्हें जोड़ने के लिए, हम तीसरे और अंतिम टोकन को जोड़ेंगे। हमारे पास पहले से ही शैलियों की सफाई के लिए एक विधि है, आपको बस इसे सभी इनलाइन नियमों के लिए कॉल करने की आवश्यकता है। ऐसा करने के लिए, हमारी सेवा के निर्माता में आवश्यक हुक पंजीकृत करें। हम सभी हुक पर चले जाएंगे जो DI का उपयोग करके हमारे पास आए थे:


  constructor( @Inject(DOMPURIFY_CONFIG) private readonly config: NgDompurifyConfig, @Inject(SANITIZE_STYLE) private readonly sanitizeStyle: SanitizeStyle, @Inject(DOMPURIFY_HOOKS) hooks: ReadonlyArray<NgDompurifyHook>, ) { super(); addHook('afterSanitizeAttributes', createAfterSanitizeAttributes(this.sanitizeStyle)); hooks.forEach(({name, hook}) => { addHook(name, hook); }); } 

हमारी सेवा तैयार है और यह केवल आपके DomSanitizer को लिखने के लिए बनी हुई है जो केवल सफाई के लिए सामग्री को इसमें स्थानांतरित करेगी। आपको bypassSecurityTrust*** विधियों को लागू करने की भी आवश्यकता है, लेकिन हमने पहले ही उन्हें कोणीय स्रोत कोड में जासूसी की है। अब DOMPurify पाइप या सेवा का उपयोग करके और स्वचालित रूप से पूरे आवेदन में दोनों बिंदुवार उपलब्ध है।


निष्कर्ष


हमें पता चला DomSanitizer कोणीय में DomSanitizer है और अब मनमाना एचटीएमएल डालने की समस्या का DomSanitizer है। अब आप सर्वर से SVG और HTML संदेशों को सुरक्षित रूप से इनलाइन कर सकते हैं। सैनिटाइज़र को ठीक से काम करने के लिए, आपको अभी भी उसे शैलियों की सफाई के लिए एक विधि प्रदान करनी होगी। दुर्भावनापूर्ण सीएसएस अन्य प्रकार के हमलों की तुलना में बहुत कम आम है, सीएसएस अभिव्यक्तियों का समय बहुत लंबा हो गया है और सीएसएस के साथ आप जो करते हैं वह आपके ऊपर है। आप अपने आप को एक हैंडलर लिख सकते हैं, आप उन सभी चीजों को थूक सकते हैं और छोड़ सकते हैं जिनके पास ब्रैकेट नहीं हैं, निश्चित रूप से। या आप थोड़ा धोखा दे सकते हैं और कोणीय के स्रोतों पर करीब से नज़र डाल सकते हैं। _sanitizeStyle विधि @angular/core पैकेज में है, जबकि DomSanitizerImpl @angular/platform-browser पैकेज में है, और यद्यपि यह विधि अपने पैकेज में निजी है, लेकिन कोणीय टीम अपने निजी नाम ɵ_sanitizeStyle द्वारा इसे एक्सेस करने में संकोच नहीं ɵ_sanitizeStyle । हम ऐसा ही कर सकते हैं और इस पद्धति को अपने सैनिटाइज़र में स्थानांतरित कर सकते हैं। इस प्रकार, हमें डिफ़ॉल्ट के रूप में सफाई शैलियों का समान स्तर मिलता है, लेकिन एचटीएमएल कोड डालने पर इनलाइन शैलियों का उपयोग करने की क्षमता के साथ। इस निजी आयात का नाम तब से बदल नहीं गया है जब यह 6 वें संस्करण में दिखाई दिया था, लेकिन आपको ऐसी चीजों से सावधान रहने की आवश्यकता है, कुछ भी किसी भी समय कोणीय टीम का नाम बदलने, स्थानांतरित करने या हटाने से रोकता है।


लेख में वर्णित कोड जीथब पर है
इसके अलावा npm tinkoff / ng-dompurify पैकेज के रूप में उपलब्ध है:
https://www.npmjs.com/package/@tinkoff/ng-dompurify
आप स्टैकब्लिट्ज़ के साथ एक डेमो के साथ थोड़ा खेल सकते हैं

Source: https://habr.com/ru/post/hi459396/


All Articles