security - मैं PHP में उपयोगकर्ता लॉगिन प्रयासों को कैसे थ्रोट कर सकता हूं




throttling honeypot (8)

cballuo एक उत्कृष्ट जवाब प्रदान किया। मैं सिर्फ एक अद्यतन संस्करण प्रदान करके पक्ष को वापस करना चाहता था जो mysqli का समर्थन करता है। मैंने एसक्यूएल और अन्य छोटी चीजों में टेबल / फ़ील्ड कॉलम को थोड़ा बदल दिया लेकिन इसे किसी को भी mysqli समकक्ष की तलाश करने में मदद करनी चाहिए।

function get_multiple_rows($result) {
  $rows = array();
  while($row = $result->fetch_assoc()) {
    $rows[] = $row;
  }
  return $rows;
}

$throttle = array(10 => 1, 20 => 2, 30 => 5);

$query = "SELECT MAX(time) AS attempted FROM failed_logins";    

if ($result = $mysqli->query($query)) {

    $rows = get_multiple_rows($result);

$result->free();

$latest_attempt = (int) date('U', strtotime($rows[0]['attempted'])); 

$query = "SELECT COUNT(1) AS failed FROM failed_logins WHERE time > DATE_SUB(NOW(), 
INTERVAL 15 minute)";   

if ($result = $mysqli->query($query)) {

$rows = get_multiple_rows($result);

$result->free();

    $failed_attempts = (int) $rows[0]['failed'];

    krsort($throttle);
    foreach ($throttle as $attempts => $delay) {
        if ($failed_attempts > $attempts) {
                echo $failed_attempts;
                $remaining_delay = (time() - $latest_attempt) - $delay;

                if ($remaining_delay < 0) {
                echo 'You must wait ' . abs($remaining_delay) . ' seconds before your next login attempt';
                }                

            break;
        }
     }        
  }
}

मैं सिर्फ इस पोस्ट को पढ़ रहा था रैपिड-फायर लॉगिन प्रयासों को रोकने पर फॉर्म-आधारित वेबसाइट प्रमाणीकरण के लिए निश्चित मार्गदर्शिका

सर्वोत्तम अभ्यास # 1: एक छोटी सी देरी जो असफल प्रयासों की संख्या के साथ बढ़ जाती है, जैसे:

1 विफल प्रयास = कोई देरी नहीं
2 विफल प्रयास = 2 सेकंड देरी
3 विफल प्रयास = 4 सेकंड देरी
4 विफल प्रयास = 8 सेकंड देरी
5 विफल प्रयास = 16 सेकंड देरी
आदि।

इस योजना पर हमला करने वाले डीओएस बहुत अव्यवहारिक होंगे, लेकिन दूसरी ओर, संभावित रूप से विनाशकारी, क्योंकि देरी तेजी से बढ़ जाती है।

मैं उत्सुक हूं कि मैं PHP में अपने लॉगिन सिस्टम के लिए ऐसा कुछ कैसे कार्यान्वित कर सकता हूं?


आईएमएचओ, डीओएस हमलों के खिलाफ रक्षा वेब सर्वर स्तर (या यहां तक ​​कि नेटवर्क हार्डवेयर में भी) पर बेहतर तरीके से निपटाई जाती है, न कि आपके PHP कोड में।


आपके पास तीन बुनियादी दृष्टिकोण हैं: स्टोर सत्र की जानकारी, स्टोर कुकी जानकारी या स्टोर आईपी जानकारी।

यदि आप सत्र की जानकारी का उपयोग करते हैं तो अंतिम उपयोगकर्ता (हमलावर) जबरन नए सत्रों का आह्वान कर सकता है, अपनी रणनीति को बाईपास कर सकता है, और फिर बिना देरी के फिर से लॉगिन कर सकता है। सत्र लागू करने के लिए बहुत ही सरल हैं, बस उपयोगकर्ता के अंतिम ज्ञात लॉगिन समय को सत्र चर में संग्रहीत करें, वर्तमान समय के साथ मेल करें, और सुनिश्चित करें कि देरी काफी लंबी हो गई है।

यदि आप कुकीज़ का उपयोग करते हैं, तो हमलावर कुकीज़ को अस्वीकार कर सकता है, सब कुछ, यह वास्तव में कुछ व्यवहार्य नहीं है।

