I. Lời mở đầu
Của tôi như thế nào mình có đề cập đến lỗi XSS mà trang web của mình đã bị hacker tấn công. Cụ thể là mình đã bị hacker tấn công bởi lỗi Stored XSS. Mình xin giải thích cụ thể hơn về lỗi XSS này.
Stored XSS: là dạng tấn công mà hacker chèn trực tiếp các mã độc vào cơ sở dữ liệu của website. Dạng tấn công này xảy ra khi các dữ liệu được gửi lên server không được kiểm tra kỹ lưỡng mà lưu trực tiếp vào cơ sở dữ liệu. Khi người dùng truy cập vào trang web này thì những đoạn script độc hại sẽ được thực thi chung với quá trình load trang web.
Trong PHP có một hàm là strip_tags
, hàm này có tác dụng loại bỏ đi các ký tự html trong một string.
Ví dụ bạn có một đoạn string có chứa các thẻ html, bây giờ bạn cần loại bỏ nó khỏi string này. Bạn có thể làm như sau:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token variable">$text</span> <span class="token operator">=</span> <span class="token single-quoted-string string">'<p>Test paragraph.</p><!-- Comment --> <a href="#fragment">Other text</a>'</span><span class="token punctuation">;</span> <span class="token keyword">echo</span> <span class="token function">strip_tags</span><span class="token punctuation">(</span><span class="token variable">$text</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">echo</span> <span class="token double-quoted-string string">"n"</span><span class="token punctuation">;</span> <span class="token comment">// Allow <p> and <a></span> <span class="token keyword">echo</span> <span class="token function">strip_tags</span><span class="token punctuation">(</span><span class="token variable">$text</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'<p><a>'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// as of PHP 7.4.0 the line above can be written as:</span> <span class="token comment">// echo strip_tags($text, ['p', 'a']);</span> <span class="token delimiter important">?></span></span> |
Và ta thu được kết quả như mong muốn:
1 2 | Test paragraph. Other text |
<p>Test paragraph.</p> <a href="#fragment">Other text</a>
Mặc dù strip_tags
có thể loại bỏ các ký tự html cho data của chúng ta tuy nhiên nó chỉ xóa một số thẻ nhất định. Mỗi lần gọi hàm bạn đều phải điền rõ thẻ mà bạn muốn xóa. Chính vì vậy giải pháp ở đây là dùng HTML Purifier.
II. Nội dung chính
Cài đặt package HTML Purifier
HTML Purifier đã có sẵn trong packagist.org.
Nếu bạn đang sử dụng composer để quản lý các dependencies. Để thực hiện cài đặt package bạn thực hiện theo command sau:
1 2 | $ composer require ezyang/htmlpurifier |
Sau khi cài đặt thành công package sẽ được add trong composer.json.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token property">"require"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"php"</span><span class="token operator">:</span> <span class="token string">"^7.2"</span><span class="token punctuation">,</span> <span class="token property">"doctrine/dbal"</span><span class="token operator">:</span> <span class="token string">"^2.10"</span><span class="token punctuation">,</span> <span class="token property">"ezyang/htmlpurifier"</span><span class="token operator">:</span> <span class="token string">"^4.12"</span><span class="token punctuation">,</span> <span class="token property">"facade/ignition"</span><span class="token operator">:</span> <span class="token string">"^1.4"</span><span class="token punctuation">,</span> <span class="token property">"fideloper/proxy"</span><span class="token operator">:</span> <span class="token string">"^4.0"</span><span class="token punctuation">,</span> <span class="token property">"intervention/image"</span><span class="token operator">:</span> <span class="token string">"^2.5"</span><span class="token punctuation">,</span> <span class="token property">"laravel/framework"</span><span class="token operator">:</span> <span class="token string">"^6.8"</span><span class="token punctuation">,</span> <span class="token property">"laravel/tinker"</span><span class="token operator">:</span> <span class="token string">"^1.0"</span><span class="token punctuation">,</span> <span class="token property">"nunomaduro/collision"</span><span class="token operator">:</span> <span class="token string">"^3.0"</span><span class="token punctuation">,</span> <span class="token property">"predis/predis"</span><span class="token operator">:</span> <span class="token string">"^1.1"</span><span class="token punctuation">,</span> <span class="token property">"rap2hpoutre/laravel-log-viewer"</span><span class="token operator">:</span> <span class="token string">"^1.3"</span><span class="token punctuation">,</span> <span class="token property">"sentry/sentry-laravel"</span><span class="token operator">:</span> <span class="token string">"^1.5"</span><span class="token punctuation">,</span> <span class="token property">"spatie/laravel-analytics"</span><span class="token operator">:</span> <span class="token string">"^3.9"</span><span class="token punctuation">,</span> <span class="token property">"sun-asterisk/laravel-chatwork"</span><span class="token operator">:</span> <span class="token string">"^0.2.0"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> |
Sử dụng package HTML Purifier
Tạo File AppSupportHTMLPurifier.php để setup config cho HTMLPurifier
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">AppSupport</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">HTMLPurifier</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token variable">$purifier</span><span class="token punctuation">;</span> <span class="token comment">/** * @param $value * @return mixed */</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">clean</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> self<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">getPurifier</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">purify</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * @return HTMLPurifier */</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">getPurifier</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_null</span><span class="token punctuation">(</span>self<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token variable">$purifier</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//Find full HTML5 config : https://github.com/kennberg/php-htmlpurfier-html5</span> <span class="token variable">$config</span> <span class="token operator">=</span> <span class="token package">HTMLPurifier_Config</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">createDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$config</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'HTML.Doctype'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'HTML 4.01 Transitional'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$config</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'HTML.SafeIframe'</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$config</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Cache.SerializerPath'</span><span class="token punctuation">,</span> <span class="token function">storage_path</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">'/app/purifier'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$config</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Cache.SerializerPermissions'</span><span class="token punctuation">,</span> <span class="token number">777</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$def</span> <span class="token operator">=</span> <span class="token variable">$config</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">getHTMLDefinition</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$def</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">addElement</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'figure'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'Block'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'Flow'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'Common'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token single-quoted-string string">'class'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$def</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">addElement</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'oembed'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'Block'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'Flow'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'Common'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token single-quoted-string string">'url'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$def</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">addAttribute</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'oembed'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'url'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'Text'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> self<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token variable">$purifier</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HTMLPurifier</span><span class="token punctuation">(</span><span class="token variable">$config</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> self<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token variable">$purifier</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
Với HTMLPurifier bạn không cần phải điền list thẻ html nữa, thay vào đó là ta whitelist các attribute thôi ví dụ như class, url, … rất là tiện đúng không nào.
Và tại phần store data mà chúng ta muốn sử dụng chỉ cần gọi nó ra
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">use</span> <span class="token package">AppModelsComment</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">AppSupportHTMLPurifier</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">IlluminateHttpRequest</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">AppHttpControllersController</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">CommentController</span> extend <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">store</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$data</span> <span class="token operator">=</span> HTMLPurifier<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">clean</span><span class="token punctuation">(</span><span class="token variable">$request</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">data</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$comment</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">create</span><span class="token punctuation">(</span><span class="token variable">$data</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> response<span class="token operator">-</span><span class="token operator">></span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'message'</span> <span class="token operator">=</span><span class="token operator">></span> 'Save a <span class="token keyword">new</span> <span class="token class-name">comment</span> successfully<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
Với HTMLPurifier các thẻ html không cần thiết đã bị xóa đi và việc tấn công XSS phần nào được ngăn chặn.
Demo
III. Tạm kết
HTML Purifier là một packge hiệu quả cho các giải pháp phòng tránh XSS. Hãy bảo vệ trang web của bạn đươc tốt nhất, mang đến trải nghiệm người dùng hiệu quả nhất. Đây chỉ là 1 trong những package trong vô vàn giải pháp ngăn ngừa và phòng tránh XSS. Rất mong được sự góp ý từ mọi người.