درخواستی که به برنامه شما می‌رسد دو نوع شاهد نشانی حمل می‌کند: همتایی که اتصال شبکه را باز کرده است، و یک فیلد HTTP که نشانی یک کلاینت پیشین را ادعا می‌کند. این دو به پرسش‌های متفاوتی پاسخ می‌دهند. همتای سوکت مشاهده می‌شود، در حالی که سرآیند را هر کس آن را نوشته ادعا می‌کند، و هیچ‌کدام نباید در یک گزارش یا قاعده دسترسی بی‌صدا جای دیگری را بگیرد. مقایسه زیر آن دو را جدا نگه می‌دارد و سپس تنها آرایشی را نشان می‌دهد که در آن سرآیند کاربردپذیر می‌شود.

پیش از مقایسه هر چیز، مسیر را توصیف کنید:

Client -> approved reverse proxy -> application origin

در این آرایش، همتای سوکت مبدأ معمولاً همان واسط است، و یک سرآیند ارسال می‌تواند ادعای واسط درباره کلاینت را حمل کند. ارزش آن از رابطه کنترل‌شده با همان واسط می‌آید، نه از نام سرآیند.

دو نوع شاهد نشانی

همتای سوکت واقعیتی درباره یک اتصال TCP است: چیزی از آن نشانی دست‌دادن را کامل کرده است. لزوماً همان انسان، دستگاه یا حسابی که برای شما مهم است نیست، زیرا یک VPN یا خروجی Tor می‌تواند به‌طور مشروع همان منبعی باشد که یک سرویس مشاهده می‌کند. با این حال، این تنها نشانی است که خود میزبان تأیید کرده است، و تمام ارزش آن همین است.

فیلد ارسال فراداده‌ای درباره یک زنجیره است. X-Forwarded-For، X-Real-IP و فیلد استانداردشده Forwarded را هر کلاینتی می‌تواند بنویسد که به نقطه پایانی‌ای برسد که آن‌ها را بازمی‌تاباند، بنابراین اعتبارشان کاملاً به این بستگی دارد که به چه کسی اجازه تنظیم آن‌ها داده شده است.

هر دو را در بافت درخواست با نام‌های جداگانه نگه دارید. برنامه‌ای که همتا را با سرآیند بازنویسی می‌کند توانایی بازسازی بعدی یک خطای اعتماد را از دست می‌دهد، و این دقیقاً همان زمانی است که به آن نیاز دارید.

سرآیند هرگز اتصال را تغییر مسیر نمی‌دهد

نوشتن نشانی در یک فیلد ارسال، منبع اتصال TCP رسیده به سرور را تغییر نمی‌دهد و مسیریابی را در هیچ نقطه‌ای از مسیر تغییر نمی‌دهد. مسیریابی معمول VPN یا NAT مبدأ ترافیک را بدون هیچ سرآیند HTTP تحویل می‌دهد. به همین ترتیب، یک دروازه ترانزیت تبادل HTTPS درست‌محافظت‌شده را بدون تغییر آرایش نقطه پایانی TLS بازنویسی نمی‌کند.

اشتباه معکوس به همان اندازه رایج است: HTTPS یک سرآیند ارسال ارائه‌شده توسط کلاینت را راستگو نمی‌کند. این پروتکل انتقال یک ادعای نادرست را دقیقاً به همان خوبی یک ادعای درست محافظت می‌کند.

اگر همکاری پیشنهاد تغییر سرآیند را برای رفع مشکل مسیریابی بدهد، تشخیص از پیش اشتباه رفته است. مسیر را درست کنید، یا پیکربندی واسطی را که سرآیند را می‌نویسد درست کنید.

واسطی را که مجاز به ادعا است نام ببرید

دو قاعده یک فیلد ارسال را کاربردپذیر می‌کند، و هر دو پیکربندی هستند نه کد. لبه هر چیزی را که از بیرون می‌رسد، بر پایه رفتار مستند خودش برای آن فیلدها، بهنجار می‌کند یا دور می‌ریزد. سپس مبدأ نشانی جانشین را از مسیر واسطی که نام بردید و از هیچ جای دیگر نمی‌گیرد. قاعده دوم تنها تا زمانی برقرار است که کلاینت‌ها نتوانند از راه دیگری به مبدأ برسند، زیرا مبدأیی که هم به درخواست‌های مستقیم و هم به درخواست‌های عبوری از واسط پاسخ می‌دهد نمی‌داند کدام‌یک را سرویس می‌دهد.