यदि आप आईपी पते ट्रैक करते हैं तो आपको किसी भी तरह से डेटाबेस में किसी भी तरह से आईपी पते से लॉगिन प्रयासों को स्टोर करने की आवश्यकता होगी। जब कोई उपयोगकर्ता लॉग ऑन करने का प्रयास करता है, तो बस अपनी आईपी की रिकॉर्ड की गई सूची अपडेट करें। आपको इस तालिका को एक उचित अंतराल पर पंप करना चाहिए, आईपी पते डंप करना जो कुछ समय में सक्रिय नहीं है। Pitfall (हमेशा एक pitfall है), यह है कि कुछ उपयोगकर्ता एक आईपी पता साझा करना समाप्त हो सकता है, और सीमा स्थितियों में आपकी देरी उपयोगकर्ताओं को अनजाने में प्रभावित कर सकती है। चूंकि आप असफल लॉग इन ट्रैक कर रहे हैं, और केवल लॉग इन विफल हैं, इससे बहुत अधिक दर्द नहीं हो सकता है।


इस मामले में कुकीज़ या सत्र-आधारित विधियां निश्चित रूप से बेकार हैं। आवेदन को पिछले लॉगिन प्रयासों के आईपी पते या टाइमस्टैम्प (या दोनों) की जांच करनी है।

एक आईपी चेक को बाईपास किया जा सकता है यदि हमलावर के पास उसके अनुरोधों को शुरू करने के लिए एक से अधिक आईपी हैं और यदि एकाधिक उपयोगकर्ता एक ही आईपी से आपके सर्वर से कनेक्ट होते हैं तो परेशानी हो सकती है। बाद के मामले में, कई बार लॉगिन करने में विफल होने वाले प्रत्येक व्यक्ति को एक ही आईपी को उस निश्चित उपयोगकर्ता के साथ लॉग इन करने से रोकना होगा।

एक टाइमस्टैम्प चेक में उपरोक्त की तरह ही समस्या है: हर कोई किसी अन्य खाते को किसी विशेष खाते में लॉग इन करने से रोक सकता है। आखिरी प्रयास के लिए लंबे इंतजार के बजाय कैप्चा का उपयोग करना शायद एक अच्छा कामकाज है।

लॉगिन प्रणाली को रोकने की एकमात्र अतिरिक्त चीजें प्रयास जांच कार्य पर दौड़ की स्थिति हैं। उदाहरण के लिए, निम्नलिखित छद्म कोड में

$time = get_latest_attempt_timestamp($username);
$attempts = get_latest_attempt_number($username);

if (is_valid_request($time, $attempts)) {
    do_login($username, $password);
} else {
    increment_attempt_number($username);
    display_error($attempts);
}

क्या होता है यदि कोई हमलावर लॉगिन पृष्ठ पर एक साथ अनुरोध भेजता है? शायद सभी अनुरोध एक ही प्राथमिकता पर चलेंगे, और संभावना है कि दूसरों को दूसरी पंक्ति से पहले होने से पहले increment_attempt_number निर्देश के लिए कोई अनुरोध नहीं मिलता है। तो हर अनुरोध एक ही $ समय और $ प्रयास मान प्राप्त करता है और निष्पादित किया जाता है। इस तरह के सुरक्षा मुद्दों को रोकने से जटिल अनुप्रयोगों के लिए मुश्किल हो सकती है और इसमें निश्चित रूप से एप्लिकेशन को धीमा कर डेटाबेस के कुछ तालिकाओं / पंक्तियों को लॉक और अनलॉक करना शामिल है।


मैं आमतौर पर लॉगिन इतिहास और लॉगिन प्रयास टेबल बनाते हैं। प्रयास तालिका उपयोगकर्ता नाम, पासवर्ड, आईपी पता, आदि लॉग करेगी, यह देखने के लिए कि क्या आपको देरी की आवश्यकता है या नहीं। मैं किसी दिए गए समय (उदाहरण के लिए एक घंटा) में 20 से अधिक प्रयासों के लिए पूरी तरह से अवरुद्ध करने की अनुशंसा करता हूं।


लॉगिन प्रक्रिया को सफल और असफल लॉगिन दोनों के लिए अपनी गति को कम करने की आवश्यकता है। लॉगिन प्रयास स्वयं को लगभग 1 सेकंड से अधिक तेज नहीं होना चाहिए। यदि ऐसा है, तो ब्रूट फोर्स यह जानने के लिए देरी का उपयोग करता है कि प्रयास विफल रहा क्योंकि सफलता विफलता से कम है। फिर, प्रति सेकंड अधिक संयोजन का मूल्यांकन किया जा सकता है।

