it-swarm-vi.com

Kỹ thuật tốt hơn mã hóa tham số url

Tôi là một lập trình viên làm việc trên một ứng dụng trong đó lựa chọn duy nhất/so với/hạn chót là triển khai mã hóa đối xứng trên các giá trị tham số url. Dữ liệu về bản chất không nhạy cảm, nhưng chúng tôi cần ngăn chặn các đại lý bán hàng nhìn trộm vào khách hàng tiềm năng của nhau. (Khóa được tạo khi tạo phiên và mạnh về mặt mật mã.) Phiên được dự kiến ​​sẽ kết thúc thường xuyên.

Hệ thống phân cấp vai trò là Manager--> Supervisor--> Agents. Các cấu trúc dữ liệu hiện không tính đến các vai trò này theo cách thực thi nghiêm ngặt ai có thể nhìn thấy những gì. Lấy thông tin này từ cơ sở dữ liệu KHÔNG phải là bất cứ nơi nào gần với đơn giản. (Cơ sở dữ liệu đệ quy.)

Tôi biết rằng kỹ thuật này nằm trong danh sách để bảo vệ chống lại thao tác tham số. Điều gì sẽ là một kỹ thuật tốt hơn?

Các ràng buộc:
[.__.] Kiểm tra dựa trên vai trò không phải là một lựa chọn.

[Thông tin bổ sung] Các url được tạo và gửi cho khách hàng trước khi tôi thực hiện bất kỳ thay đổi nào trông giống như:

https://www.example.com/agent/?producerId=12345

Bề mặt mối đe dọa cụ thể ở đây là thao tác tham số chống lại ?agentId=12345. Id tác nhân được gán duy nhất cho mỗi đại lý. Vì vậy, nếu Đại lý A muốn xem số liệu thống kê của Đại lý B, anh ta có thể đã nhập đại lýId = 22222 để xem báo giá và số liệu thống kê bán hàng hiện tại của đại lý đó.

Một lần nữa, kiểm tra dựa trên vai trò không phải là một lựa chọn đối với tôi: Tôi không thể thực hiện thay đổi cho cơ sở dữ liệu OR tầng liên tục.

Giải pháp của tôi là sử dụng khóa mã hóa do phiên tạo (sử dụng lớp KeyGenerator của Java) và mã hóa các url gửi đi được gửi đến máy khách. Vì vậy, bây giờ, url trông giống như:

https://www.example.com/agent/?producerId=<ciphertext>

Bây giờ, nếu ai đó thử agentId = 22222, máy chủ sẽ giải mã những gì nó nghĩ là bản mã và cuối cùng sẽ tạo ra một chuỗi ký tự không hợp lệ.

