APP_KEY và lập trình viên

Mỗi lần một lập trình viên bắt đầu tạo một ứng dụng Laravel mới hoặc clone một ứng dụng Laravel, việc tạo một mã/khóa ứng dụng ( Application Key) hay APP_KEY là một trong những bước khởi đầu quan trọng nhất.

Phiên bản cập nhật bảo mật gần đây của Laravel đã sửa một vấn đề về cách mà APP_KEY được sử dụng. Với những người đã gặp phải vấn đề này, họ sẽ phải có access với production APP_KEY để sửa nó. Cách đơn giản nhất đó là thay đổi APP_KEY. Điều này dẫn tới một số câu hỏi : Vậy APP_KEY dùng để làm gì ? Để thay đổi nó chúng ta cần những gì? Làm cách nào để quản lý những key này cho các ứng dụng Laravel của chúng ta?

Trong bài viết này chúng ta sẽ bàn về những gì APP_KEY có thể làm, vài hiểu nhầm/nhận thức sai thường gặp về mối quan hệ của nó với password hashing của người dùng, và những bước đơn giản để thay đổi APP_KEY một cách an toàn mà không bị mất quyền truy cập vào dữ liệu của bạn.

Sửa lỗi bảo mật Laravel

Đầu tháng 8, Laravel phiên bản 5.5 và 5.6 đã nhận được một bản sửa lỗi liên quan tới mã hóa cookie. Một mặt, cách để sửa lỗi này đơn giản và hầu hết các ứng dụng đều không bị ảnh hưởng. Mặt khác, đây cũng là một nguy cơ bảo mật nghiêm trọng và nó thể hiện nhu cầu hiểu rõ hơn cách mà APP_KEY hoạt động của cộng đồng.

Để khai thác được vấn đề bảo mật này cần ai đó hiểu rõ APP_KEY, đó là lí do vì sao tôi sẽ hướng dẫn các bạn qua từng bước, chi tiết về key của bạn, tại sao nó quan trọng và cách để thay đổi nó.

Những thông tin về bản nâng cấp sửa lỗi bảo mật các bạn có thể tìm hiểu ở các nguồn sau đây:

APP_KEY là gì?

Khóa ứng dụng là một chuỗi 32 kí tự ngẫu nhiên được lưu tại khóa APP_KEY bên trong file .env của bạn. Bộ cài Laravel sẽ tạo một key cho bạn nên bạn sẽ không tìm thấy nó khi bạn clone một ứng dụng có sẵn .

Hẳn bạn đã từng gặp lỗi này trước đây :

 

Để tạo một key mới, bạn có thể tự mình tạo 1 key và paste nó vào file .env. Hoặc bạn có thể chạy dòng lệnh ” php artisan key:generate” để Laravel tự động tạo và insert key này cho bạn.

Khi ứng dụng của bạn chạy, APP_KEY sẽ được sử dụng bởi cookies. Laravel dùng key này cho tất cả các cookies đã được mã hóa, bao gồm cả cookie của session, trước khi chuyển nó tới trình duyệt của người dùng, và nó cũng được dùng để giải mã cookies được đọc từ trình duyệt. Điều này nhằm tránh việc phía client thay đổi cookies và phân quyền admin cho họ hoặc giả mạo một người dùng khác trong ứng dụng của bạn. Cookies được mã hóa là một tính năng bảo mật quan trọng của Laravel.

Tất cả quá trình mã hóa và giải mã được xử lí trong Laravel bởi Encrypter ( bộ công cụ mã hóa) sử dụng các công cụ bảo mật xây dựng sẵn của PHP, bao gồm cả OpenSSL. Chúng ta sẽ không tìm hiểu kĩ cách mã hóa hoạt động trong bài này, nhưng nếu bạn muốn tìm hiểu có thể đọc thêm về  PHP implementation of OpenSSLopenssl_encrypt function.

Những quan niệm/định nghĩa sai lầm thường gặp về password hashing(hàm băm).

Một quan niệm sai thường thấy ở trong cộng đồng Laravel – bao gồm cả bản thân tôi trước đây – đó là APP_KEY được dùng để hash mật khẩu. Thực tế thì không phải, nhưng chính quan niệm này đã khiến nhiều người cho rằng không thể thay đổi APP_KEY mà không làm ảnh hưởng tới việc đăng nhập của tất cả người dùng.

Mật khẩu không được mã hóa (encrypted) mà được băm (hashed).

Mật khẩu của Laravel được hash bởi phương thức Hash::make() hoặc bcrypt(), và không có cái nào trong 2 phương thức trên sử dụng APP_KEY. Giờ chúng ta hãy thử nhìn qua về mã hóa(encryption) và hàm băm(hashing) trong Laravel.

Encrypting vs. Hashing

Có hai kiểu cấu trúc mã hóa điện tử chính trong Laravel là Crypt( Mã hóa đối xứng – Symmetric Encryption) và Hash( hàm băm mã hóa một chiều – one way cryptographic hashing). Mật khẩu được hashed( băm ) còn cookies được encrypted(mã hóa ). Giờ hãy nhìn vào những điểm khác nhau.