प्रति मशीन के साथ-साथ लॉगिन प्रयासों की संख्या लोड बैलेंसर द्वारा सीमित की जानी चाहिए। अंत में, आपको केवल एक ट्रैक करने की आवश्यकता है कि एक ही उपयोगकर्ता या पासवर्ड का उपयोग एक से अधिक उपयोगकर्ता / पासवर्ड लॉगिन प्रयास द्वारा किया जाता है। मनुष्य प्रति मिनट लगभग 200 शब्दों से अधिक तेज़ टाइप नहीं कर सकते हैं। तो, मशीनों के एक सेट से 200 मिनट प्रति मिनट से अधिक तेज़ या एक साथ लॉगिन प्रयास तेजी से प्रयास करते हैं। इस प्रकार उन्हें एक ब्लैक लिस्ट में सुरक्षित रूप से पाइप किया जा सकता है क्योंकि यह आपका ग्राहक नहीं है। प्रति मेजबान ब्लैक लिस्ट टाइम्स को लगभग 1 सेकंड से अधिक होने की आवश्यकता नहीं है। यह कभी भी मानव की असुविधा नहीं करेगा, लेकिन एक क्रूर बल के प्रयास के साथ विनाश करता है चाहे धारावाहिक या समांतर में।

प्रति सेकंड एक संयोजन में 2 * 10 ^ 1 9 संयोजन, 4 बिलियन अलग-अलग आईपी पते पर समानांतर में चलने वाले, खोज स्थान के रूप में निकास के लिए 158 वर्ष लगेंगे। 4 अरब हमलावरों के खिलाफ प्रति उपयोगकर्ता एक दिन तक पहुंचने के लिए, आपको कम से कम 9 स्थानों पर पूरी तरह से यादृच्छिक अल्फान्यूमेरिक पासवर्ड की आवश्यकता होती है। पास वाक्यांशों में प्रशिक्षण उपयोगकर्ताओं को कम से कम 13 स्थानों तक, 1.7 * 10 ^ 20 संयोजनों पर विचार करें।

यह देरी, हमलावर को आपकी साइट के बलपूर्वक बल की बजाय अपनी पासवर्ड हैश फ़ाइल चोरी करने के लिए प्रेरित करेगी। अनुमोदित, नामित, हैशिंग तकनीक का प्रयोग करें। एक सेकंड के लिए इंटरनेट आईपी की पूरी आबादी पर प्रतिबंध लगाने से, समानांतर हमलों के प्रभाव को सीमित कर दिया जाएगा, जो मानव की सराहना करता है। अंत में, यदि आपका सिस्टम प्रतिबंध प्रणाली के कुछ प्रतिक्रिया के बिना 1000 से अधिक असफल लॉगऑन प्रयासों को एक सेकंड में अनुमति देता है, तो आपकी सुरक्षा योजनाओं पर काम करने के लिए बड़ी समस्याएं हैं। सबसे पहले उस स्वचालित प्रतिक्रिया को ठीक करें।


स्टोर आईपी द्वारा डेटाबेस में विफल प्रयासों। (चूंकि आपके पास लॉगिन सिस्टम है, इसलिए मुझे लगता है कि आपको यह पता होना चाहिए कि यह कैसे करें।)

जाहिर है, सत्र एक मोहक तरीका है, लेकिन वास्तव में समर्पित कोई भी आसानी से महसूस कर सकता है कि वे आसानी से थ्रॉटल को रोकने के लिए विफल प्रयासों पर अपने सत्र कुकी को हटा सकते हैं।

लॉग इन करने के प्रयास पर, कितने हालिया (कहें, आखिरी 15 मिनट) लॉगिन प्रयास थे, और नवीनतम प्रयास का समय।

$failed_attempts = 3; // for example
$latest_attempt = 1263874972; // again, for example
$delay_in_seconds = pow(2, $failed_attempts); // that's 2 to the $failed_attempts power
$remaining_delay = time() - $latest_attempt - $delay_in_seconds;
if($remaining_delay > 0) {
    echo "Wait $remaining_delay more seconds, silly!";
}

आप केवल एक आईपी या उपयोगकर्ता नाम पर थ्रॉटलिंग को चेन करके डीओएस हमलों को रोक नहीं सकते हैं। नरक, आप वास्तव में इस विधि का उपयोग कर तेजी से आग लॉगिन प्रयासों को भी रोक नहीं सकते हैं।

