Cảm ơn bạn đã đọc và ủng hộ blog KTMT ʘ‿ʘ Từ bây giờ chúng tôi sẽ là kipalog.com !

Comments

Mở đầu

Facebook là mạng xã hội phổ biến nhất hiện nay. Nếu bạn đọc blog này mà không có tài khoản Facebook, tôi nghi ngờ bạn đến từ Vegeta. Chức năng “Login bằng tài khoản Facebook” là môt trong những chức năng giúp cho người dùng đăng ký vào hệ thống ứng dụng của bạn một cách nhanh chóng. Phần lớn các app mobile đếu support chức năng này.

Việc cài đặt hỗ trợ chức năng login bằng Facebook trên server tuy khá đơn giản, nhưng rất dễ xảy ra lỗi bảo mật, nếu bạn không chú ý. Bài viết này trình bày một vài lỗi cơ bản hay gặp khi cài đặt trên server cho ứng dụng trên mobile

Luồng thực hiện để login bằng facebook trên mobile

Các bước được thực hiện khi người dùng bấm vào nút “login bằng facebook” trên một ứng dụng mobile. Để phần trình bày bên dưới rõ ràng, tôi sẽ gọi ứng dụng mobile là A, và server của ứng dụng này là server A, ứng dụng trên FB cho app A là FBA

  • Ứng dụng mobile A sẽ chuyển sang gọi ứng dụng Facebook để yêu cầu người dùng các quyền để truy cập vào tải khoản của người dùng trên FB
  • Sau khi người dùng cho phép ứng dụng mobile A truy cập vào FB, ứng dụng mobile A sẽ gửi request lên FB server để lấy access token
  • Server FB trả về access token cho ứng dụng mobile A

Sau khi có được access token, ứng dụng mobile có thể làm theo 2 cách

  • Cách 1: ứng dụng mobile A sử dụng access token để truy cập vào FB API, lấy thông tin của user từ FB như user id, username, email. Và gửi những thông tin này lên cho server A. Dựa vào trường user id hoặc email, server A sẽ tạo mới tài khoản cho user, hoặc merge với một tài khoản có sẵn ở trong server, rồi trả về thông tin tài khoản cho user.

  • Cách 2: ứng dụng mobile A gửi ngay access token nhận được từ FB lên cho server A. Server A sử dụng access token truy cập vào FB API và check user trình bày ở cách 1.

Cách 1 có ưu điểm hơn so với cách 2 ở chỗ, việc request vào FB API được thực hiện ở phía client do đó server sẽ giảm được tải đi một phần. Cả server và client đều có khả năng lấy được thông tin user trên FB bằng access token, vậy thì sử dụng client để lấy thông tin, server có thể sử dụng tài nguyên để làm việc khác. Hơn nữa nếu để server lấy thông tin user, thời gian để lấy thông tin sẽ phụ thuộc vào tốc độ mạng giữa server A và FB server.

Mọi lý do đều dẫn đến cách 1 là tốt hơn so với cách 2 về mặt hiệu năng.

Tuy nhiên, nếu xét về khía cạnh bảo mât, cách 1 lại cưc kỳ nguy hiểm. Vì sao vậy

Hãy xét một trường hợp thế này:

  • User B sử dụng app A và dùng chức năng login bằng FB để đăng nhập. App A cài đặt theo cách 1. Sau khi có access token, app A lấy được FB user id của B là id_B, và email là email_B. App A gửi những thông tin này lên cho server A. Server A nhận thấy hệ thống chưa có user nào có FB ID là id_B, nên tạo mới một account cho B, và trả về thông tin của B.
  • Lần tiếp theo user B sử dụng app A để đăng nhập bằng FB, server A sẽ không tạo mới account, mà trả về luôn thông tin của account đã tạo ra ở trên
  • Hacker C, bằng cách nào đó biết được URL endpoint của server A phục vụ cho việc đăng nhập bằng FB. Hacker C, truyền lên một thông tin giả với trường FB ID là id_B. Lúc này server B, chỉ kiểm tra FB ID của request là id_B, nên sẽ vẫn coi đây là request của user B và trả về thông tin của tài khoản user B.

Vậy là C có thể chiếm bất cứ tài khoản FB nào mà C biết FB id (có tới 1 tỉ tải khoản FB đó, nên C hẳn sẽ có rất nhiều thứ để chiếm đây)

Với cách implement bằng cách 1, server sẽ không thể phân biệt được request nào là request thật của user. Để chắc chắn server khi nhận được access token, phải request tới FB API để kiểm tra xem access token truyền lên có phải là của user mà app A truyền lên hay không. Nói cách khác server vẫn cần request tới FB API. Như vậy việc client gửi request tới FB API để lấy thông tin là cũng không cần thiết.

Liệu server sử dụng access token để lấy thông tin của user trên FB đã là đủ?

Hãy nói tiếp về cách implement thứ 2. Khi server A nhận access token từ app A. Server gửi request tới FB API để lấy thông tin của user như FB ID và email. Sau đó tạo mới hoặc merge với một tài khoản đã có trong hệ thống (trùng FB ID hoặc là trùng email).

