PDA

View Full Version : حفرهء امنیتی کدهای بدست آوردن IP واقعی کاربر



eshpilen
چهارشنبه 15 شهریور 1391, 12:53 عصر
اینطور کدها که IP رو از HTTP_CLIENT_IP و HTTP_X_FORWARDED_FOR (و امثالهم)، یعنی هدرهای HTTP، میگیرن، حفرهء امنیتی ایجاد میکنن:


<?php
function getRealIpAddr()
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) //check ip from share internet
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
else
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) //to check ip is pass from proxy
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
} else
{
$ip=$_SERVER['REMOTE_ADDR'];
} return $ip;
}
echo getRealIpAddr();
?>



این بحثو تو یه تاپیک دیگه پیگیری کنید. IP Spoofing به نظرم تنها مسئله امنیتی هست که میتونه تو این کد وجود داشته باشه....
تنها مسئلهء امنیتی؟ مگه باید مسئلهء امنیتی بیشتری باشه تا از این کد استفاده نکنیم؟
این کد اغلب برای محدود کردن دسترسی بر اساس IP استفاده میشه، درحالیکه هر سایتی که از این کد استفاده کنه این قابلیت رو به هکر میده که IP خودش رو براحتی جعل کنه.
خب با این حساب این کد دیگه به چه دردی میخوره؟
همون REMOTE_ADDR به تنهایی که بهتر بود! چون حداقل قابل جعل نیست و کاربر برای دور زدنش با نیاز و محدودیت پراکسی ها مواجه هست.

این کد فقط برای مقاصد Logging احتمالا بدرد میخوره و نه بیشتر. چون به IP هایی که بدست میده اطمینان کافی نیست از نظر امنیتی.
البته یک وقت هست که براتون حفاظت فقط در برابر کاربران عادی که نهایت استفاده از پراکسی و اینطور چیزها رو بلدن، و نه هکرهای واقعی، مهمه، اونوقت فرق میکنه.
وگرنه حدس زدن/تست کردن اینکه یه سایتی از این کد استفاده میکنه برای یک هکر حرفه ای اونقدر هم سخت نیست و براحتی هم میتونه ازش سوء استفاده کنه. گذشته از اینکه در پروژه های بازمتن قابل استفاده نیست. کلا هم که استفاده ازش باوجود آگاهی از این حفره، میشه «امنیت از طریق تیرگی».

Sajjad.Aghapour
پنج شنبه 16 شهریور 1391, 08:54 صبح
تنها مسئلهء امنیتی؟ مگه باید مسئلهء امنیتی بیشتری باشه تا از این کد استفاده نکنیم؟


شما که نمیخوای وقتی جایی برای SQL Injection نیست بیای و این سطح امنیتی رو اعمال کنی؟ یک تدبیر امنیتی لازم نیست مگر در جای خودش. اینجا Spoof کردن IP مطرح هست. اگر توی همین کد از متغیرهای CLIENT_IP و X_FORWARDED_FOR استفاده نمیشد بازهم این بحث رو ایجاد میکردی؟

eshpilen
پنج شنبه 16 شهریور 1391, 09:20 صبح
شما که نمیخوای وقتی جایی برای SQL Injection نیست بیای و این سطح امنیتی رو اعمال کنی؟ یک تدبیر امنیتی لازم نیست مگر در جای خودش. اینجا Spoof کردن IP مطرح هست. اگر توی همین کد از متغیرهای CLIENT_IP و X_FORWARDED_FOR استفاده نمیشد بازهم این بحث رو ایجاد میکردی؟

جان؟
اصلا متوجه نشدم چی میگی.
میشه واضحتر صحبت کنی؟
من دارم میگم جایی که از این کد برای تشخیص IP کاربر جهت اعمال محدودیت هایی استفاده بشه (مثلا محدودیت تعداد ثبت نام از یک IP در یک بازهء زمانی مشخص)، هکر میتونه ازش سوء استفاده کنه و اون محدودیت ها رو براحتی دور بزنه؛ چون میتونه با ست کردن هدرهای HTTP مربوطه، هر IP ای رو که میخواد بعنوان IP خودش معرفی کنه. اینطوری حتی میتونه خودش رو بجای کاربران دیگری جا بزنه و کاری کنه تا دسترسی اونا به سایت ما قطع بشه.

حالا چه ربطی به SQL Injection داره این مسئله؟ کدوم سطح امنیتی؟ اصلا متوجه نشدم.

