Tải bản đầy đủ (.docx) (79 trang)

bảo mật web server apache với mod security

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.38 MB, 79 trang )

Bảo mật web server Apache với mod Security
PHẦN 1: CƠ BẢN VỀ GIAO THỨC HTTP
Phần này chỉ giới thiệu sơ lược các kiến thức cơ bản về giao thức HTTP, tạo tiền đề
để phần 2 trình bày về MODSECURITY. Vì vậy, nếu bạn nào cần tìm hiểu sâu hơn,
hãy tham khảo RFC hoặc cuốn HTTP Essentials
11. Giới thiệu chung
1.1.1. Lớp của giao thức HTTP
HTTP (Hypertext Transfer Protocol) là giao thức thuộc lớp ứng dụng trong mô hình
tham chiếu OSI. Hoạt động thông thường ở port 80 và là giao thức hướng kết nối.
Nói cách khác, trước khi thực hiện phiên làm việc, giao thức HTTP sẽ thực hiện bắt
tay ba bước.
Hình 1.1 Lớp của giao thức HTTP
1.1.2. URI – Uniform Resource Identifiers
Thông thường, chúng ta thường quen thuộc với định nghĩa URL (Uniform Resource
Locators) Địa chỉ là một ví dụ về URL. Trên thực tế,
không có nhiều khác biệt giữa hai khái niệm URL và URI, URL một chỉ là một loại
của URI.
URI là một đặc điểm kỹ thuật của giao thức HTTP. Như hình dưới cho thấy một URI
chứa rất nhiều các thành phần, không đơn giản như URL.
Hình 1.2 Cấu trúc đầy đủ của URI
- Protocol: Xác định các giao thức và các ứng dụng cần thiết để truy cập tài nguyên,
trong trường hợp này là giao thức HTTP
- Username: Nếu giao thức hỗ trợ khái niệm về tên người dùng thì username cung
cấp tên người dùng để chứng thực truy cập tài nguyên
- Password: Mật khẩu truy cập tài nguyên
- Host: Tên miền truyền thông cho webserver,
- Port: Là port cho các giao thức lớp ứng dụng, ví dụ như HTTP là cổng 80 (có thể
bỏ qua tham số này).
- Path: đường dẫn phân cấp đến tài nguyên được đặt trên Server
- File: Tên các tập tin tài nguyên trên Server
- Query: Các tuy vấn thêm thông tin về tài nguyên của Client


- Fragment: Một vị trí nào đó trong tài nguyên
1.2. Hoạt động HTTP
1.2.1. Kết nối
HTTP thuộc giao thức TCP, vì vậy trước khi tiến hành truyền thông phải thực hiện
phiên kết nối TCP.
Trước đây, mỗi kết nối TCP sẽ thực hiện chỉ 1 request HTTP và 1 response, nghĩa là
nếu client request 2 lần thì sẽ cần 2 phiên kết nối TCP. Điều này gây ra nhiều bất cập,
ảnh hưởng rất nhiều đến hiệu năng hoạt động của server.
Hình 1.3 Hoạt động của HTTP
Hiện nay, giao thức HTTP đã hỗ trợ một phiên kết nối TCP cho phép nhiều HTTP
request
Hình 1.4 Hoạt động kết nối của HTTP
1.2.2. Pipelining
Pipelining cũng nhằm mục đích cải thiện hiệu năng của HTTP. Client không cần phải
chờ Server response mới có thể request và ngược lại.
Hình 1.5 Hoạt động Pipelining của HTTP
1.2.3. Web Page Retrieval – GET
Hoạt động HTTP đơn giản nhất là GET. Đó là cách để Client lấy một đối tượng hoặc
tài nguyên nào đó trên Server. Các trình duyệt sẽ yêu cầu một website từ Server với
GET.
Hình 1.6 Hoạt động GET của HTTP
Với ví dụ trên, Client khởi tạo và gởi thông điệp GET đến Server, thông điệp này
định danh đối tượng mà Client yêu cầu Server đáp ứng bằng một URI (Uniform
Resource Identifier). Server có thể trả về tài nguyên mà Client yêu cầu với một mã
trạng thái 200 OK. Nếu Server không đáp ứng được yêu cầu Client thì nó sẽ gởi về
một số mã trạng thái khác được mô tả ở bảng bên dưới.
Bảng 1.1 Mã trạng thái được trả về của HTTP
1.2.4. Web Forms – POST
Trong khi GET cho phép một Server gửi thông tin đến Client, thì hoạt động POST
cung cấp một cách để Client gửi thông tin đến các Server. Trình duyệt sử dụng POST