Nhưng như thế đã là đủ?

Ta lại xét tiếp một tình huống như sau:

  • Hacker C tạo ra một FB app mới là FBA’ và bằng cách nào đó lấy được access token của user B khi B sử dụng FBA’.
  • Hacker C gửi request tới server A sử dụng access token FBA’.
  • Server A dùng FBA’ để lấy thông tin, và nhận thấy thông tin đó là của user B, nên server A trả về thông tin của tài khoản B

Như vây, hacker C lại chiếm được tài khoản của user B trên hệ thống A bằng cách dùng một access token trên một FB app khác của user B

Để bảo mật, cách duy nhất là khi server A nhận được access token, server A cần gửi request tới FB API để check xem access token này là từ FB Application nào.

Tổng kết

Bài viết trình bày 2 lỗi bảo mật hay gặp khi cài đặt hệ thống login bằng facebook trên server cho ứng dụng mobile. Lỗi thứ nhất là sử dụng client để lấy access token, mà không kiểm tra độ chính xác của access token. Lỗi thứ hai là chỉ kiểm tra độ chính xác của access token bằng cách thông tin user, mà không kiểm tra thông tin của app từ access token.

Nếu bạn đã có một hệ thống login bằng FB, hãy check lại nó. Nếu bạn chưa từng cài đặt hệ thống này thì chúc mừng bạn, bạn đã được cảnh báo !!!

Comments

Giới thiệu Reflection class trong PHP

Kể từ PHP 5 trở đi, Programmer PHP đã có bộ API Reflection rất hữu dụng để reverse-engineer các class, interfaces, function hay các extension. Bài viết này sẽ giới thiệu tính năng, ý nghĩa và ứng dụng của Reflection trong PHP.

Thế nào là dynamically-typed language ?

Cũng giống như Python hay Ruby, PHP là 1 dynamically-typed language.

Chúng ta hãy cùng xem xét 2 class sau:

Author.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
class Author
{
  private $name;
  private $birth;

  public function __construct($name, $birth)
  {
      $this->name = $name;
      $this->birth = $birth;
  }

  public function getName()
  {
      return $this->name;
  }

  public function getBirth()
  {
      return $this->birth;
  }

}
?>
Book.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class Book
{
  private $author;
    public function setAuthor($author)
    {
      $this->author = $author;
    }
    public function getAuthor()
    {
      return $this->author;
    }
}

?>

2 class rất đơn giản phải không :) Book hoàn toàn có thể được setAuthor() là 1 string hay là 1 instance của class Author.

Không khó để hình dung ra kết quả của đoạn code dưới đây.

sample.php
1
2
3
4
5
6
7
8
9
<?php
$book1 = new Book;
$book1->setAuthor("Nam Cao");
var_dump($book1->getAuthor());

$book2 = new Book;
$book2->setAuthor(new Author("Nam Cao","29-10-1915"));
var_dump($book1->getAuthor());
?>

Nếu chỉ dừng ở đây thì tôi với bạn chẳng có gì để nói với nhau :D Nhưng bạn hãy thử để ý, 1 instance của 1 class Book khi gọi đến hàm setAuthor hoàn toàn không có 1 khái niệm nào về $author cả. Nói cách khác, $author có thể là bất cứ 1 object nào. Điều gì sẽ xảy ra khi tôi modify class Book 1 chút như sau:

Book.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class Book
{
  private $author;
    public function setAuthor($author)
    {
      $this->author = $author;
      var_dump($author->getName()); // Attention here! Now we try to call getName() of variable $author
    }
    public function getAuthor()
    {
      return $this->author;
    }
}

?>

Bạn thử chạy lại đoạn code sample.php bên trên, bạn sẽ thấy $book1 trả về Fatal Error nhưng $book2 sẽ chạy qua bình thường!

Vào thời điểm runtime $book2, PHP sẽ “inspect” object $author truyền vào cho setAuthor() và tự hiểu $author là 1 instance của class Author và có 1 method là getName().

Reverse engineer example

Vậy PHP nói riêng và các dynamically-typed language nói chung làm thế nào để nhận biết được type của object truyền vào function hay class ?

Câu trả lời là reflection class! Bạn đã nhận ra PHP dùng reflection như thế nào qua ví dụ bên trên, bạn thậm chí có thể tự sử dụng reflection class.

Book.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class Book
{
  private $author;
    public function setAuthor($author)
    {
      $this->author = $author;
      var_dump($author->getName()); // original name
      $reflector = new ReflectionClass($author); // Here we start to inspect $author
      $authorName = $reflector->getProperty('name'); // Get local variable 'name'
      $authorName->setAccessible(true); // since 'name' is a private local variable of class Author, we need access here to modify 
      $authorName->setValue($author,'Ngo Tat To'); // now hack the 'name' of $author :))
      var_dump($author->getName()); // Guess what will be output here :D 
    }
    public function getAuthor()
    {
      return $this->author;
    }
}


?>

Bạn thử đoán xem đoạn var_dump sau sẽ ra kết quả gì :D