این به تنهایی یک باگ امنیتی جدیه و برای باگ امنیتی بودن و سوء استفاده نیازی به SQL Injection نداره (البته امکان SQL Injection هم هست درصورتیکه برنامه نویس از این قضیه بی اطلاع بوده باشه و مقدار این فیلدها رو موقع درج در کوئری Escape نکرده باشه).
حالا قبلا هم گفتم اگر بنظر شما این امکان مهم نیست خب میتونید از همین کد استفاده کنید. ولی بهرحال این حفره واضح وجود داره و قابل سوء استفاده هست.


اگر توی همین کد از متغیرهای CLIENT_IP و X_FORWARDED_FOR استفاده نمیشد بازهم این بحث رو ایجاد میکردی؟نه چرا ایجاد کنم؟!
استفاده از این متغییرها که مقدار اونا از هدرهای HTTP گرفته میشه باعث میشه که هر کس بتونه حتی بدون استفاده از پراکسی یک IP جعلی به سیستم معرفی کنه بعنوان IP خودش. اونم هر IP دلخواه (نه IP واقعی هر پراکسی که در کنترل کاربر نیست).

اصلا منظورت مفهوم نیست. شاید قضیه رو متوجه نشدی.

Sajjad.Aghapour
پنج شنبه 16 شهریور 1391, 09:58 صبح
نه دوست عزیز. هدف من از من از بیان کردن SQL Injection مثال زدن برای حرف بعدیم بود که هر تدبیر امنیتی جایی و مکانی داره. و در کل حرف من جواب اون قسمت از حرف شما بود که نقل قول کرده بودم.

موفق باشید/

eshpilen
پنج شنبه 16 شهریور 1391, 11:20 صبح
ما که نفهمیدیم بالاخره شما با چی پس مخالفت داشتی.
این کد حفرهء امنیتی ایجاد میکنه. قبول داری؟ بقول خودت IP Spoofing.
پس ما اصلا برای چی ازش داریم استفاده میکنیم؟!
چون هدف ما پیدا کردن IP واقعی کاربر بوده برای کاربردهای امنیتی، که در نتیجه باید بهش اطمینان کافی وجود داشته باشه که جعلی نباشه.
اگر کاربرد امنیتی نبود خب فرق میکرد.

ضمنا شما فرمودی IP Spoofing تنها باگ این کده.
هم درسته و هم نادرست.
درسته چون هیچ کد SQL در این کد نیست.
نادرسته چون اون کسی که از این کد استفاده میکنه فکر میکنه حتما یک رشته با فرمت IP میگیره، بنابراین احتمال زیاد موقع درج اون رشته در کوئری های SQL اونو Escape نمیکنه.
اما مشکل همینجاست که با این کد، هکر میتونه دقیقا هر رشته ای رو با هر فرمتی، یعنی حتی شامل دستورات SQL و کاراکترهای خاص اون، بجای IP خودش ست کنه. بنابراین باگ SQL Injection هم در بقیه کد خیلی از استفاده کنندگان از این کد وجود خواهد داشت!

eshpilen
پنج شنبه 16 شهریور 1391, 22:29 عصر
اینطور کدها که IP رو از HTTP_CLIENT_IP و HTTP_X_FORWARDED_FOR (و امثالهم)، یعنی هدرهای HTTP، میگیرن، حفرهء امنیتی ایجاد میکنن:


<?php

function getRealIpAddr()
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) //check ip from share internet
{
$ip=$_SERVER['HTTP_CLIENT_IP'];
}
else
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) //to check ip is pass from proxy
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
} else
{
$ip=$_SERVER['REMOTE_ADDR'];
} return $ip;
}
echo 'Your real IP is: ', getRealIpAddr();

?>

این کد اغلب برای محدود کردن دسترسی بر اساس IP استفاده میشه، درحالیکه هر سایتی که از این کد استفاده کنه این قابلیت رو به هکر میده که IP خودش رو براحتی جعل کنه.
خب با این حساب این کد دیگه به چه دردی میخوره؟
همون REMOTE_ADDR به تنهایی که بهتر بود! چون حداقل قابل جعل نیست و حداقل کاربر برای دور زدنش با نیاز به و محدودیتهای پراکسی ها مواجه هست.

این کد فقط برای مقاصد Logging احتمالا بدرد میخوره و نه بیشتر. چون به IP هایی که بدست میده اطمینان کافی نیست از نظر امنیتی.
البته یک وقت هست که براتون حفاظت فقط در برابر کاربران عادی که نهایت استفاده از پراکسی و اینطور چیزها رو بلدن، و نه هکرهای واقعی، مهمه، اونوقت فرق میکنه.
وگرنه حدس زدن/تست کردن اینکه یه سایتی از این کد استفاده میکنه برای یک هکر حرفه ای اونقدر هم سخت نیست و براحتی هم میتونه ازش سوء استفاده کنه. گذشته از اینکه در پروژه های بازمتن قابل استفاده نیست. کلا هم که استفاده ازش باوجود آگاهی از این حفره، میشه «امنیت از طریق تیرگی».

