Các phương pháp xác thực & bảo mật ứng dụng-P1: Basic Authentication

6 phút đọc

Tổng quan

Basic Authentication là phương thức xác thực đơn giản nhất trong HTTP, được định nghĩa trong RFC 7617. Client gửi usernamepassword được encode Base64 trong header Authorization của mỗi HTTP request.

Đây là điểm khởi đầu tốt để hiểu các cơ chế xác thực — vì các phương pháp hiện đại hơn (Digest, JWT, OAuth2...) đều ra đời để khắc phục những hạn chế của Basic Auth.


Flow xác thực

sequenceDiagram
    participant Client
    participant Server
    Client->>Server: GET /resource
    Server-->>Client: 401 Unauthorized (WWW-Authenticate: Basic)
    Client->>Client: Encode Base64(username:password)
    Client->>Server: GET /resource (Authorization: Basic dXNlcjpwYXNz)
    Server->>Server: Decode Base64 → Verify credentials
    alt Credentials hợp lệ
        Server-->>Client: 200 OK + Resource
    else Credentials không hợp lệ
        Server-->>Client: 401 Unauthorized
    end

⚠️ Base64 KHÔNG phải mã hóa

Đây là điểm hay bị hiểu nhầm nhất. Base64 chỉ là encoding (chuyển đổi định dạng), không phải encryption.

Ví dụ, chuỗi user:password123 sau khi encode Base64 thành:

dXNlcjpwYXNzd29yZDEyMw==

Bất kỳ ai cũng có thể decode ngay lập tức:

# Decode dễ dàng trên terminal
echo "dXNlcjpwYXNzd29yZDEyMw==" | base64 --decode
# Output: user:password123

Kết luận: Nếu không có HTTPS, credentials bị lộ hoàn toàn khi bị sniff trên network.


Ví dụ thực tế

cURL:

# Cách 1: dùng flag -u
curl -u username:password https://api.example.com/resource

# Cách 2: gửi header thủ công
curl -H "Authorization: Basic dXNlcjpwYXNz" https://api.example.com/resource

# Cách 3: nhúng credentials trực tiếp vào URL
curl https://username:password@api.example.com/resource

# Lưu ý: nếu password có ký tự đặc biệt, phải percent-encode
# Ví dụ password là p@ssw0rd → encode @ thành %40
curl https://username:p%40ssw0rd@api.example.com/resource

Tương tự với các ký tự đặc biệt khác trong URL:

Ký tự Encode
@ %40
: %3A
/ %2F
# %23
? %3F
& %26
+ %2B
% %25

PHP (Laravel):

// Middleware kiểm tra Basic Auth
public function handle(Request $request, Closure $next)
{
    $credentials = base64_decode($request->header('Authorization'));
    [$username, $password] = explode(':', $credentials, 2);

    if (!Auth::attempt(compact('username', 'password'))) {
        return response('Unauthorized', 401)
            ->header('WWW-Authenticate', 'Basic realm="API"');
    }

    return $next($request);
}

JavaScript (Fetch API):

const credentials = btoa('username:password');

fetch('https://api.example.com/resource', {
  headers: {
    'Authorization': `Basic ${credentials}`
  }
});

Ưu & Nhược điểm

Ưu điểm Nhược điểm
Cực kỳ đơn giản, mọi HTTP client đều hỗ trợ Credentials bị lộ nếu không dùng HTTPS
Không cần session, token hay state Gửi credentials trong mỗi request → rủi ro cao
Dễ test và debug API Không có cơ chế logout (phía server)
Được hỗ trợ native bởi trình duyệt Không hỗ trợ fine-grained permissions

Khi nào nên và không nên dùng?

✅ Phù hợp khi:

  • Internal APIs trong mạng nội bộ (intranet) với HTTPS
  • Script tự động hóa (CI/CD, cron jobs) cần xác thực đơn giản
  • Bảo vệ môi trường STG/UAT không cho public truy cập — cấu hình ở tầng Nginx, không cần code trong app
  • Kết hợp với TLS mutual authentication

Config Nginx cho môi trường STG

Use case phổ biến nhất của Basic Auth thực tế: chặn truy cập STG bằng Nginx mà không cần sửa code ứng dụng.

Bước 1 — Tạo file .htpasswd:

# Cài htpasswd
sudo apt install apache2-utils      # Ubuntu/Debian
sudo yum install httpd-tools        # CentOS/RHEL

# Tạo file và thêm user đầu tiên
sudo htpasswd -c /etc/nginx/.htpasswd stguser

# Thêm user tiếp theo (bỏ -c để không ghi đè)
sudo htpasswd /etc/nginx/.htpasswd anotheruser

File .htpasswd sau khi tạo (password đã được hash):

stguser:$apr1$xyz$hashedpassword
anotheruser:$apr1$abc$anotherhash

Bước 2 — Config Nginx:

server {
    listen 443 ssl;
    server_name stg.example.com;

    # Bảo vệ toàn bộ site
    auth_basic "Staging Environment";
    auth_basic_user_file /etc/nginx/.htpasswd;

    # Ngoại lệ: webhook endpoint không cần Basic Auth
    # (vì Stripe/service bên ngoài không tự gửi credentials)
    location /stripe/webhook {
        auth_basic off;
        proxy_pass http://127.0.0.1:8000;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
    }
}

Bước 3 — Reload Nginx:

sudo nginx -t             # Kiểm tra config hợp lệ
sudo systemctl reload nginx

❌ Không phù hợp khi:

  • Public API cho người dùng cuối
  • Ứng dụng cần logout hoặc revoke quyền truy cập
  • Hệ thống yêu cầu bảo mật cao (ngân hàng, y tế...)
  • Cần phân quyền chi tiết (role-based access)

Lưu ý quan trọng

  • Bắt buộc dùng HTTPS — không có ngoại lệ nào cho production
  • Base64 không phải mã hóa, chỉ là encoding → dễ dàng decode
  • Mỗi request đều phải gửi lại credentials
  • Cân nhắc dùng API Key hoặc JWT nếu cần bảo mật tốt hơn

Tiếp theo: Digest Authentication — phiên bản cải tiến của Basic Auth, dùng hash thay vì gửi credentials dạng plaintext.

Bài viết liên quan

Đang cập nhật...