Thiết lập luật Modsecurity chống SQL Injection
Thiết lập luật Modsecurity chống SQL Injection
1) Giới thiệu
SQL injection được đánh giá là một trong những lỗi cơ bản và phổ biến trên các ứng dụng Web. Trong bài viết này tôi sẽ trình bày cách sử dụng Modsecurity để có thể ngăn chặn lỗi này trên một ứng dụng web.
Trong bài viết tôi chủ yếu tập trung vào MySQL một trong những hệ quản trị cơ sở dữ liệu phổ biến hiện nay.
Một số lưu ý trước khi vào nội dung chính của bài viết này:
Trong MySQL có hai cơ sở dữ liệu mặc định:
Mã:
mysql (Privileged)
information_schema (Version >= 5) .
Các dấu comment:
Mã:
#
/*
-- -
;
`
Các dấu comment trong MySQL được hiểu nếu trong câu truy vấn gặp dấu comment thì toàn bộ phần truy vấn đằng sau sẽ được hiểu thành comment và không được thực hiện.
Ví dụ như câu truy vấn:
Mã:
' OR 1=1 -- -' ORDER BY id;
Câu này sẽ được hiểu thành: ' OR 1=1 -- -' phần ORDER BY id bị hiểu thành comment của câu truy vấn.
Tiếp theo trong một form đăng nhập bạn sẽ hiểu là việc sau khi người dùng POST dữ liệu lên server thì Server sẽ kiểm tra xem trong cơ sở dữ liệu có các bản ghi tương ứng không. Vì vậy có một số cách mà hacker thường sử dụng:
Mã:
' OR '1
' OR 1 -- -
" OR "" = "
" OR 1 = 1 -- -
'='
'LIKE'
'=0--+
Bản chất của những đoạn mã này là việc inject vào ứng dụng một câu truy vấn nhằm biến câu truy vấn trở thành luôn đúng và có thể Bypass qua việc truy vấn các bản ghi trong cơ sở dữ liệu của website.
Ví dụ:
Mã:
SELECT * FROM Users WHERE username = 'Mike' AND password = '' OR '' = ''
Câu truy vấn này được hiểu: tìm trong bảng Users có tên đăng nhập là Mike và password là OR = ‘’ là 1 phép toán logic đúng điều này làm cho câu truy vấn trở thành câu truy vấn đúng.
Trong MySQL có bảng mặc định là mysql.user và có 2 cột là user, password có thể dùng câu truy vấn: SELECT current_user; để thấy được điều này.
Thông thường một lập trình viên sẽ thường có những hàm lọc dữ liệu trước khi sử dụng dữ liệu cho các ứng dụng web của mình. Tuy vậy không phải các hàm lọc này lúc nào cũng phát huy tác dụng ví dụ như viêc lọc từ khóa UNION, SELECT… bằng cách:
Hex các dữ liệu truyền vào:
Mã:
' AND 1=0; DECLARE @S VARCHAR(4000) SET @S=CAST(0x53454c4543542031 AS VARCHAR(4000)); EXEC (@S);--sp_password
Sử dụng các URL Encode:
Mã:
%53%45%4c%45%43%54%20%31%20%46%52%4f%4d%20%64%75%61%6c
Có thể còn sử dụng Double URL Encoded:
Mã:
%2553%2545%254c%2545%2543%2554%2520%2531%2520%2546%2552%254f%254d%2520%2564%2575%2561%256c
2). Sử dụng luật chặn
Với tâm lý của một hacker thường sẽ chỉ quan tâm đến những dữ liệu mình cần và bỏ qua phần còn lại. Vì vậy hacker thường sử dụng các ký tự comment trong MySQL để có thể đạt được mục đích của mình
Như đã phân tích phần comment trong MySQL chúng ta có thể dễ dàng chặn được các ký tự coment với Modsecurity:
Sẽ cần định nghĩ một đoạn regex:
Mã:
(/\*!?|\*/|[';]--|--[\s\r\n\v\f]|(?:--[^-]*?-)|([^\-&])#.*?[\s\r\n\v\f]|;?\\x00)
Đoạn regex này với ý nghĩa bỏ qua tất cả các ký tự: ‘\*’, ‘--’, ‘-’….
Tránh việc bypass chúng ta nên để giá trị các biến truyền vào dạng ký tự thông thường và có được urlDecodeUni hay không.
Lời khuyên trong phần này để có thể dễ dàng phát hiện ra luật thiết lập có đúng không thì nên lưu lại log.
Một số cấu hình mặc định của Modsecurity các giá trị biến được gửi lên server chuyển về dạng plain-text sau đó so sánh với regex nếu không vi phạm sẽ được server sử lý và trả lại người dùng . Nếu vi phạm sẽ ngay lập tức trả về người dùng (tùy theo cấu hình mặc định) tôi để 404 Not found, và lưu vào Auditlog
Chặn các tên database phổ biến.
Định nghĩa 1 đoạn regex:
Mã:
(?i:(?:m(?:s(?:ysaccessobjects|ysaces|ysobjects|ysqueries|ysrelationships|ysaccessstorage|ysaccessxml|ysmodules|ysmodules2|db)|aster\.\.sysdatabases|ysql\.db)|s(?:ys(?:\.database_name|aux)|chema(?:\W*\(|_name)|qlite(_temp)?_master)|d(?:atabas|b_nam)e\W*\(|information_schema|pg_(catalog|toast)|northwind|tempdb))
Đoạn regex này giúp phát hiện các tên mặc định của ORACLE, và MySQL: ysaccessobjects, ysaces, ysobjects, ysrelationships, ysaccessstorage, ysmodules...
Một số luật tôi đã thiết lập:
Mã:
[Detect comment]
SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|!REQUEST_COOKIES:/_pk_ref/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(/\*!?|\*/|[';]--|--[\s\r\n\v\f]|(?:--[^-]*?-)|([^\-&])#.*?[\s\r\n\v\f]|;?\\x00)" "phase:2,rev:'2',maturity:'8',accuracy:'8',id:'981231',t:none,t:urlDecodeUni,block,msg:'SQL Comment Sequence Detected.',severity:'2',capture,logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',tag:' /WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.sql_injection_score=+1,setvar:'tx.msg=%{rule.msg}',setvar:tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"
----[ Detect DB Names ]------
SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|!REQUEST_COOKIES:/_pk_ref/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(?i:(?:m(?:s(?:ysaccessobjects|ysaces|ysobjects|ysqueries|ysrelationships|ysaccessstorage|ysaccessxml|ysmodules|ysmodules2|db)|aster\.\.sysdatabases|ysql\.db)|s(?:ys(?:\.database_name|aux)|chema(?:\W*\(|_name)|qlite(_temp)?_master)|d(?:atabas|b_nam)e\W*\(|information_schema|pg_(catalog|toast)|northwind|tempdb))" \
"phase:2,rev:'2',ver:'OWASP_CRS/2.2.9',maturity:'9',accuracy:'8',capture,t:none,t:urlDecodeUni,ctl:auditLogParts=+E,block,msg:'SQL Injection Attack: Common DB Names Detected',id:'981320',logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',severity:'2',tag:'OWASP_CRS/WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"
Chú ý: Biến setvar:tx.sql_injection_score dùng để đếm số lần request vi phạm của 1 IP tôi đang để nếu setvar:tx.sql_injection_score=1 tôi sẽ chặn request đó.
1) Giới thiệu
SQL injection được đánh giá là một trong những lỗi cơ bản và phổ biến trên các ứng dụng Web. Trong bài viết này tôi sẽ trình bày cách sử dụng Modsecurity để có thể ngăn chặn lỗi này trên một ứng dụng web.
Trong bài viết tôi chủ yếu tập trung vào MySQL một trong những hệ quản trị cơ sở dữ liệu phổ biến hiện nay.
Một số lưu ý trước khi vào nội dung chính của bài viết này:
Trong MySQL có hai cơ sở dữ liệu mặc định:
Mã:
mysql (Privileged)
information_schema (Version >= 5) .
Các dấu comment:
Mã:
#
/*
-- -
;
`
Các dấu comment trong MySQL được hiểu nếu trong câu truy vấn gặp dấu comment thì toàn bộ phần truy vấn đằng sau sẽ được hiểu thành comment và không được thực hiện.
Ví dụ như câu truy vấn:
Mã:
' OR 1=1 -- -' ORDER BY id;
Câu này sẽ được hiểu thành: ' OR 1=1 -- -' phần ORDER BY id bị hiểu thành comment của câu truy vấn.
Tiếp theo trong một form đăng nhập bạn sẽ hiểu là việc sau khi người dùng POST dữ liệu lên server thì Server sẽ kiểm tra xem trong cơ sở dữ liệu có các bản ghi tương ứng không. Vì vậy có một số cách mà hacker thường sử dụng:
Mã:
' OR '1
' OR 1 -- -
" OR "" = "
" OR 1 = 1 -- -
'='
'LIKE'
'=0--+
Bản chất của những đoạn mã này là việc inject vào ứng dụng một câu truy vấn nhằm biến câu truy vấn trở thành luôn đúng và có thể Bypass qua việc truy vấn các bản ghi trong cơ sở dữ liệu của website.
Ví dụ:
Mã:
SELECT * FROM Users WHERE username = 'Mike' AND password = '' OR '' = ''
Câu truy vấn này được hiểu: tìm trong bảng Users có tên đăng nhập là Mike và password là OR = ‘’ là 1 phép toán logic đúng điều này làm cho câu truy vấn trở thành câu truy vấn đúng.
Trong MySQL có bảng mặc định là mysql.user và có 2 cột là user, password có thể dùng câu truy vấn: SELECT current_user; để thấy được điều này.
Thông thường một lập trình viên sẽ thường có những hàm lọc dữ liệu trước khi sử dụng dữ liệu cho các ứng dụng web của mình. Tuy vậy không phải các hàm lọc này lúc nào cũng phát huy tác dụng ví dụ như viêc lọc từ khóa UNION, SELECT… bằng cách:
Hex các dữ liệu truyền vào:
Mã:
' AND 1=0; DECLARE @S VARCHAR(4000) SET @S=CAST(0x53454c4543542031 AS VARCHAR(4000)); EXEC (@S);--sp_password
Sử dụng các URL Encode:
Mã:
%53%45%4c%45%43%54%20%31%20%46%52%4f%4d%20%64%75%61%6c
Có thể còn sử dụng Double URL Encoded:
Mã:
%2553%2545%254c%2545%2543%2554%2520%2531%2520%2546%2552%254f%254d%2520%2564%2575%2561%256c
2). Sử dụng luật chặn
Với tâm lý của một hacker thường sẽ chỉ quan tâm đến những dữ liệu mình cần và bỏ qua phần còn lại. Vì vậy hacker thường sử dụng các ký tự comment trong MySQL để có thể đạt được mục đích của mình
Như đã phân tích phần comment trong MySQL chúng ta có thể dễ dàng chặn được các ký tự coment với Modsecurity:
Sẽ cần định nghĩ một đoạn regex:
Mã:
(/\*!?|\*/|[';]--|--[\s\r\n\v\f]|(?:--[^-]*?-)|([^\-&])#.*?[\s\r\n\v\f]|;?\\x00)
Đoạn regex này với ý nghĩa bỏ qua tất cả các ký tự: ‘\*’, ‘--’, ‘-’….
Tránh việc bypass chúng ta nên để giá trị các biến truyền vào dạng ký tự thông thường và có được urlDecodeUni hay không.
Lời khuyên trong phần này để có thể dễ dàng phát hiện ra luật thiết lập có đúng không thì nên lưu lại log.
Một số cấu hình mặc định của Modsecurity các giá trị biến được gửi lên server chuyển về dạng plain-text sau đó so sánh với regex nếu không vi phạm sẽ được server sử lý và trả lại người dùng . Nếu vi phạm sẽ ngay lập tức trả về người dùng (tùy theo cấu hình mặc định) tôi để 404 Not found, và lưu vào Auditlog
Chặn các tên database phổ biến.
Định nghĩa 1 đoạn regex:
Mã:
(?i:(?:m(?:s(?:ysaccessobjects|ysaces|ysobjects|ysqueries|ysrelationships|ysaccessstorage|ysaccessxml|ysmodules|ysmodules2|db)|aster\.\.sysdatabases|ysql\.db)|s(?:ys(?:\.database_name|aux)|chema(?:\W*\(|_name)|qlite(_temp)?_master)|d(?:atabas|b_nam)e\W*\(|information_schema|pg_(catalog|toast)|northwind|tempdb))
Đoạn regex này giúp phát hiện các tên mặc định của ORACLE, và MySQL: ysaccessobjects, ysaces, ysobjects, ysrelationships, ysaccessstorage, ysmodules...
Một số luật tôi đã thiết lập:
Mã:
[Detect comment]
SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|!REQUEST_COOKIES:/_pk_ref/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(/\*!?|\*/|[';]--|--[\s\r\n\v\f]|(?:--[^-]*?-)|([^\-&])#.*?[\s\r\n\v\f]|;?\\x00)" "phase:2,rev:'2',maturity:'8',accuracy:'8',id:'981231',t:none,t:urlDecodeUni,block,msg:'SQL Comment Sequence Detected.',severity:'2',capture,logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',tag:' /WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.sql_injection_score=+1,setvar:'tx.msg=%{rule.msg}',setvar:tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"
----[ Detect DB Names ]------
SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|!REQUEST_COOKIES:/_pk_ref/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "(?i:(?:m(?:s(?:ysaccessobjects|ysaces|ysobjects|ysqueries|ysrelationships|ysaccessstorage|ysaccessxml|ysmodules|ysmodules2|db)|aster\.\.sysdatabases|ysql\.db)|s(?:ys(?:\.database_name|aux)|chema(?:\W*\(|_name)|qlite(_temp)?_master)|d(?:atabas|b_nam)e\W*\(|information_schema|pg_(catalog|toast)|northwind|tempdb))" \
"phase:2,rev:'2',ver:'OWASP_CRS/2.2.9',maturity:'9',accuracy:'8',capture,t:none,t:urlDecodeUni,ctl:auditLogParts=+E,block,msg:'SQL Injection Attack: Common DB Names Detected',id:'981320',logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',severity:'2',tag:'OWASP_CRS/WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1',tag:'PCI/6.5.2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"
Chú ý: Biến setvar:tx.sql_injection_score dùng để đếm số lần request vi phạm của 1 IP tôi đang để nếu setvar:tx.sql_injection_score=1 tôi sẽ chặn request đó.