12/08/2018, 16:20

TrustedProxy Laravel 5.5

Trong phiên bản mới nhất 5.5, file composer.json đã require package fideloper/proxy. Đây là một package cực kỳ tiện ích và quan trọng với những developer sử dụng AWS hoặc Google Cloud. Trước hết chúng ta hãy xem xét những tiện ích mà package này mang lại cho Laravel và tìm hiểu tạo sao nó lại quan ...

Trong phiên bản mới nhất 5.5, file composer.json đã require package fideloper/proxy. Đây là một package cực kỳ tiện ích và quan trọng với những developer sử dụng AWS hoặc Google Cloud. Trước hết chúng ta hãy xem xét những tiện ích mà package này mang lại cho Laravel và tìm hiểu tạo sao nó lại quan trọng với hệ sinh thái Laravel đến vậy.

TrustedProxy Package làm gì?

Nếu trang web của bạn nằm ở phía sau một proxy ví dụ như là một load balancer, ứng dụng của bạn có thể gặp phải một số vấn đề sau:

  • Redirects và PHP-generated URLs có thể không chính xác về địa chỉ website, protocol hoặc port.
  • Các Unique session có thể không được tạo cho mỗi user, dẫn đến việc truy cập các tài khoản không chính xác hoặc không có khả năng đăng nhập.
  • Việc ghi log dữ liệu hoặc thu thập dữ liệu có thể xuất phát từ cùng một vị trí (chính bản thân proxy) khiến bạn không có cách nào phân biệt giữa lượng truy cập hoặc hành động thực hiện bởi các client khác nhau.

Chúng ta có thể giải quyết vấn đề trên bằng cách lắng nghe các X-Forwarded-* header. Các header này thường được thêm bởi các proxy để cho phép ứng dụng web của bạn biết chi tiết về người khởi tạo yêu cầu. Thông thường gồm có:

  • X-Forwarded-For - Địa chỉ IP của client.
  • X-Forwarded-Host - Tên máy chủ được sử dụng để truy cập web trên trình duyệt.
  • X-Forwarded-Proto - The schema/protocol (http/https) được sử dụng bởi client.
  • X-Forwarded-Port - Port được sử dụng bởi client (thường là 80 hoặc 443).

Laravel sử dụng Symfony để xử lý các Request và Response. Các class này có các phương thức để xử lý các proxy. Tuy nhiên, vì lý do an ninh, chúng phải được thông báo về các proxy được "tin tưởng" trước khi đọc các X-Forwarded-* header. Bạn cần nói cho Laravel biết các địa chỉ IP của proxy của bạn, ứng dụng sẽ biết để "tin tưởng" chúng. Nếu nó nhận thấy địa chỉ IP nhận được là đáng tin cậy, nó sẽ tìm các X-Forwarded-* header. Nếu không, nó sẽ bỏ qua.

Laravel không có một cấu hình đơn giản nào cho việc này. Và TrustedProxy Package chỉ đơn giản là cung cấp điều đó.

Cài đặt

Bạn có thể tạo file config/trustedproxy.php bằng command:

php artisan vendor:publish --provider="FideloperProxyTrustedProxyServiceProvider"

Ở phiên bản 5.5, nếu bạn chạy vendor:publish không có tham số thì sẽ xuất hiện 1 giao diện lựa chọn giúp publish dễ dàng hơn.

<?php

return [
	/*
	.....
	*/
    'proxies' => [
        '192.168.1.10',
    ],
	/*
	.....
	*/
    'headers' => [
        (defined('IlluminateHttpRequest::HEADER_FORWARDED') ? IlluminateHttpRequest::HEADER_FORWARDED : 'forwarded') => 'FORWARDED',
        IlluminateHttpRequest::HEADER_CLIENT_IP    => 'X_FORWARDED_FOR',
        IlluminateHttpRequest::HEADER_CLIENT_HOST  => 'X_FORWARDED_HOST',
        IlluminateHttpRequest::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
        IlluminateHttpRequest::HEADER_CLIENT_PORT  => 'X_FORWARDED_PORT',
    ]
];

Trong ví dụ, chúng ta giả định có load balancer hoặc một proxy khác ở điạ chỉ IP 192.168.1.10

  • Nếu bạn sử dụng Rackspace, Amazon AWS hoặc một PasS "cloud" service có cung câp load balancers thì địa chỉ IP của load balancer có thể không được xác định. Điều đó có nghĩa là mọi địa chỉ IP cần đc tin tường. Khi đó bạn có thể set như sau (Sứ dụng * để Laravel biết rằng cần tin tưởng tất cả các địa chỉ IP làm proxy):

    <?php
    
    return [
    
    	 'proxies' => '*',
    
    ];
    

    Tuy nhiên, vẫn tình huống trên, giải sử bạn có 1 CDN (ví dụ Amazon CloudFront) chuyển đến load balancer (ví dụ như Amazon ELB) thì có thể bạn sẽ kết thúc bằng một chuỗi các proxy không xác định được chuyển tiếp. Trong trường hợp đó '*' ở trên chỉ phù hợp với proxy cuối cùng (là Amazon ELB trong ví dụ này), nghĩa là $$equest->getClientIp() sẽ trả về địa chỉ IP của proxy kế tiếp trong dòng (là Amazon CloudFront trong ví dụ) thay vì địa chỉ IP của máy client. Để luôn nhận được IP của client trong trường hợp này bạn cần tin tưởng tất các proxy trong route đến request. Bạn có thể làm điều đó như sau:

    <?php
    
    return [
    
    	 'proxies' => '**',
    
    ];
    
  • Thay đổi X-Forwarded-* Header

    Mặc định, Symfony Request sẽ mong đợi các header name như sau:

    X-Forwarded-For
    X-Forwarded-Host
    X-Forwarded-Proto
    X-Forwarded-Port
    

    Một số proxy có thể gửi header hơi khác một chút. Trong trường hợp này bạn có thể nói cho Symfony Request những header này. Ví dụ, HAProxy có thể gửi X-Forwarded-Scheme header thay vì X-Forwarded-Proto. Khi đó bạn chỉ cần làm như sau:

    	<?php
    
    	return [
    
    		'headers' => [
    			IlluminateHttpRequest::HEADER_CLIENT_PROTO => 'X_FORWARDED_SCHEME',
    		]
    
    	];
    

    Một số service không hỗ trợ header cụ thể, vì vậy bạn cũng có thể đặt các giá trị này là null để không tin tưởng vào chúng. Đặc biệt, AWS ELB và Heroku không hỗ trợ FORWARDED và X_FORWARDED_HOST vì vậy bạn set những giá trị này là null để ngăn người dùng giả mạo các IP đáng tin cậy.

      <?php
    
     return [
    
     	'headers' => [
     		(defined('IlluminateHttpRequest::HEADER_FORWARDED') ? IlluminateHttpRequest::HEADER_FORWARDED : 'forwarded') => null,
     		IlluminateHttpRequest::HEADER_CLIENT_IP    => 'X_FORWARDED_FOR',
     		IlluminateHttpRequest::HEADER_CLIENT_HOST  => null,
     		IlluminateHttpRequest::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
     		IlluminateHttpRequest::HEADER_CLIENT_PORT  => 'X_FORWARDED_PORT',
     	]
    
     ];
    

0