12/08/2018, 15:06

Những lỗ hổng bảo mật thường gặp trong website và cách phòng tránh

Gần đây mình có độc một cuốn sách về bảo mật của tác giả Phạm Huy Hoàng, sau khi đọc cuốn sách này mình thấy nó khá hay và hữu ích đối với bất kỳ developer nào. Vì vậy hôm nay mình muốn chia sẻ với các bạn những gì mình thu được từ cuốn sách này. Trong bài này mình chủ yếu nêu những lỗ hổng hay gặp ...

Gần đây mình có độc một cuốn sách về bảo mật của tác giả Phạm Huy Hoàng, sau khi đọc cuốn sách này mình thấy nó khá hay và hữu ích đối với bất kỳ developer nào. Vì vậy hôm nay mình muốn chia sẻ với các bạn những gì mình thu được từ cuốn sách này. Trong bài này mình chủ yếu nêu những lỗ hổng hay gặp và cách khắc phục chứ không nêu cụ thể cách tấn công. Những cách khác phục mình cũng chỉ gợi ý từ khóa thôi, không đi sâu vào cụ thể một framework cũng như một ngôn ngữ nào cả, đựa vào từ khóa các bạn có thể tìm cách khắc phục cụ thể với hệ thống mình đang sử dụng.

XSS

Trên trang viblo.com có nhiều bài viết về lỗ hổng bảo mật này nên mình cũng không giới thiệu thêm về nó nữa và chỉ giới thiệu cách phòng trống mà thôi. Vì XSS là một dạng tấn công phổ biến và dễ gây hậu quả nghiêm trọng nên hầu hết các framework đều tích hợp sẵn cách để phòng trống lỗ hổng này, chỉ cần bạn sử dựng phiên bản framework mới nhất cũng đã tránh được khá nhiều lỗi này rồi. Nhưng quan trọng là lỗi này thường gặp ở nhiều trang khác nhau, nhiều tình huống nên nếu dev không cẩn thận rất có thể sẽ thiếu sót do không để ý.