để gởi nội dung các Form đến Web Server. Hình dưới cho thấy một ví dụ như vậy.
Hình 1.7 Ví dụ về Web Forms POST
Hình 1.8 Hoạt động POST của HTTP
Như hình trên cho thấy, hoạt động POST đơn giản như GET. Client gửi một thông
điệp POST và bao gồm thông tin mà nó muốn gửi đến server. Cũng giống như GET,
một phần của thông điệp POST là URI. Nhưng trong trường hợp này, URI xác định
các đối tượng trên Server có thể xử lý thông tin.
Cũng như GET, Server có thể trả về thông tin cho client. Đối với trình duyệt web,
thông tin thường là một trang web mới để hiển thị.
1.2.5. File Upload – PUT
Các hoạt động PUT cũng cung cấp một cách để client gửi thông tin đến các Server.
Hay nói cách khác, PUT dùng để upload dữ liệu lên server.
Như hình dưới cho thấy, hai hoạt động nhìn rất giống nhau. Với POST, Client gửi
bao gồm một URI và dữ liệu. Web Server về mã trạng thái, tuỳ chọn kèm theo và dữ
liệu. Sự khác biệt giữa POST và PUT ở chỗ URI : Với POST, các URI xác định một
đối tượng trên Server mà có thể xử lý dữ liệu. Với một PUT, các URI xác định đối
tượng trong đó các Server nên đặt dữ liệu (ví dụ đường dẫn cho Server đặt dữ liệu).
Trong khi một POST URI thường chỉ ra một chương trình, script thì PUT uri thường
là đường dẫn và tên cho tập tin.
Hình 1.9 Hoạt động PUT của HTTP
Hình 1.10 Ví dụ về File Upload - PUT
1.2.6. File Deletion – DELETE
Với GET và PUT, giao thức HTTP trở thành một giao thức chuyển file đơn giản.
Hoạt động DELETE sẽ hoàn thành chức năng này bằng cách giúp client xoá các đối
tượng, tài nguyên từ các server.
Như hình dưới cho thấy, client gửi một thông điệp DELETE cùng với các URI của
đối tượng mà server nên xoá. Các server đáp ứng với một mã trạng thái và dữ liệu
kèm theo.
Hình 1.11 Hoạt động File Delection - DELETE
1.2.7. Trạng thái - HEAD

Các hoạt động của HEAD giống như GET, ngoại trừ Server không trả lại đối tượng
thực tế yêu cầu. Cụ thể, server sẽ trả về một mã trạng thái nhưng không có dữ liệu.
(HEAD có nghĩa là "tiêu đề," nghĩa là server chỉ trả về thông điệp chứa tiêu đề chứ
không chứa dữ liệu) .
Client có thể sử dụng thông điệp HEAD khi muốn xác minh rằng một đối tượng có
tồn tại hay không,.
Ví dụ: Có thể sử dụng thông điệp HEAD để đảm bảo liên kết đến một đối tượng hợp
lệ mà không tiêu tốn băng thông.
Cache trong trình duyệt cũng có thể sử dụng thông điệp HEAD để xem một đối tượng
đã thay đổi hay không, nếu không thay đổi thì hiển thị thông tin đã được lưu trước
đây, nếu thay đổi thì sẽ thực hiện GET để lấy dữ liệu về từ Server
1.3. Thông điệp HTTP
Các phần trước đã trình bày hoạt động của HTTP, và chúng ta đã xem xét từng thông
điệp cụ thể. Không giống như các giao thức truyền thông khác, các thông điệp HTTP
chủ yếu là các văn bản tiếng Anh. Thay vì lo lắng về các bit và byte, trong phần này
chúng ta xem xét những từ mà HTTP định dạng.
Phần này sẽ trình bày cấu trúc tổng thể của thông điệp HTTP. Chúng ta sẽ thấy, một
thông điệp HTTP bắt đầu với một “line” hay một mã trạng thái, có thể được theo sau
bởi các tiêu đề (header) khác nhau và phần thân (body) của thông điệp.
1.3.1. Cấu trúc của thông điệp HTTP
HTTP có hai tác nhân là client và server. Các client gởi yêu cầu (request) và server
trả lời (response). Vì vậy, chúng ta sẽ phân tích hai thông điệp chính là HTTP
Requests và HTTP Responses.
a. HTTP Request
Hình 1.12 Cấu trúc thông điệp HTTP Request
Hình trên cho thấy cấu trúc cơ bản của HTTP Requests. Một HTTP Requests. bắt đầu
bởi Request-Line. Request-Line có thể được theo sau bởi một hoặc nhiều header và
body.
Để cụ thể hơn, hình bên dưới cho thấy một thông điệp http (dưới dạng văn bản) do
Internet Explorer của Microsoft gửi khi người dùng truy cập vào trang www.ft.com.