क्यूं कर? चूंकि हमले आपके थ्रॉटलिंग प्रयासों को छोड़ने के लिए कई आईपी और उपयोगकर्ता खातों का विस्तार कर सकते हैं।

मैंने कहीं और पोस्ट किया है, आदर्श रूप से आपको साइट पर सभी असफल लॉगिन प्रयासों को ट्रैक करना चाहिए और उन्हें टाइमस्टैंप से जोड़ना चाहिए, शायद:

CREATE TABLE failed_logins (
    id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(16) NOT NULL,
    ip_address INT(11) UNSIGNED NOT NULL,
    attempted DATETIME NOT NULL,
    INDEX `attempted_idx` (`attempted`)
) engine=InnoDB charset=UTF8;

Ip_address फ़ील्ड पर एक त्वरित नोट: आप डेटा को स्टोर कर सकते हैं और INET_ATON () और INET_NTOA () के साथ क्रमशः डेटा पुनर्प्राप्त कर सकते हैं, जो अनिवार्य रूप से एक हस्ताक्षरित पूर्णांक से और आईपी पते को परिवर्तित करने के समान होता है।

# example of insertion
INSERT INTO failed_logins SET username = 'example', ip_address = INET_ATON('192.168.0.1'), attempted = CURRENT_TIMESTAMP;
# example of selection
SELECT id, username, INET_NTOA(ip_address) AS ip_address, attempted;

किसी दिए गए समय में असफल लॉग इन की कुल संख्या (इस उदाहरण में 15 मिनट) के आधार पर कुछ देरी सीमाओं पर निर्णय लें। आपको इसे अपनी failed_logins तालिका से निकाले गए सांख्यिकीय डेटा पर आधार देना चाहिए क्योंकि यह उपयोगकर्ताओं की संख्या के आधार पर समय के साथ बदल जाएगा और उनमें से कितने लोग अपना पासवर्ड याद कर सकते हैं (और टाइप करें)।

> 10 failed attempts = 1 second
> 20 failed attempts = 2 seconds
> 30 failed attempts = reCaptcha

किसी दिए गए अवधि के लिए असफल लॉगिन की संख्या खोजने के लिए प्रत्येक असफल लॉगिन प्रयास पर तालिका को क्वेरी करें, 15 मिनट कहें:

SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute);

यदि दी गई अवधि के दौरान प्रयासों की संख्या आपकी सीमा से अधिक है, या तो थ्रॉटलिंग को लागू करें या सभी उपयोगकर्ता को कैप्चा (यानी रीकैप्चा) का उपयोग करने के लिए मजबूर करें, जब तक कि दी गई समयावधि में असफल प्रयासों की संख्या थ्रेसहोल्ड से कम न हो।

// array of throttling
$throttle = array(10 => 1, 20 => 2, 30 => 'recaptcha');

// retrieve the latest failed login attempts
$sql = 'SELECT MAX(attempted) AS attempted FROM failed_logins';
$result = mysql_query($sql);
if (mysql_affected_rows($result) > 0) {
    $row = mysql_fetch_assoc($result);

    $latest_attempt = (int) date('U', strtotime($row['attempted']));

    // get the number of failed attempts
    $sql = 'SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute)';
    $result = mysql_query($sql);
    if (mysql_affected_rows($result) > 0) {
        // get the returned row
        $row = mysql_fetch_assoc($result);
        $failed_attempts = (int) $row['failed'];

        // assume the number of failed attempts was stored in $failed_attempts
        krsort($throttle);
        foreach ($throttle as $attempts => $delay) {
            if ($failed_attempts > $attempts) {
                // we need to throttle based on delay
                if (is_numeric($delay)) {
                    $remaining_delay = time() - $latest_attempt - $delay;
                    // output remaining delay
                    echo 'You must wait ' . $remaining_delay . ' seconds before your next login attempt';
                } else {
                    // code to display recaptcha on login form goes here
                }
                break;
            }
        }        
    }
}

एक निश्चित दहलीज पर रीकैप्चा का उपयोग करना सुनिश्चित करेगा कि एकाधिक मोर्चों से हमला बंद कर दिया जाएगा और सामान्य साइट उपयोगकर्ताओं को वैध असफल लॉगिन प्रयासों के लिए महत्वपूर्ण देरी का अनुभव नहीं होगा।





honeypot