(Điều này mở ra khả năng một tác nhân hiện có có thể được tìm thấy, nhưng hoàn toàn không chắc là nó có liên quan đến người thực hiện vụ tấn công.

Tôi sẽ nhấn mạnh rằng câu hỏi này không phải là về bảo mật tối ưu (sẽ là kiểm tra dựa trên vai trò để đảm bảo quyền truy cập tài nguyên) và về việc cố gắng siết chặt một số bảo mật trong một khu vực màu xám.

Một trong những người bảo mật của chúng tôi đã đề xuất giải pháp mã hóa tham số ở đây. Tôi đã nhận được một điểm mà tôi chưa từng xem xét về giải pháp này - các url bị hỏng - và sẽ sử dụng đó cũng như vấn đề bảo trì được tạo bởi giải pháp này để tranh luận về thời gian thực thi các quy tắc truy cập trong một thời gian ít dừng lại.

21
avgvstvs

Câu hỏi hay! Cảm ơn bạn đã giải thích về mối đe dọa mà bạn đang cố gắng chống lại. Tôi đã chỉnh sửa câu trả lời của tôi cho phù hợp.

Tóm tắt. Phòng thủ chính của bạn phải là kiểm soát truy cập. Bạn cần giới hạn người dùng nào có thể xem trang nào. Chi tiết bên dưới.

Kiểm soát truy cập trong các ứng dụng web. Điều bạn cần làm là kiểm tra xem người dùng có được phép truy cập dữ liệu bạn sẽ hiển thị trên một trang không, trước khi cho phép họ xem dữ liệu đó. Điều này về cơ bản thuộc về kiểm soát truy cập: bạn muốn các điều khiển giới hạn người dùng nào có thể xem dữ liệu nào, dựa trên một số chính sách ủy quyền.

Có vẻ như bạn có một chuỗi các trang, mỗi trang cho một tác nhân:

http://www.example.com/agent/?producerId=12345
http://www.example.com/agent/?producerId=12346
http://www.example.com/agent/?producerId=12347
...

trong đó các nhà sản xuất (tác nhân) có khả năng đoán hoặc dự đoán được. Bạn muốn đảm bảo rằng đại lý 12345 có thể xem http://www.example.com/agent/?producerId=12345 nhưng không phải bất kỳ trang nào khác. ĐỒNG Ý.

Đây là một tình huống tiêu chuẩn không có thật, và phòng thủ tiêu chuẩn không có thật là: kiểm soát truy cập.

Để thực hiện kiểm soát truy cập, bạn mã hóa ứng dụng web để mỗi trang kiểm tra xem người dùng có được phép xem trang đó hay không trước khi cho phép người dùng xem trang đó. Ví dụ, đối với trang được liệt kê ở trên, logic triển khai trang đó sẽ kiểm tra danh tính của người dùng hiện đang đăng nhập. Nếu id của người dùng đã đăng nhập khớp với nhà sản xuất của tham số trang, thì bạn sẽ hiển thị cho họ thông tin. Nếu id không khớp, bạn không hiển thị cho họ thông tin: nếu đó là một người dùng khác, bạn sẽ hiển thị cho họ một trang lỗi (với thông tin về cách truy cập) hoặc nếu người dùng chưa đăng nhập, bạn chuyển hướng họ đến một trang đăng nhập.

Điều này sẽ không phá vỡ dấu trang. Nó không yêu cầu thay đổi cơ sở dữ liệu, thay đổi lớp liên tục hoặc kiểm soát truy cập dựa trên vai trò. Nó đòi hỏi bạn phải có cách tìm kiếm danh tính của người dùng hiện đang đăng nhập và liên kết với ID nhà cung cấp của họ. Ngoài ra, nếu bạn muốn cho phép người quản lý và người giám sát xem dữ liệu cho tất cả các đại lý khác, thì bạn cần một cách để tìm kiếm người dùng đang đăng nhập và xác định xem họ có phải là người quản lý hoặc người giám sát hay không. Nếu bạn muốn chỉ cho phép người quản lý/người giám sát của đại lý xem trang của họ (không phải tất cả người quản lý/người giám sát khác), thì bạn cần có cách để xác định người quản lý/người giám sát của mỗi đại lý. Đây là những yêu cầu khá cơ bản, tối thiểu; thật khó để thấy làm thế nào bạn có thể tránh chúng.

Như @symbcbean chỉ ra đúng, đây là một lỗi rất phổ biến thường thấy trong các ứng dụng web. Một ví dụ điển hình có thể là một trang web sử dụng một số giá trị tham số có thể đoán được để xác định tài nguyên và không xác thực đầy đủ người dùng. Chẳng hạn, giả sử các đơn hàng được gán một số thứ tự liên tiếp:

https://www.example.com/show_order.php?id=1234
https://www.example.com/show_order.php?id=1235
https://www.example.com/show_order.php?id=1236
...

và giả sử rằng bất kỳ ai biết URL đều có thể xem đơn hàng. Điều đó sẽ rất tệ, bởi vì điều đó có nghĩa là bất cứ ai biết (hoặc đoán) số thứ tự đều có thể xem đơn hàng, ngay cả khi họ không được phép làm như vậy. Đây là một trong Mười đầu của OWASP rủi ro bảo mật ứng dụng web: Tham chiếu đối tượng trực tiếp không an toàn . Để biết thêm thông tin, tôi khuyên bạn nên đọc các tài nguyên có sẵn trên OWASP. OWASP có rất nhiều tài nguyên tuyệt vời về bảo mật ứng dụng web.

Nhận xét khác. Những người khác đã đề xuất sử dụng SSL. Mặc dù điều đó sẽ không ngăn chặn việc giả mạo tham số, nhưng đó là một thực tiễn bảo mật tốt nói chung chống lại các loại vấn đề khác. Sử dụng SSL rất đơn giản: chỉ cần định cấu hình trang web của bạn để sử dụng https, thay vì http (và lý tưởng nhất là bật HSTS và đặt bit secure trên tất cả cookie).

Ngoài ra, thường là tốt hơn để tránh lưu trữ thông tin bí mật trong các tham số URL, tất cả những thứ khác đều bằng nhau. Bạn có thể lưu trữ thông tin bí mật ở trạng thái phiên hoặc trong cơ sở dữ liệu.

16
D.W.

Tóm lại: Không mã hóa các tham số URL, sử dụng tra cứu riêng .

Ngoài ra, sử dụng HTTPS về cơ bản là không thể thương lượng nếu bạn muốn bất kỳ biện pháp bảo mật ứng dụng web nào. Đó là bắt buộc trong năm 2015. Hãy thoải mái với TLS 1.1+.


Nhà phát triển muốn làm gì

What developers want to do

Thay vào đó, các nhà phát triển nên làm gì

enter image description here

7
Scott Arciszewski

nhưng chúng tôi cần phải ngăn các đại lý bán hàng nhìn trộm khách hàng tiềm năng của nhau

Điều này khá ngụ ý rằng máy khách là một trình duyệt - bạn có đang gửi khóa dưới dạng Cleartext tại một số điểm không?

Đa thức là chính xác, bạn nên sử dụng SSL. Điều đó sẽ không giải quyết vấn đề người dùng nhập các giá trị liền kề vào một URL trông giống như:

https://www.example.com/show_order.php?id=1234
https://www.example.com/show_order.php?id=1235
https://www.example.com/show_order.php?id=1236
...

Hoàn toàn có thể tạo phía máy chủ mã thông báo xác thực dựa trên các tham số phải được trình bày để xác thực yêu cầu. Lý tưởng nhất là bạn sẽ sử dụng mã xác thực tin nhắn (MAC) cho việc này, nhưng hàm băm cũng sẽ hoạt động nếu bạn cẩn thận. ví dụ. trong PHP ...

 print "<a href='show_order.php?id=" . $id . "&valid=" . md5($id . crypto_key()) . "'>...

Được xác nhận đơn giản bởi:

if ($_GET['valid'] != md5($_GET['id'] . crypto_key()) {
   die('not authorized');
}

Ở đây crypto_key() trả về khóa mật mã tĩnh (tạo nó bằng cách kéo, giả sử, 128 bit từ /dev/urandom Và lưu trữ nó trong cơ sở dữ liệu).

Nhưng bạn vẫn cần kiểm soát quyền truy cập vào mã tạo URL.

3
symcbean

Đây là giải pháp của tôi

$id=1234;
$en_id = encrypString( $id);

và tôi tạo url như

https://www.example.com/show_order.php?id=$en_id

url sẽ trông như thế nào

https://www.example.com/show_order.php?id=9muEYh4lShFDeCnXqoNpxucs42Fuz5Nexq1IUGWYEffffe88yRbJu

và mặt khác tôi giải mã

$en_id= decryptString($_GET['id']);

các chức năng cho tiền điện tử và giải mã là

function encrypString($plaintext) {
         # --- ENCRYPTION ---

        $key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");//change this

        # show key size use either 16, 24 or 32 byte keys for AES-128, 192
        # and 256 respectively
        $key_size =  strlen($key);
        //echo "Key size: " . $key_size . "\n";


        # create a random IV to use with CBC encoding
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);

        # creates a cipher text compatible with AES (Rijndael block size = 128)
        # to keep the text confidential 
        # only suitable for encoded input that never ends with value 00h
        # (because of default zero padding)
        $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,
                                     $plaintext, MCRYPT_MODE_CBC, $iv);

        # prepend the IV for it to be available for decryption
        $ciphertext = $iv . $ciphertext;

        # encode the resulting cipher text so it can be represented by a string
        $ciphertext_base64 = base64_encode($ciphertext);

        return  rawurlencode($ciphertext_base64);//important rawurlencode for + symbol in url

    }