Dòng đầu tiên là Request-Line, và tiêu đề thông điệp tạo nên phần còn lại của văn
bản.
Hình 1.13 Một ví dụ về nội dung thông điệp HTTP
Hình dưới phân tích cụ thể hơn Request-Line, bao gồm 3 phần: Method – phương
thức của thông điệp, URI, và Version- phiên bản của HTTP
Hình 1.14 Ví dụ cụ thể về Request-Line
Phương thức (method) cụ thể xuất hiện đầu tiên trong Request-Line. Trong ví dụ trên
đây là một phương thức GET
Mục tiếp theo trong Request-Line là Request-URI. Request-URI chứa nguồn tài
nguyên cần truy cập. Trong ví dụ trên, Request-uri là (/), chỉ ra một yêu cầu đối với
các nguồn tài nguyên gốc. Phần cuối cùng của Request-Line là phiên bản HTTP. Như
ví dụ trên cho thấy, HTTP phiên bản 1.1.
b. HTTP Response:
Request Resonse bắt đầu bởi Status-Line (dòng mã trạng thái). Sau đó là phần thông
tin của Header và một dòng trắng. Cuối cùng là phần body.
Hình 1.15 Cấu trúc thông điệp HTTP Response
Status-Line bắt đầu bởi số phiên bản của HTTP (trường hợp này là HTTP/1.1), sau
đó là mã trạng thái(trường hợp này là 200 OK)
Hình 1.16 Cụ thể trường Status-Line
1.3.2. Các trường trong HTTP header
Hình 1.17 Ví dụ về HTTP header
Như chúng ta đã thấy ở các phần trước, HTTP Request và HTTP Response có thể bao
gồm một hoặc nhiều thông điệp header (message header). Message header bắt đầu
với một tên trường và dấu hai chấm ( .
Như ví dụ trên, các Message header là Accept: , Accept-Language…
Trong HTTP header có rất nhiều trường đảm nhận các tính năng, mục đích khác
nhau. Bài viết này chỉ mang tính giới thiệu nên sẽ không trình bày. Các bạn có thể
tham khảo ở
PHẦN 2: MODSECURITY
2.1. GIỚI THIỆU MODSECURITY

ModSecurity là một Opensource web application firewall được Ivan Ristic phát triển
dành cho Web Server Apache. Ivan Ristic cũng là tác giả quyển sách “Mod Security
Handbook”. Ông là một người có rất nhiều kinh nghiệm trong bảo vệ Web Server
Apache. Ông đã có nhiều thời gian nghiên cứu Web Application Security, Web
Intrusion Detection, và Security Patterns. Trước khi chuyển sang lĩnh vực security,
Ivan đã có nhiều năm làm việc như một Developer, System Architect, Technical
Director trong phát triển phần mềm. Ông là người sáng lập ra công ty ThinkingStone
làm các dịch vụ liên quan đến web application security.
Hiện tại ModSecurity sử dụng giấy phép GPL, hoàn toàn miễn phí
2.2. CÁC KHẢ NĂNG CỦA MODSECURITY
Hình 2.1 Mô hình tổng quan của ModSecurity
- Request filtering: Tất cả các request gửi đến web server đều được phân tích và cản
lọc (filter) trước khi chúng được đưa đến các modules khác để xử lý
- Understanding of the HTTP protocol: ModSecurity là một tường lửa ứng dụng
nên nó có khả năng hiểu được giao thức HTTP. ModSecurity có khả năng cản lọc
dựa trên các thông tin ở HTTP Header hay có thể xem xét đến từng thông số hay
cookies của các request vv
- POST payload analysis: Ngoài việc cản lọc dựa trên HTTP Header, ModSecurity
có thể dựa trên nội dung (payload) của POST requests.
- Audit logging: Mọi requests đều có thể được ghi lại (bao gồm cả POST) để người
quản trị có thể theo dõi nếu cần.
- HTTPS filtering: ModSecurity có thể phân tích HTTPS.
- Compressed content filtering: ModSecurity sẽ phân tích sau khi đã giải nén các các
dữ liệu được yêu cầu.
Hình 2.2 Quá trình xử lý các request của Apache và ModSecurity
Modsecurity cho phép chúng ta đặt rule tại một trong năm thời điểm trong chu kỳ xử
lý của Apache như sau:
1. Phase Request Header: Rule được đặt tại đây sẽ được thực hiện ngay say khi
Apache đọc request header, lúc này phần request body vẫn chưa được đọc.
2. Phase Request Body: Đây là thời điểm các thông tin chức năng chung đưa vào

vào được phân tích và xem xét, các rule mang tính ứng dụng hướng kết nối
(application-oriented) thường được đặt ở đây. Ở thời điểm này, Server đã nhận đủ các
thông số của request và phần request body đã được đọc. Modsecurity hỗ trợ ba loại
mã hoá request body
+ application/x-www-form-urlencoded: Dùng để truyền form dữ liệu
+ multipart/form-data: Dùng để truyền file
+ text/xml: Dùng để phân tích dữ liệu XML
3. Phase Response Header: Đây là thời điểm ngay sau khi phần response header
được gửi trả về cho client. Chúng ta đặt rule ở đây nếu muốn giám sát quá trình sau
khi phần response được gửi đi.
4. Phase Response Body: Đây là thời điểm chúng ta muốn kiểm tra những dữ liệu
HTML gửi trả về.
5. Phase logging: Là thời điểm các hoạt động log được thực hiện, các rules đặt ở đây
sẽ định rõ việc log sẽ như thế nào, nó sẽ kiểm tra các error message log của Apache.
Đây cũng là thời điểm cuối cùng để chúng ta chặn các kết nối không mong muốn,
kiểm tra các response header mà chúng ta không thể kiểm tra ở phase 3 và phase 4.
2.3. CÀI ĐẶT VÀ CẤU HÌNH
(phần cài đặt được tham khảo từ một bài viết của forum ceh.vn, có một số chỉnh sửa
cho phù hợp )
Trước khi cài đặt, chúng ta phải đảm bảo web server Apache đã hoạt động tốt. Distro
Linux sử dụng là CentOS5 và phiên bản ModSecurity sử dụng là 2.5. Có thể thực
hiện trên các Distro khác như Ubuntu, Fedora
- Thực hiện tải mã nguồn về
Code:
wget />
01:52:06 (161 KB/s) - `modsecurity-apache_2.5.11.tar.gz' saved [1338425/1338425]
- Thực hiện tra tính toàn vẹn của mã nguồn (việc này không bắt buộc nhưng chúng ta
nên có thói quen kiểm tra để đảm bảo rằng mã nguồn đã không bị can thiệp vào dưới
bất kỳ hình thức nào). Có thể sử dụng MD5 hay PGP để làm việc này. Ở đây sử dụng
PGP

+ Đầu tiên cần download chữ ký :
Code:
wget />
02:04:38 (14.8 MB/s) - `modsecurity-apache_2.5.11.tar.gz.asc' saved [189/189]
+ Download Publick Key:
Code:
gpg keyserver pgp.mit.edu recv-key E77B534D

gpg: Total number processed: 1
gpg: imported: 1
+ Kiểm tra chữ ký:
Code:
gpg verify modsecurity-apache_2.5.11.tar.gz.asc

gpg: Good signature from "Brian Rectanus (work) <>"
gpg: aka "Brian Rectanus <>"
gpg: aka "Brian Rectanus (personal) <>"
- Kiểm tra thành công. Thực hiện giải nén mã nguồn:
Code:
tar xfvz modsecurity-apache_2.5.11.tar.gz
- Kiểm tra các gói thư viện cần thiết, ModSecurity yêu cầu có 4 thành phần sau
trước khi biên dịch :
+ apxs : Kiểm tra bằng cách :
Code:
whereis -b apxs

apxs: /usr/sbin/apxs
Nếu chưa có, chúng ta phải cài thêm gói httpd-devel (hay apache2-dev đối với dòng
debian,ubuntu )
Code:

yum install httpd-devel (hoặc apt-get install apache2-dev)
+ libxml2: Kiểm tra bằng cách :
Code:
whereis -b libxml2

libxml2: /usr/lib/libxml2.a /usr/lib/libxml2.so /usr/include/libxml2
Nếu chưa có, chúng ta phải cài thêm gói libxml2-devel (hay libxml2-dev đối với
debian,ubuntu )
Code:
yum install libxml2-devel (hoặc apt-get install libxml2-dev)
+ pcre: Kiểm tra bằng cách :
Code:
whereis pcre

pcre: /usr/include/pcre.h /usr/share/man/man3/pcre.3.gz
Nếu chưa có thì chúng ta phải cài thêm gói pcre-devel :
yum install pcre-devel (hoặc apt-get install pcre-dev)
+ mod_unique_id: Là mod thường đã được biên dịch cùng Apache. Có thể kiểm tra
lại bằng cách tìm trong httpd.conf dòng:
Code:
LoadModule unique_id_module modules/mod_unique_id.so
Nếu chưa có, chúng ta phải thêm vào với nội dung như trên.
- Chuyển vào thư mục chứa mã nguồn và tiến hành biên dịch :
Code:
cd modsecurity-apache_2.5.11/apache2/
+ Tạo Make file :
Code:
./configure

configure: creating ./config.status

config.status: creating Makefile
config.status: creating build/apxs-wrapper
config.status: creating mlogc-src/mlogc-batch-load.pl
config.status: creating t/run-unit-tests.pl
config.status: creating t/run-regression-tests.pl
config.status: creating t/gen_rx-pm.pl
config.status: creating t/csv_rx-pm.pl
config.status: creating t/regression/server_root/conf/httpd.conf
config.status: creating /tools/rules-updater.pl
config.status: creating mlogc-src/Makefile
config.status: creating mod_security2_config.h
+ Tiến hành biên dịch
Code:
make
Sau khi biên dịch thành công file mod_security2.so sẽ được tạo ra trong thư mục
libs.
- Tích hợp ModSecurity vào Apache
Để Apache nhận ra sự tồn tại của ModSecurity chúng ta cần copy mod_security2.so
đến thư mục chứa
modules của apache, đối với distro CentOS là /etc/httpd/modules
Code:
cp /libs/mod_security2.so /etc/httpd/modules/
Sửa lại file httpd.conf để thực hiện load module ModSecurity:
Code:
vi /etc/httpd/conf/httpd.conf
Thêm dòng
Code:
LoadModule security2_module modules/mod_security2.so
- Quy định file cấu hình ModSecurity
Chúng ta có thể cấu hình trực tiếp các thông số và rule của ModSecurity vào file

httpd.conf. Nhưng để cho rõ ràng và đảm bảo không sai sót trong quá trình thực hiện
- gây ảnh hưởng Apache, Chúng ta nên tạo một file cấu hình riêng và sau đó include
vào.
Trong CentOS các file cấu hình riêng mặc định chứa trong /etc/httpd/conf.d/
Code:
vi /etc/httpd/conf.d/modsecurity.conf
Thêm vào các thông số cấu hình cơ bản
Code:
<IfModule security2_module>

# Bat che do loc cua Modsecurity
SecRuleEngine On
# Thiet lap action mac dinh
SecDefaultAction "phase:2,deny,log,status:404"
# rule thu nghiem block tat ca request co uri chua "hacker"
SecRule REQUEST_URI "hacker"

</IfModule>
Thực hiện thử nghiệm để kiểm tra hoạt động của ModSecurity. Tiến hành tạo 2 file
trong thư mục web, hacker.html và index.html chẳng hạn. Khi chúng ta truy cập vào
file index.html thì trình duyệt trả về kết quả bình thường
Còn khi truy cập vào hacker.html thì trình duyệt báo lỗi :
Code:
404 – Forbidden
Đó là kết quả do ModSecurity đã chặn những URI có chứa chuỗi hacker và cũng
đồng nghĩa với việc ModSecurity đã hoạt động.
2.4. VIẾT RULES
2.4.1. Cú pháp SecRule
SecRule được sử dụng để tạo các rule cho ModSecurity . Cú pháp rất đơn giản
Code:

SecRule Target Operator [Actions]
Target (mục tiêu): Quy định cụ thể mục tiêu của request hoặc response mà chúng ta
muốn kiểm tra. Trong ví dụ kiểm cơ bản được đưa ra trong phần trước, sử dụng biến
có tên REQUEST_URI, trong đó có các URI được request trên server, để nhận diện
và chặn bất cứ Client nào truy cập vào các vị trí /hacker.html. Hiện có hơn 70 biến có
thể được sử dụng để tạo rule.
Ngoài ra còn có một loại biến đặc biệt được gọi là biến collection có thể chứa nhiều
đối số. Một ví dụ về collection là ARGS, trong đó có chứa tất cả các đối số được
truyền trong một chuỗi truy vấn hoặc thông qua một request POST.
Phần Operator xác định phương pháp và so sánh khớp dữ liệu để kích hoạt Action.
Với Operator, mặc định là @rx
Cuối cùng, Actions (hành động) là một danh sách các hành động được thực hiện nếu
phù hợp (matching) rule. Action có thể là allow (cho phép) hoặc deny (từ chối) các
request; và quy định cụ thể các mã trạng thái (status code) khi trả về (response) cho
client. Nếu không có action nào được quy định, các action mặc định của action
SecDefaultAction sẽ được sử dụng (rule chứa action này thường được khai báo đầu
tiên).
Để làm rõ hơn, chúng ta xem ví dụ. Giả sử chúng ta là một chủ doanh nghiệp nhỏ bán
sách dạy nấu ăn ở định dạng file PDF trên website. Để lôi kéo khách hàng mua sách,
chúng ta cung cấp một chương mẫu có chứa các công thức nấu ăn ngon nhất trong
cuốn sách, mà họ có thể tải về miễn phí để xem trước khi quyết định mua.
Công việc kinh doanh đang ổn định, nhưng sau đó đột nhiên chúng ta nhận được đơn
khiếu nại qua email nói rằng trang web của chúng ta rất chậm hoặc không truy cập
được.
Khi nhìn vào log chúng ta nhận thấy rằng, một IP kết nối tới server web tràn ngập với
các request cho các chương mẫu. Các chuỗi user-agent thấy được có tênRed Bullet
Downloader . User-agent này của các chương trình Download nhanh.
Giải pháp đưa ra để giải quyết vấn đề này là dùng Mod Securiry để ngăn chặn các
user-agent này download. Rules được viết như sau.
Code:

SecRule REQUEST_HEADERS:User-Agent "Red Bullet" "deny,nolog"
Trong ví dụ trên, REQUEST_HEADERS là một Collection chứa tất cả các trường
trong thông điệp header (message header) được gởi đến bởi client và trong header
này chứa User-agent. Vì vậy, ta sử dụng từ khoá cho user-agent là “Red Bullet” vì từ
Red Bullet này thường xuyên xuất hiện trong các header được gởi đển từ client. Và
Action là deny – là từ chối và nolog là không ghi lại log
2.4.1.1. Biến và bộ chọn lọc Collection
Hiện khoản hơn 70 biến có sẵn. ModSecurity sử dụng hai loại biến: biến Standard,
đơn giản chỉ chứa một giá trị duy nhất, và biến Collection, có thể chứa nhiều hơn một
giá trị. Một ví dụ về một Collection là REQUEST_HEADERS, trong đó chứa tất cả
các trường trong thông điệp header mà Client gởi tới Server, chẳng hạn như User-
agent hoặc Referer.
Để truy cập vào một trường trong collection, chúng ta ghi tên collection, tiếp theo là
dấu hai chấm và sau đó là tên của trường hoặc tuỳ chọn mà chúng ta muốn truy cập.
Ví dụ:
Code:
SecRule REQUEST_HEADERS:Referer "bad-referer.com"
Trong trường hợp kiểm tra toàn bộ dữ liệu trên tất cả các collection. Ví dụ, nếu chúng
ta muốn kiểm tra sự hiện diện của chuỗi script có thể sử dụng rules sau đây:
Code:

×