Mã hóa đối xứng – Symmetric Encryption

Ví dụ tôi muốn gửi một tin nhắn riêng cho người bạn tên Arthur. Lần trước gặp nhau chúng tôi đã thống nhất một key bí mật là:

$key = “dont-panic”;

Tôi muốn gửi cho Arthur một đoạn tin nhắn mà chỉ có key của chúng tôi mới có thể giải mã. Tôi sẽ dùng chuẩn (công nghiệp) ưa thích của tôi, chức năng mã hóa mã nguồn mở openssl_encrypt() ( sử dụng Crypt của Laravel) với $key của chúng tôi cùng với một đoạn văn bản được mã hóa để gửi tới anh ấy.

$message = “So long and thanks for all the fish”;

$key = “dont-panic”;

$cipher = “AES-256-CBC”;

echo openssl_encrypt($message, $cipher, $key);

output: JJEK8L4G3BCfY0evXDRxUke2zqAzq6i7wL/Px4SjaEHXqt3x7hsj4+PhVQaH4ujX

Tôi có thể gửi tin nhắn này cho Arthur bằng bất cứ cách nào tôi muốn, vì chỉ có 2 chúng tôi có key, tôi sẽ không phải lo lắng về việc tin nhắn của mình bị đọc bởi một ai khác.

Khi Arthur nhận được tin nhắn, anh ấy sẽ đảo ngược quy trình và sử dụng key bí mật của chúng tôi. Đây chính là phần Symmetric ( đối xứng ): chúng tôi đều có thể mã hóa và giải mã tin nhắn mà không lo bị mất thông tin.

$secret = “JJEK8L4G3BCfY0evXDRxUke2zqAzq6i7wL/Px4SjaEHXqt3x7hsj4+PhVQaH4ujX”;

$key = “dont-panic”;

$cipher = “AES-256-CBC”;

echo openssl_decrypt($secret, $cipher, $key);

Laravel dùng phương thức tương tự cho cookies, cho cả người gửi và người nhận, sử dụng APP_KEY như một khóa mã hóa. Cookies phản hồi được mã hóa, gửi tới người dùng, đọc lại trong một request trong tương lai, và giải mã, tất cả đều sử dụng chung một khóa ứng dụng (application key).

Hàm băm một chiều ( One-way hashing)

Ví dụ trên của chúng ta về mã hóa đối xứng cho thấy có nhiều tiềm năng để sử dụng nó, nhưng tất cả chúng đều liên quan tới giải mã một đoạn văn bản bị xáo trộn.

Tuy nhiên khi nhắc tới những thứ như mật khẩu của người dùng, chúng ta KHÔNG nên có cách để giải mã chúng. KHÔNG BAO GIỜ.

Điều này có nghĩa là chúng ta sẽ không sử dụng phương thức Crypt, cũng có nghĩa là chúng ta không thể dựa vào một mã/khóa mà chúng ta có. Thay vào đó, chúng ta dùng chức năng Băm (hashing), thứ mà có các thuộc tính như:

1.Speedy ( Thời gian xử lý nhanh ): Máy tính có thể tạo một mã hash nhanh chóng.

2.Deterministic ( Tính kiên định ): Hashing cùng 1 input sẽ luôn trả ra output giống nhau.

3.Seemingly Random(Có vẻ ngẫu nhiên): Thay đổi dù chỉ một ký tự input sẽ thay thay đổi output.

4.Unique (Duy nhất): Tỉ lệ bị trùng (các input khác nhau trả ra output giống nhau) là rất nhỏ.

5.Hard to brute force( Khó bị tấn công ): Sẽ rất khó khăn để hash tất cả các input, để có thể đoán ra được input gốc của chúng ta.

Bạn hẳn đã quen với rất vài thuật toán mã hóa một chiều sử dụng hashing như MD5 và SHA-1. Chúng xử lý nhanh nhưng không phải là những phương pháp bảo mật nhất ( chúng tỏ ra yếu hơn ở item thứ 4 và item thứ 5 trở lên).

Laravel hashing triển khai phương thức của PHP thuần là password_hash(), mặc định là một thuật toán băm với tên gọi bcrypt. Khi mã hóa một chiều, nó là một phương thức mặc định tốt và bạn không cần phải thay đổi nó ( mặc dù Laravel hiện giờ đã cung cấp thêm một vài phương thức hashing khác).

 use Illuminate\Support\Facades\Hash;

$password = “dont-panic”;

echo Hash::make($password);

// $2y$10$hEEF0lv4spxnvw5O4XyLZ.QjCE1tCu8HjMpWhmCS89J0EcSW0XELu

Nếu bạn đã từng nhìn qua bảng người dùng, ví dụ trên hẳn phải rất quen thuộc với bạn. Đây là ý nghĩa của nó :

$2y$ được hash sử dụng thuật toán blowfish (bcrypt).