ماژول IP واقعی NGINX تقسیم را به‌روشنی نشان می‌دهد. set_real_ip_from فرستندگانی را نام می‌برد که ادعایشان به حساب می‌آید، و real_ip_header می‌گوید کدام فیلد آن را حمل می‌کند؛ اولی اعتماد را انجام می‌دهد، دومی فقط می‌گوید کجا را ببینید.

set_real_ip_from 10.20.0.0/24;
real_ip_header X-Forwarded-For;
# real_ip_recursive off - take the rightmost value in the field
# real_ip_recursive on  - walk the list back past every trusted address

یک دستور انتخاب سرآیند به‌تنهایی هنوز هیچ چیز درباره اینکه چه کسی مقدار را فرستاده است اثبات نمی‌کند.

امنیت انتقال باید تمام مسیر میان اجزایی را که به آن‌ها اعتماد دارید بپیماید. لبه‌ای که درست بهنجار می‌کند آن ارزش را همان لحظه از دست می‌دهد که یک پرش بی‌محافظ در پایین‌دست به کس دیگری اجازه می‌دهد ادعا را در حین سفر ویرایش کند.

زنجیره‌های چندپرشی را آگاهانه مدیریت کنید

چند واسط به مدل زنجیره صریح نیاز دارند. سمت چپ‌ترین یا راست‌ترین مقدار جداشده با کاما را صرفاً به این دلیل که یک نوشته وبلاگی چنین کرده است برندارید؛ دریابید کدام یک از واسط‌های شما می‌افزاید، کدام جایگزین می‌کند و کدام فیلد ورودی را نگه می‌دارد. مثلاً NGINX می‌تواند فهرست را در برابر مجموعه مورداعتمادش به عقب بپیماید و در نخستین نشانی‌ای که مالکش نیست بایستد، و این قاعده همان محصول است نه قانونی که هر چارچوبی از آن پیروی کند. مستندات واسط خودتان را با همان دو پرسش بخوانید: کدام مقدار را انتخاب می‌کند، و به کدام فرستندگان باور دارد؟

زنجیره را بنویسید: ترتیب پرش‌ها، کدام پرش فیلد را تعیین می‌کند و کدام پرش نسخه‌های ورودی را حذف می‌کند. سپس آن یادداشت را کنار پیکربندی واسط نگه دارید، نه فقط در تیکت. سپس آن را با فرستادن درخواستی با سرآیند جعلی از طریق فرایند اعتبارسنجی تأییدشده و تأیید اینکه مبدأ آن را نادیده می‌گیرد بیازمایید.

وقتی پرشی می‌افزایید، آن سند را دوباره بررسی کنید. یک متعادل‌کننده بار جدید که جلوی واسط مورداعتماد گذاشته می‌شود می‌تواند بدون ویرایش برنامه توسط کسی، تعیین کند کدام نشانی برنده می‌شود.

هر دو مشاهده را در گزارش نگه دارید

آنجا که تشخیص مجاز است، همتای اتصال و نشانی بهنجارشده کلاینت را به‌عنوان دو فیلد جداگانه ثبت کنید و تنها فراداده‌های ارسالی را که هدف واقعاً نیاز دارد بیفزایید. سپس استقرار را با دو درخواست اثبات کنید: یکی از طریق واسط که باید هر دو مقدار را به‌طور معقول ثبت کند، و تلاشی مستقیم به مبدأ که باید رد شود.

به یاد داشته باشید این جفت چه معنایی دارد. سرآیند زمینه‌ای را می‌افزاید که جزئی انتخاب‌شده برای اعتماد فراهم می‌کند؛ همتا تنها نشانی باقی می‌ماند که میزبان شما واقعاً دیده است. هیچ‌کدام اثبات هویت فردی نیست، و طراحی‌ای که هر یک را اثبات بداند درباره کسی اشتباه خواهد کرد.

مقالات مرتبط