decryptString($ciphertext_base64) {
        # --- DECRYPTION ---

        $key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");//change this

        # show key size use either 16, 24 or 32 byte keys for AES-128, 192
        # and 256 respectively
        $key_size =  strlen($key);
        //echo "Key size: " . $key_size . "\n";

        # create a random IV to use with CBC encoding
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);

        $ciphertext_dec = base64_decode($ciphertext_base64);

        # retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
        $iv_dec = substr($ciphertext_dec, 0, $iv_size);

        # retrieves the cipher text (everything except the $iv_size in the front)
        $ciphertext_dec = substr($ciphertext_dec, $iv_size);

        # may remove 00h valued characters from end of plain text
        $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key,
                                    $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);

        return rawurldecode($plaintext_dec);
    }
2
Valentin Ursuleac

Để tránh giả mạo tham số, tôi luôn gửi một hàm băm với các giá trị văn bản thuần túy.

ví dụ. lấy url này bạn muốn bảo mật

https://www.mysite.com/somepage?param1=abc&param2=xyz

Trên máy chủ, mô hình của bạn sẽ băm tất cả các giá trị url bằng một muối bí mật

string salt = "A1B2C3D4...";
string param1 = "abc";
string param2 = "xyz";
string hash = YourFavoriteHashingAlgorithm(param1 + param2 + salt);
// result hash = "Y83YMB38DX83YUHFIEIGKDHSEUG"