جایی که از این کد برای تشخیص IP کاربر جهت اعمال محدودیت هایی استفاده بشه (مثلا محدودیت تعداد ثبت نام از یک IP در یک بازهء زمانی مشخص)، هکر میتونه ازش سوء استفاده کنه و اون محدودیت ها رو براحتی دور بزنه؛ چون میتونه با ست کردن هدرهای HTTP مربوطه، هر IP ای رو که میخواد بعنوان IP خودش معرفی کنه. اینطوری حتی میتونه خودش رو بجای کاربران دیگری جا بزنه و کاری کنه تا دسترسی اونا به سایت ما قطع بشه.
استفاده از این متغییرها که مقدار اونا از هدرهای HTTP گرفته میشه باعث میشه که هر کس بتونه حتی بدون استفاده از پراکسی یک IP جعلی به سیستم معرفی کنه بعنوان IP خودش. اونم هر IP دلخواه (نه IP واقعی هر پراکسی که در کنترل کاربر نیست).

ضمنا اون کسی که از این کد استفاده میکنه فکر میکنه حتما یک رشته با فرمت IP میگیره، بنابراین احتمال زیاد موقع درج اون رشته در کوئری های SQL اونو Escape نمیکنه.
اما مشکل همینجاست که با این کد، هکر میتونه دقیقا هر رشته ای رو با هر فرمتی، یعنی حتی شامل دستورات SQL و کاراکترهای خاص اون، بجای IP خودش ست کنه. بنابراین باگ SQL Injection هم در بقیه کد خیلی از استفاده کنندگان از این کد وجود خواهد داشت!

تست آنلاین هم صورت گرفت.
من نمونه اون کد حفره دار رو روی سایت خودم گذاشتم تا هرکس میخواد تست کنه: http://hamidreza-mz.tk/real_ip.php

با این کد هم میتونید جعل IP رو تست کنید:


<?php

$service_port = getservbyname('www', 'tcp');

$target='hamidreza-mz.tk';
$fake_ip='1.1.1.1';

$address = gethostbyname($target);

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket < 0) {
echo "socket_create() failed: reason: " . socket_strerror($socket) . "\n";
}

$result = socket_connect($socket, $address, $service_port);
if ($result < 0) {
echo "socket_connect() failed.\nReason: ($result) " . socket_strerror($result) . "\n";
}

$in = "GET /real_ip.php HTTP/1.1\r\n";
$in .= "Host: $target\r\n";
$in .= "X-FORWARDED-FOR: $fake_ip\r\n";
$in .= "X_FORWARDED_FOR: $fake_ip\r\n";
$in .= "CLIENT-IP: $fake_ip\r\n";
$in .= "CLIENT_IP: $fake_ip\r\n";
$in .= "Connection: Close\r\n\r\n";
$out = '';

socket_write($socket, $in, strlen($in));

header('Content-type: text/plain');

while ($out = socket_read($socket, 2048)) {
echo $out;
}

socket_close($socket);

?>

عملا با این حفره نه تنها جعل IP، بلکه SQL Injection و حتی در مواردی XSS میشه انجام داد. چون اون افرادی که از این کدها استفاده میکنن فکر میکنن حتما یک چیزی با فرمت IP دریافت میکنن، و در نتیجه نه موقع درج در کوئری اون رو Escape میکنن و نه موقع درج در متن صفحه HTML (مثلا موقع مشاهده لاگ ها از طریق اینترفیس برنامشون) اون رو مثلا از تابع htmlspecialchars عبور میدن.

amin1softco
شنبه 18 شهریور 1391, 21:47 عصر
درسته ولی در سیستم های حرفه ای تمام اطلاعات که قرار در دیتا بیس درج بشه اسکپ می شه برای مثال :


"regip" => $db->escape_string($user['regip']),

parsagw
یک شنبه 19 شهریور 1391, 09:24 صبح
مشکل sqli یه چیزه
جعل هویت یه چیز دیگه

در نهایت برای بدست آوردن IP چه کدی درست هست؟

eshpilen
یک شنبه 19 شهریور 1391, 11:53 صبح
در نهایت برای بدست آوردن IP چه کدی درست هست؟
استفاده از این متغییر حداقل اجازهء جعل هر IP یا رشتهء دلخواه رو نمیده:


$_SERVER['REMOTE_ADDR'];

ولی IP ای که دریافت میشه ممکنه IP یک پراکسی باشه. یعنی لزوما IP اصلی خود کاربر نیست، اما حداقل IP واقعی یک ماشینی هست که درخواست از طرف اون دریافت شده (میتونه پراکسی باشه، VPN باشه و غیره).
درکل من تاحالا راهی ندیدم که بشه IP اصلی کاربر رو بطور مطمئنی بدست آورد.
حالا شما پیدا کردید بذارید ما هم بررسیش کنیم.