10$ yếu tố “chi phí” (cost) ( cao hơn nghĩa là hash tốn nhiều thời gian để thực thi hơn ).

hEEF0lv4spxnvw5O4XyLZ  1 chuỗi ngẫu nhiên 22 kí tự, được gọi là 1 random salt ( hạt muối).

QjCE1tCu8HjMpWhmCS89J0EcSW0XELu là output của đoạn hash.

Vì đây là mã hóa một chiều, chúng ta không thể giải mã nó. Tất cả những gì chúng ta có thể làm là test nó.

Khi người dùng với mật khẩu này thử đăng nhập, Laravel hash mật khẩu của họ và sử dụng phương thức password_verify() của PHP để so sánh đoạn mã hash này với đoạn mã hash lưu trong cơ sở dữ liệu.

use Illuminate\Support\Facades\Hash;

$input = request()->get(‘password’); // “dont-panic”

$hash = ‘$2y$10$hEEF0lv4spxnvw5O4XyLZ.QjCE1tCu8HjMpWhmCS89J0EcSW0XELu’;

return Hash::check($input, $hash);

Để ý rằng Laravel chỉ yêu cầu duy nhất một mã/khóa ( trong trường hợp này là APP_KEY) khi cần thực hiện quy trình đảo ngược mã hóa đối xứng. Mật khẩu của người dùng được lưu không bao giờ được phép thực hiện quy trình trên, do đó nó sẽ không cần APP_KEY.

Nhưng điều đó không có nghĩa là bạn không cần quan tâm tới mã/khóa của mình. Thay vào đó, bảo mật nó như cách bạn làm với mọi mật khẩu khác,ví dụ như mật khẩu MySQL, mật khẩu Gmail của các bạn.

Thay đổi key của bạn :

Bất cứ cách thức quản lý định danh tốt nào cũng nên bao gồm rotation ( xoay vòng ) : đổi keys và mật khẩu thường xuyên ( ví dụ theo chu kỳ mỗi 6 tháng ) hoặc trong một số tình huống đặc biệt ( ví dụ như khi có một nhân viên nghỉ việc ).

Đa máy chủ ( Multiple Servers):

Nếu bạn chạy một ứng dụng trên nhiều server, bạn sẽ cần cập nhật lại key trên mỗi server.

Sessions (cookies) của người dùng hiện tại:

Session của bất cứ người dùng nào đã đăng nhập vào ứng dụng của bạn sẽ báo không hợp lệ ngay khi bạn thay đổi APP_KEY. Lên lịch thay đổi key của bạn vào một thời gian thích hợp để giảm thiểu sự bất tiện cho người dùng.

Các dữ liệu đã mã hóa khác :

Mặc dù cookies của bạn là nơi duy nhất sử dụng bảo mật  APP_KEY như một framework, bạn nên có một mã tự tạo cho ứng dụng của mình để mã hóa dữ liệu. Nếu bạn sử dụng bất cứ tính năng mã hóa nào của Laravel, lên kế hoạch để test và giải mã dữ liệu với key cũ của bạn và mã hóa lại nó với key mới.

Thiết lập một APP_KEY mới :

Đầu tiên, copy APP_KEY hiện có của bạn ra một thư mục khác để đề phòng thay đổi của bạn có thể gây ra những hiệu ứng ngoài mong muốn.

Trước khi bạn thử thay đổi APP_KEY trên server thành phẩm, hãy thử thay đổi nó trên máy local để đảm bảo rằng mọi thứ diễn ra suôn sẻ. Khi đã sẵn sàng, chạy lệnh ” php artisan key:generate “

 [jbathman my_app]# php artisan key:generate

**************************************

*     Application In Production!     *

**************************************

Do you really wish to run this command? (yes/no) [no]:

> yes

Application key [base64:x2SHH01+2R+vwv09YcrvXqdFJ+bbqgT9gW4njcYLjDE=] set successfully.

Vậy là xong. Nếu bạn muốn tạo một key mà không thay đổi file .env, hãy bao gồm cả  –show flag trong dòng lệnh.

 

VD: [jbathman ~/my_app]# php artisan key:generate –show

base64:c3SzeMQZZHPT+eLQH6BnpDhw/uKH2N5zgM2x2a8qpcA=

Những điều nên biết về APP_KEY:

-Thay đổi APP_KEY không tác động đến mật khẩu của người dùng.

-Sessions( dùng qua cookies) sẽ trở nên không hợp lệ nếu bạn thay đổi APP_KEY, và sẽ đăng xuất tất cả người dùng hiện tại.

-Đừng sợ APP_KEY.

-Bạn nên có một chiến lược để thường xuyên thay đổi APP_KEY cùng với các key và mật khẩu khác.

-Nếu đoạn code của bạn sử dụng bộ giải mã Laravel thủ công, bạn sẽ cần tạo một kế hoạch giải mã những dữ liệu mã hóa với key cũ và mã hóa lại nó với key mới.

 

Tác giả: Jake Bathman

Nguồn : https://tighten.co/blog/app-key-and-you/