Cách phòng chống

  1. Encode
  • Encode tất cả những gì người dùng nhập vào (mã hóa các ký hiệu <, >, ", ' ...), không tin tưởng bất kì những gì mà người dùng nhập vào.
  • Nếu bắt buộc cần hiển thị những thẻ html thì nên qui định những thẻ nào được nhập, những thẻ nào không được nhập cần mã hóa (<script> chẳng hạn). Tốt nhất tôi khuyên bạn bên sử dụng những lib có sẵn để check cho an toàn tuyệt đối không tự code rất có thể sẽ thiếu một trường hợp nào đó mà hacker có thể lợi dụng để tấn công.
  1. CSP (Content security Policy)
  • Với CSP thì có thể cấu hình trình duyệt chỉ chạy những mã javascript từ những domain được chỉ định sẵn mà thôi, chỉ cần thêm vào config của webserver dòng mã sau:
Content-Security-Policy: script-src 'self' https://apis.google.com

đoạn này này có nghĩa là trình duyệt chỉ thực hiện mã javascript trên chính domain đó và doamain apis.google.com mà thôi, dù hacker có gắn được link một đoạn mã javascript nào đó vào website thì nó cũng không thực thi link đó:

<p><script src= "//hacker.com/hack.js"></script></p>

Cookie

Hầu hết các website hiện nay đều sử dụng cookie để ghi nhớ người dùng, cookie này sẽ được gửi lên server mỗi lần request để server biết người dùng đó là ai, vì vậy nêu hacker lấy được cookie này thì có nghĩ là hacker sẽ chiếm được tài khoản của user đó.

  • Đối với website không dùng https khì việc lấy được cookie là vô cùng đơn giản, chỉ cần dùng công cụ đơn giản để sniff trong cùng một mạng LAN như Fiddler, Wireshark và dùng extention EditThisCookie để đưa vào trình duyệt là có thế chiếm quyền tài khoản đó rồi.
  • Hoặc website nào có lỗ hổng xss thì hacker sẽ chuyển hướng người dùng sang một trang web khác với hàm document.cookie thì hacker sẽ lấy được cookie của người dùng.

Cách phòng chống:

  1. Set exprired và Max-age: Để thời gian sống của cookie không quá dài sẽ giảm thiểu được thiệt hại khi bị đánh cắp cookie.
  2. Sử dụng Flag http only: Cookie có flag này sẽ không thể bị đánh cắp qua hàm document.cookie, nếu không may dính lỗi xss thì hacker cũng không lấy được cookie của người dùng.
  3. Sử dụng Flag Secure: Cookie có Flag này thì sẽ chỉ được gửi nếu giao thức là https nên hacker sẽ không thể sniff được nó, vì đã được mã hóa khi truyền tải.
  • Lưu ý:
  1. Tuyệt đối không submit những dữ liệu nhạy cảm như username, password qua những website không sử dụng https, vì thông thường hầu hết người dùng chỉ dùng 1 password cho tất cả các website, nên hacker có thể lợi dụng điều này để trộm được password.
  2. Có nhiều website có lưu thêm những thông tin khác vào cookie nên tốt nhất cần mã hóa những thông tin này bằng rsa hay chữ ký điện tử, tránh việc nghịch ngợm linh tinh.
  3. Với api restfull thì nên dùng OAuth 2.0 để login.

SQL Injection

Lỗi SQL injection hiện nay ít còn gặp vì những framework đã khắc phục hầu hết lỗi này, nhưng cũng có những trường hợp bắt buộc phải sử dựng những câu truy vấn sql thuần thì vẫn có thể bị dính lỗi bảo mật cực kỳ nguy hiểm này: Thông thường ta hay dùng câu sau để truy vấn:

$username = $request->username; // admin
$pass = $request->pass; // 123456
$sql = "select * from users where username = ' " + username + " '  And password = ' " + $pass +  " ' "; 
// câu query sẽ thành
==> select * from users where username = 'admin'  And password = '123445 '

nhưng hacker lại không hiền như vậy họ sẽ dùng:

$username = $request->username; // admin
$pass = $request->pass; // ' OR drop table users
$sql = "select * from users where username = ' " + username + " '  And password = ' " $pass " ' " ; 
// câu query sẽ thành
==> select * from users where username = 'admin'  And password = ' ' OR drop tables users; 

Như ví dụ trên ta có thể thấy nó nguy hiểm thế nào khi dính phải lỗi bảo mật này:

  • Cực kỳ nguy hiểm – Có thể gây ra những thiệt hại khổng lồ. Với SQL Injection, hacker có thể truy cập một phần hoặc toàn bộ dữ liệu trong hệ thống.
  • Lỗ hổng này rất nổi tiếng, từ developer đến hacker gần như ai cũng biết. Ngoài ra, còn có 1 số tool tấn công SQL Injection cho những ai không hề biết về lập trình.
  • Rất nhiều ông lớn từng bị dính – Sony, Microsoft UK và gần đây nhất là Yahoo. Mọi vụ lùm xùm liên quan tới “lộ dữ liệu người dùng” ít nhiều đều dính dáng tới SQL Injection.

Cách phòng chống:

  1. Lọc dữ liệu người dùng: tương tự với xss ta nên lọc dữ liệu người dùng trước khi sử dụng, loại bỏ những ký tự đặc biết như ', ", ; ....
  2. Không cộng chuỗi để tạo câu truy vấn: Sử dụng parameter thay vì cộng chuỗi.
  3. Không hiển thị exception hay message lỗi của sql: hacker có thể đựa vào mesage này để tìm ra lỗ hổng của database.
  4. Phân quyền user trong DB: Chỉ phân quyền user đó sử dụng những database cũng như table cần thiết thôi, không dùng user root để truy cập database vì nếu dính lỗi này có thể ảnh hưởng đến cả những database đang có trên server.
  5. Backup dữ liệu thường xuyên: Điều này là tất nhiên để phòng chống tất cả các rủi do, nên backup dữ liệu lên cloud chứ không nên backup và để luôn ở server hiện tại.

Giấu đầu lòi đuôi

Lỗi này nghe khá lạ phải không các bạn, nhưng có cũng không phải là ít gặp trong thực tế. Nguyên nhân chính của lỗi này là do sự bất cẩn của developer mà thôi. Lỗ hổng này xảy ra khi cho phép người dùng truy cập những tài nguyên không được phép trong hệ thống, hay do cách phân quyền user chưa truyệt để. Giả sử ta có đường link như sau để xem một order history của một user: https://viblo.asia/shop/order?id=1234. Nhưng khi hacker thay đổi cái id = 1234 là id = 1235 chẳng hạn thì đôi khi dev "quên" không check xem user khác có quyền truy cập vào order đó hay không mà cứ thế cho hiện thị ra với id đúng. Thật tai hại khi để lộ thông tin của người dùng bởi những lỗi ngớ ngẩn như thế này phải không nào.

Cách phòng chống.

  1. Test cẩn thận với những trường hợp liên quan đến thông tin cá nhân của user, hay những phân quyền user cần thiết.
  2. Bảo vệ dữ liệu: Hạn chế quyền truy cập bất hợp pháp vào source code, database, config, không phân quyền 777 đối với những source cần bảo mật trên server, tốt nhất với server hay source cần bảo mật cao thì hạn chế IP truy cập tới.
  3. Tránh để lộ key: Tránh để lộ key kiểu int, nên mã hóa id này để tránh hacker có thể lợi dụng thay đổi id, nếu có lộ hàng cũng không thể dùng một vòng lặp để lấy toàn bộ thông tin nhạy cảm khác được.

CSRF (Cross Site Request Forgery)

Lỗi này khá phổ biến và cũng hay gặp trong website, nhất là những website dùng framework cũ hay tự phát triển.

Cách tấn công:

  • Giả sử tôi đã login và trang xxx.com, vì vậy trình duyệt của tôi đã lưu cookie vào, lần sau tôi vào không cần phải login lại nữa.
  • Hacker tạo ra một form với nội dung như sau:
<img src="jav.com/smiley.gif" alt="Smiley face" height="42" awidth="42">
<form role="form" method="POST" action="https://xxx.com/users/password">
    <input type="password" name="new_password" value="123456">
    <input type="password" name="new_password_confirmation" value="123456">
    <button type="submit" class="btn btn-primary">Download JAV</button>
</form>

Nhìn vào đoạn mã trên ta có thể thấy có một ảnh, và 1 thẻ form + với một nút download jav

  • Hacker sẽ gửi form này vào mail hay nhúng vào một trang web nào đó để lừa tôi click vào.
  • Vốn đam mê JAV tôi click ngay mà không ngần lại gì cả, khi click vào thì vô tình đã có một form đổi passwork được submit tới trang xxx.com để yêu cầu đổi mật khẩu, do đã login từ trước nên form này sẽ được gửi cùng với cookie login của tôi, nên vô tình tôi đã tạo một request đổi mật khẩu mà không hề hay biết.
  • Hacker chỉ việc dùng mật khẩu mới này để login vào tài khoản của tôi.

Ngoài cách đổi mật khẩu hacker có thể dùng cách tấn công này để thực hiện nhiều hành động nguy hiểm với một user cụ thể nào đó.

Cách Khắc Phục:

  1. Sử dụng CSRF Token: Trong mỗi form hay request, ta đính kèm một CSRF token, token này sẽ được sinh ra theo session của user đó. Khi submit form về server ta gửi kèm session này theo, do được sinh ra theo session của user nên session này không thể làm giả, trên server sẽ check session này có đúng không.
  2. Kiểm tra giá trị Referer và Origin trong header: Kiểm tra giá trị này, chỉ thực hiện sử lý khi form được gửi lên từ domain của mình.
  3. Kiểm trả header XC-Requested-With: Request chứa header này là request an toàn.
  4. Với những người quản trị hệ thống quan trọng thì đăng xuất khi sử dụng xong, hoặc set timeout để yêu cầu login lại sau một khoảng thời gian không sử dụng.
  5. Dùng đúng chuẩn restfull 2.0 (GET, PUT, POST, DELETE) để thao tác với hệ thống. Tránh dùng get để thực hiện thao tác delete, thao tác này rất nguy hiểm nên bắt buộc phải dùng DELETE và cần confirm lần cuối trước khi delete.
  6. Dùng trình duyệt ẩn để truy cập những trang có nội dung nguy hiểm             </div>
            
            <div class=
0