sau đó bạn gửi hàm băm này cùng với các giá trị url khác

https://www.mysite.com/somepage?param1=abc&param2=xyz&hash=Y83YMB38DX83YUHFIEIGKDHSEUG

Bây giờ, khi bạn nhận được yêu cầu tới URL này, bạn lại lấy các tham số bạn được trình bày và băm chúng thông qua cùng một thuật toán. Hàm băm bạn vừa tạo phải khớp với hàm băm bạn đang trình bày, nếu không hãy gửi cho họ kết quả 400 "Yêu cầu không hợp lệ"!.

Điều tuyệt vời là các thông số của bạn vẫn có thể đọc được bởi con người và tất cả logic xác thực hiện tại của bạn có thể giữ nguyên.

1
raterus

Không sử dụng đầu vào của người dùng

(vì bạn không chú ý tin tưởng nó)

Câu trả lời này mở rộng được chấp nhận với những gì có vẻ đơn giản hóa đáng kể.

Bây giờ, từ mô tả của bạn và từ sự hiểu biết tốt nhất của tôi , bạn đã nói rằng bạn muốn ngăn chặn Sales Agent A (cụ thể là 12345) để nhìn trộm Sales Agent B (s (cụ thể là 54321) dữ liệu.

Đơn giản, giết tham số agentId từ chuỗi truy vấn và lấy nó từ phiên

URL trở thành https://example.org/show_order.php

Trong nội bộ, ứng dụng phải trích xuất id đại lý bán hàng từ tiền gốc được lưu trữ trong phiên. Tôi quá mức hoen rỉ trong PHP vì vậy tôi sẽ sử dụng mã giả

SELECT * FROM sales where salesman_id = ?1;
[1 = getPrincipalSalesId()]

Truy vấn này đơn giản sẽ bỏ qua mọi thứ xuất phát từ máy khách. Nó không yêu cầu sửa đổi cho lớp kiên trì. Nó thậm chí không yêu cầu triển khai RBAC (kiểm soát truy cập dựa trên vai trò), nhưng mọi thứ đều liên quan đến chức năng đó getPrincipalSalesId.

Về cơ bản, đó là cùng một mã mà bạn sử dụng để tạo URL, nhưng lần này bạn đưa giá trị đó vào truy vấn, làm cho nó ngầm .

0