amin1softco
یک شنبه 19 شهریور 1391, 15:26 عصر
راه درست و درمونی نیست ولی فک کنم این تقریباً بیشتر مسائل رو پوشش بده تا جایی که امکان داره البته:


function getRealIP(){
if( $_SERVER['HTTP_X_FORWARDED_FOR'] != '' ) {
$client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] :(( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : "unknown" );

// los proxys van añadiendo al final de esta cabecera
// las direcciones ip que van "ocultando". Para localizar la ip real
// del usuario se comienza a mirar por el principio hasta encontrar
// una dirección ip que no sea del rango privado. En caso de no
// encontrarse ninguna se toma como valor el REMOTE_ADDR

$entries = split('[, ]', $_SERVER['HTTP_X_FORWARDED_FOR']);
reset($entries);
while (list(, $entry) = each($entries)){
$entry = trim($entry);
if ( preg_match("/^([0-9]+.[0-9]+.[0-9]+.[0-9]+)/", $entry, $ip_list) ){
// http://www.faqs.org/rfcs/rfc1918.html
$private_ip = array(
'/^0./',
'/^127.0.0.1/',
'/^192.168..*/',
'/^172.((1[6-9])|(2[0-9])|(3[0-1]))..*/',
'/^10..*/');
$found_ip = preg_replace($private_ip, $client_ip, $ip_list[1]);
if ($client_ip != $found_ip){
$client_ip = $found_ip;
break;
}
}
}
} else {
$client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] : ( ( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : "unknown" );
if ($client_ip == 'unknown') {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) //check ip from share internet
{
$ip=$_SERVER['HTTP_CLIENT_IP'];}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) //to check ip is pass from proxy
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else {
$ip=$_SERVER['REMOTE_ADDR'];
}
$client_ip = $ip;
}
}
return $client_ip;
}


by: oliver leuyim angel

رضا قربانی
یک شنبه 19 شهریور 1391, 18:07 عصر
راه درست و درمونی نیست ولی فک کنم این تقریباً بیشتر مسائل رو پوشش بده تا جایی که امکان داره البته:


function getRealIP(){
if( $_SERVER['HTTP_X_FORWARDED_FOR'] != '' ) {
$client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] :(( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : "unknown" );

// los proxys van añadiendo al final de esta cabecera
// las direcciones ip que van "ocultando". Para localizar la ip real
// del usuario se comienza a mirar por el principio hasta encontrar
// una dirección ip que no sea del rango privado. En caso de no
// encontrarse ninguna se toma como valor el REMOTE_ADDR

$entries = split('[, ]', $_SERVER['HTTP_X_FORWARDED_FOR']);
reset($entries);
while (list(, $entry) = each($entries)){
$entry = trim($entry);
if ( preg_match("/^([0-9]+.[0-9]+.[0-9]+.[0-9]+)/", $entry, $ip_list) ){
// http://www.faqs.org/rfcs/rfc1918.html
$private_ip = array(
'/^0./',
'/^127.0.0.1/',
'/^192.168..*/',
'/^172.((1[6-9])|(2[0-9])|(3[0-1]))..*/',
'/^10..*/');
$found_ip = preg_replace($private_ip, $client_ip, $ip_list[1]);
if ($client_ip != $found_ip){
$client_ip = $found_ip;
break;
}
}
}
} else {
$client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] : ( ( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : "unknown" );
if ($client_ip == 'unknown') {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) //check ip from share internet
{
$ip=$_SERVER['HTTP_CLIENT_IP'];}
elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) //to check ip is pass from proxy
{
$ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
}
else {
$ip=$_SERVER['REMOTE_ADDR'];
}
$client_ip = $ip;
}
}
return $client_ip;
}


by: oliver leuyim angel
خب چه کاریه با این همه کد . ما که هر کاری کنیم آی پی عوض میشه و نمی تونیم آی پی درست رو بگیریم . همین $_SERVER['REMOTE_ADDR'] رو موقع درج در بانک از طریق توابع امنیتی پی اچ پی عبور میدیم .من دو سال پیش که درگیر گرفتن آی پی کاربر بودم به این نتیجه رسیدم.

جدا از این بحث ها خود سرور هم در پنل آی پی بازدید کنندگان و تاریخ و ساعت بازدید و ... رو درست و درمون میگیره که اونم باز می تونه عوض شده باشه.

حرف دوستان هم اینه که موقع درج در بانک باید فیلتر بشه (حالا یا ریپلیس کردن یا رگولار اکسپرشن یا توابع پیشفرض ...)