Book.php
1
2
3
4
5
<?php
$book2 = new Book;
$book2->setAuthor(new Author("Nam Cao","29-10-1915"));
var_dump($book1->getAuthor()); // Suprisingly, 'Ngo Tat To', not 'Nam Cao' here 
?>

Reflection class dùng để làm gì ?

Đến đây có lẽ bạn đọc đã hình dung ra phần nào cách thức hoạt động của reflection class, các ngôn ngữ dynamically-typed “hiểu” các object như thế nào. Reflection thực tế tồn tại trong PHPUnit hay các mocking framework, các code analysis framwworks hay metaprogramming.

Reflection class trong PHP là 1 tool mạnh mẽ cung cấp cho programmer chính những sức mạnh mà ngôn ngữ sở hữu. Tuy nhiên reflection class không hề được khuyến khích dùng rộng rãi, vì với bản chất là tool của quá trình reverse engineering, nó hoàn toàn có thể làm design của hệ thống trở nên mess up và khó kiểm soát.

Reflection chỉ nên dùng khi nào thực sự cần thiết, ứng dụng nhìn thấy rõ nhất là khi bạn phải “đối đầu” với 1 project mà document ko đầy đủ hay không được upadte thường xuyên. Cake Api Generator là ví dụ điển hình nhất.

Summary

  • Dynamically-typed language: Là ngôn ngữ có thể tự hiểu được object tại thời điểm runtime, không cần tại compile time. PHP, Ruby, Python là dynamically-typed language. Ngược lại C hay Java là statically typed language.
  • Reflection Class Là 1 bộ API được PHP cung cấp để sử dụng kỹ thuật reverse engineer, hữu dụng khi tạo document tự động.
Comments

Giới thiệu

Memcached là cơ sở dữ liệu được lưu trong memory. Thông thường chúng ta sử dụng memcached trong mạng nội bộ, hoặc sử dụng private IP để kết nối tới memcached, tuy nhiên trong một số trường hợp, IP của memcached server cần public ra ngoài (ví dụ toàn bộ các server đều đặt trên AWS). Trong trường hợp này, chúng ta cần bảo mật kết nối của memcached server.

Từ phiên bản 1.4.3, memcached đã support sử dụng SASL

Bài viết này sẽ giới thiệu với các bạn cách cài đặt memcached với SASL cũng như giới thiệu cơ chế, cách làm việc của SASL

Cách cài đặt SASL với memcached

Đầu tiên bạn cần cài đặt phiên bản mới nhất của memcached. Bạn sẽ cần một số gói và thư viện khác để support SASL.

install.sh
1
$> sudo apt-get install libsasl2-2 sasl2-bin libsasl2-2 libsasl2-dev libsasl2-modules

Đừng quên, để cài đặt memcached, bạn cùng sẽ cần cài libevent

Cài đặt memcached

install.sh
1
2
3
4
5
6
7
$> wget http://memcached.googlecode.com/files/memcached-1.4.3.tar.gz
$> tar xvf memcached-1.4.3.tar.gz
$> cd memcached-1.4.3
$> ./configure --enable-sasl
$> sed -i 's/-Werror//g' Makefile
$> make
$> sudo make install

Cài đặt libmemcached

install.sh
1
2
3
4
5
6
$> wget https://launchpad.net/libmemcached/1.0/1.0.17/+download/libmemcached-1.0.17.tar.gz
$> tar xvf libmemcached-1.0.17.tar.gz
$> cd libmemcached-1.0.17
$> ./configure
$> make
$> sudo make install

Set up SASL với memcached

Điều đầu tiên bạn cần đảm bảo đó là set biến môi trường SASL_CONF_PATH khi bạn chay memcached. Trong ví dụ này SASL_CONF_PATH sẽ được trỏ tới /home/kiennt/sasl

install.sh
1
$> export SASL_CONF_PATH=/home/kiennt/sasl

Sau đó bạn cần set up file memcached.conf trong SASL (tên của file sẽ là tên của ứng dụng SASL - cụ thể ở đây là memcached)

1
2
3
mech_list: plain
log_level: 5
sasldb_path: /home/kiennt/sasl/sasldb2

Tiếp theo, bạn cần tạo một file database (được trỏ tới từ bước trước) trong file memcached.conf

install.sh
1
$> sudo saslpasswd2 -c -a memcached -f /home/kiennt/sasl/sasldb2 <username>

Chú ý rằng cờ -a xác định tên của ứng dụng memcached - chính là tên của config file bạn đã xác định ở trên memcached.conf. Khi bạn chạy saslpasswd2, bạn sẽ được hỏi password và password verify cation.

Chạy memcached với SASL

Để chạy memcached với SASL, bạn cần sử dụng cờ -S để bật cơ chế security của mecached lên

install.sh
1
2
$> export SASL_CONF_PATH=/home/kiennt/sasl
$> /usr/local/bin/memcached -S -vvv

Tổng kết

Bài viết này giới thiệu với các cài đặt và chạy memcached với SASL. Giờ bạn có thể tập trung vào việc code ứng dụng của bạn rồi.

Copyright © 2015 kỹ thuật máy tính