Botların Web Sunucumu Öldürmesini Durdurmam Gerekiyor


9

Hosting hesabımdaki bazı web sitelerimde EXTREME bot sorunları yaşıyorum. Botlar, tüm hosting hesabım için CPU kaynaklarının% 98'inden ve bant genişliğimin% 99'undan fazlasını kullanıyor. Bu botlar, sitelerim için saatte 1 GB'tan fazla trafik üretiyor . Tüm bu siteler için gerçek insan trafiği ayda 100 MB'den azdır .

Bu botları engellemek için robots.txt ve .htaccess dosyalarında kapsamlı araştırmalar yaptım ancak tüm yöntemler başarısız oldu.

Ayrıca komut dosyaları dizinlerine erişimi engellemek için robots.txt dosyalarına kod koydum, ancak bu botlar (Google, MS Bing ve Yahoo) kuralları görmezden gelir ve komut dosyalarını yine de çalıştırır.

Google, MS Bing ve Yahoo botlarını tamamen engellemek istemiyorum, ancak orada tarama hızını sınırlamak istiyorum. Ayrıca, robots.txt dosyasına bir Tarama gecikmesi ifadesi eklemek botları yavaşlatmaz. Tüm siteler için mevcut robots.txt ve .htacces kodum aşağıda belirtilmiştir.

Tarama hızını mutlak minimum seviyeye düşürmek için hem Microsoft hem de Google web yöneticisi araçlarını ayarladım, ancak yine de bu sitelere saniyede 10 tıklama hızında vuruyorlar.

Buna ek olarak, bir hataya neden olan her dosya yüklediğimde, VPS web sunucusu tüm saniyeler içinde kapanıyor, böylece siteye erişemiyorum bile, bu botların isabetlerinden dolayı sorunu düzelttim.

Web sitelerime gelen trafiği yerinde durdurmak için ne yapabilirim?

Web barındırma şirketimi (site5.com) geçen aylarda bu konuda birçok kez sormaya çalıştım ve bu sorunla ilgili bana yardımcı olamıyorlar.

Gerçekten ihtiyacım olan Botların rss2html.php betiğini çalıştırmasını engellemektir. Hem oturumları hem de çerezleri denedim ve ikisi de başarısız oldu.

robots.txt

User-agent: Mediapartners-Google
Disallow: 
User-agent: Googlebot
Disallow: 
User-agent: Adsbot-Google
Disallow: 
User-agent: Googlebot-Image
Disallow: 
User-agent: Googlebot-Mobile
Disallow: 
User-agent: MSNBot
Disallow: 
User-agent: bingbot
Disallow: 
User-agent: Slurp
Disallow: 
User-Agent: Yahoo! Slurp
Disallow: 
# Directories
User-agent: *
Disallow: /
Disallow: /cgi-bin/
Disallow: /ads/
Disallow: /assets/
Disallow: /cgi-bin/
Disallow: /phone/
Disallow: /scripts/
# Files
Disallow: /ads/random_ads.php
Disallow: /scripts/rss2html.php
Disallow: /scripts/search_terms.php
Disallow: /scripts/template.html
Disallow: /scripts/template_mobile.html

.htaccess

ErrorDocument 400 http://english-1329329990.spampoison.com
ErrorDocument 401 http://english-1329329990.spampoison.com
ErrorDocument 403 http://english-1329329990.spampoison.com
ErrorDocument 404 /index.php
SetEnvIfNoCase User-Agent "^Yandex*" bad_bot
SetEnvIfNoCase User-Agent "^baidu*" bad_bot
Order Deny,Allow
Deny from env=bad_bot
RewriteEngine on
RewriteCond %{HTTP_user_agent} bot\* [OR]
RewriteCond %{HTTP_user_agent} \*bot
RewriteRule ^.*$ http://english-1329329990.spampoison.com [R,L]
RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|\%3D) [OR]
# Block out any script trying to base64_encode crap to send via URL
RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [OR]
# Block out any script that includes a <script> tag in URL
RewriteCond %{QUERY_STRING} (\<|%3C).*script.*(\>|%3E) [NC,OR]
# Block out any script trying to set a PHP GLOBALS variable via URL
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
# Block out any script trying to modify a _REQUEST variable via URL
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
# Send all blocked request to homepage with 403 Forbidden error!
RewriteRule ^(.*)$ index.php [F,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/index.php
RewriteCond %{REQUEST_URI} (/|\.php|\.html|\.htm|\.feed|\.pdf|\.raw|/[^.]*)$  [NC]
RewriteRule (.*) index.php
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
# Don't show directory listings for directories that do not contain an index file (index.php, default.asp etc.)
Options -Indexes
<Files http://english-1329329990.spampoison.com>
order allow,deny
allow from all
</Files>
deny from 108.
deny from 123.
deny from 180.
deny from 100.43.83.132

EK KULLANICI AJAN BOT KONTROL KODUNU GÖSTERMEK İÇİN GÜNCELLEME

<?php
function botcheck(){
 $spiders = array(
   array('AdsBot-Google','google.com'),
   array('Googlebot','google.com'),
   array('Googlebot-Image','google.com'),
   array('Googlebot-Mobile','google.com'),
   array('Mediapartners','google.com'),
   array('Mediapartners-Google','google.com'),
   array('msnbot','search.msn.com'),
   array('bingbot','bing.com'),
   array('Slurp','help.yahoo.com'),
   array('Yahoo! Slurp','help.yahoo.com')
 );
 $useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
 foreach($spiders as $bot) {
   if(preg_match("/$bot[0]/i",$useragent)){
     $ipaddress = $_SERVER['REMOTE_ADDR']; 
     $hostname = gethostbyaddr($ipaddress);
     $iphostname = gethostbyname($hostname);
     if (preg_match("/$bot[1]/i",$hostname) && $ipaddress == $iphostname){return true;}
   }
 }
}
if(botcheck() == false) {
  // User Login - Read Cookie values
     $username = $_COOKIE['username'];
     $password = $_COOKIE['password'];
     $radio_1 = $_COOKIE['radio_1'];
     $radio_2 = $_COOKIE['radio_2'];
     if (($username == 'm3s36G6S9v' && $password == 'S4er5h8QN2') || ($radio_1 == '2' && $radio_2 == '5')) {
     } else {
       $selected_username = $_POST['username'];
       $selected_password = $_POST['password'];
       $selected_radio_1 = $_POST['group1'];
       $selected_radio_2 = $_POST['group2'];
       if (($selected_username == 'm3s36G6S9v' && $selected_password == 'S4er5h8QN2') || ($selected_radio_1 == '2' && $selected_radio_2 == '5')) {
         setcookie("username", $selected_username, time()+3600, "/");
         setcookie("password", $selected_password, time()+3600, "/");
         setcookie("radio_1", $selected_radio_1, time()+3600, "/");
         setcookie("radio_2", $selected_radio_2, time()+3600, "/");
       } else {
        header("Location: login.html");
       }
     }
}
?>

Ayrıca rss2html.php betiğinin en üstüne şunu ekledim

// Checks to see if this script was called by the main site pages, (i.e. index.php or mobile.php) and if not, then sends to main page
   session_start();  
   if(isset($_SESSION['views'])){$_SESSION['views'] = $_SESSION['views']+ 1;} else {$_SESSION['views'] = 1;}
   if($_SESSION['views'] > 1) {header("Location: http://website.com/index.php");}

Ürününüzü ne zaman güncellediniz robots.txt? Robotların güncellenmiş sürümü okuması zaman alabilir.
ilanco

birkaç gün önce. Gerçekten ihtiyacım olan Botların rss2html.php betiğini çalıştırmasını engellemektir. Hem oturumları hem de çerezleri denedim ve ikisi de başarısız oldu.

rss2html.phpSiteniz tarafından nasıl kullanılıyor? PHP ile içerir, yönlendirmeler, Ajax ....?
cHao

Rss2html.php dosyaları bir file_get_contents () komutu ile çağrılır

file_get_contents...? Oldukça tuhaf görünüyor. Dosya başka bir sunucuda mı?
cHao

Yanıtlar:


3

Eğer rss2html.php( 's PHP hep bunu kullanarak yerine bir bağlantı ya da bir şey olma durumunda olduğunu) istemci tarafından doğrudan kullanılmıyor, daha sonra blok botlara çalışıyor unut. Yapmanız gereken tek şey ana sayfada bir sabiti veya başka bir şeyi tanımlamak include. Diğer komut dosyasında, sabitin tanımlanıp tanımlanmadığını kontrol edin ve 403 hatası mı yoksa boş bir sayfa mı yoksa tanımlanmamışsa tükürün.

Şimdi, bunun çalışması için kullanmak includeyerine file_get_contents, sadece dosyada okuyacak (yerel bir yol kullanıyorsanız) veya başka bir işlemde ( URL kullanıyor). Ama Joomla! bir komut dosyasının doğrudan eklenmesini önlemek için kullanılır. Ve URL yerine bir dosya yolu kullanın, böylece PHP kodu çalıştırmayı denemeden önce ayrıştırılmaz.

Daha da iyisi rss2html.php, belge kökünün altından çıkmak olabilir, ancak bazı ana bilgisayarlar bunu yapmayı zorlaştırır. Bunun bir seçenek olup olmadığı sunucunuzun / ana makinenizin kurulumuna bağlıdır.


1
Chao, teşekkürler. Şu anda, bunun yerine file_get_contents dönüştürmek için kodumu yeniden yazıyorum.
Sammy

4

Komut dosyanızı, botlar tarafından sağlanan kullanıcı aracısı dizesine göre 404 hatası verecek şekilde ayarlayabilirsiniz - hızlı bir şekilde ipucu alırlar ve sizi yalnız bırakırlar.

if(isset($_SERVER['HTTP_USER_AGENT'])){
   $agent = $_SERVER['HTTP_USER_AGENT'];
}

if(preg_match('/^Googlebot/i',$agent)){
   http_response_code(301);
   header("HTTP/1.1 301 Moved Permanently");
   header("Location: http://www.google.com/");
   exit;
}

Günlüklerinizi seçin ve Bingbot vb. Benzer bir şekilde reddedin - istekleri durduramaz, ancak bazı bant genişliğini koruyabilir - googlebot'a kendi ilacının bir tadını verin - Mwhahahahaha!

Güncellenmiş

Kodunuza bakarak, sorunun burada olduğunu düşünüyorum:

if (preg_match("/$bot[1]/i",$hostname) && $ipaddress == $iphostname)

Eğer kötü niyetli botlarsa, o zaman her yerden geliyor olabilirler, bu $ipaddressmaddeyi çıkarın ve onlara 301 veya 404 yanıtı atın.

Kutunun yanında düşünmek

  1. Googlebot çerezleri asla kabul etmez , bu nedenle saklayamaz. Aslında, tüm kullanıcılar için çerezlere ihtiyacınız varsa, bu muhtemelen botun sayfanıza erişmesini engelleyecektir.
  2. Googlebot formları veya javascript'i anlamadığından, bağlantılarınızı dinamik olarak oluşturabilir veya kullanıcıların kodunuza erişmek için (uygun bir jeton eklenmiş olarak) bir düğmeyi tıklamasını sağlayabilirsiniz.

    <a href="#" onclick="document.location='rss2html.php?validated=29e0-27fa12-fca4-cae3';">Rss2html.php</a>

    • rss2html.php? validated = 29e0-27fa12-fca4-cae3 - insan
    • rss2html.php - bot

1
Düşündüğünüz kadar hızlı değil. Botların aynı varolmayan sayfaya aylarca, bazen sayfanın silinmesinden yıllar sonra bile geldiğini gördüm. Botun ne kadar iyi davrandığına ve sonrasında ne olduğuna bağlıdır.
cHao

İnsan ziyaretçiler için yol, index.php dosyasının rss2html.php komut dosyasını çağırmasıdır. Botlar index.php komut dosyasını önler ve doğrudan rss2html.php komut dosyasını çalıştırır. İndex.php betiği aracılığıyla erişilmemişse bu rss2html.php dosyasını nasıl koruyabilirim?

Rss2html.php dosyasını başka bir adla yeniden adlandırmayı ve index.php'nizi yeni ada referans gösterecek şekilde güncellemeyi deneyin.
BluesRockAddict

Dosyayı yeniden adlandırmayı denedim, ancak birkaç gün sonra başarısız oldu. Kullandığım kodu bu konuya nasıl ekleyebilirim? Ne denediğimi göstermek istiyorum.

1
tamam -

2

Örümcekler / Botlar / İstemciler vb. İçin PHP Limiti / Bloğu Web Sitesi istekleri.

Burada web sitesi trafiğini azaltmak için istenmeyen istekleri engelleyebilir bir PHP işlevi yazdım. Örümcekler, Botlar ve can sıkıcı Müşteriler için iyi.

MÜŞTERİ / Bot Engelleyici

DEMO: http://szczepan.info/9-webdesign/php/1-php-limit-block-website-requests-for-spiders-bots-clients-etc.html

KODU:

/* Function which can Block unwanted Requests
 * @return array of error messages
 */
function requestBlocker()
{
        /*
        Version 1.0 11 Jan 2013
        Author: Szczepan K
        http://www.szczepan.info
        me[@] szczepan [dot] info
        ###Description###
        A PHP function which can Block unwanted Requests to reduce your Website-Traffic.
        God for Spiders, Bots and annoying Clients.

        */

        # Before using this function you must 
        # create & set this directory as writeable!!!!
        $dir = 'requestBlocker/';

        $rules   = array(
                #You can add multiple Rules in a array like this one here
                #Notice that large "sec definitions" (like 60*60*60) will blow up your client File
                array(
                        //if >5 requests in 5 Seconds then Block client 15 Seconds
                        'requests' => 5, //5 requests
                        'sek' => 5, //5 requests in 5 Seconds
                        'blockTime' => 15 // Block client 15 Seconds
                ),
                array(
                        //if >10 requests in 30 Seconds then Block client 20 Seconds
                        'requests' => 10, //10 requests
                        'sek' => 30, //10 requests in 30 Seconds
                        'blockTime' => 20 // Block client 20 Seconds
                ),
                array(
                        //if >200 requests in 1 Hour then Block client 10 Minutes
                        'requests' => 200, //200 requests
                        'sek' => 60 * 60, //200 requests in 1 Hour
                        'blockTime' => 60 * 10 // Block client 10 Minutes
                )
        );
        $time    = time();
        $blockIt = array();
        $user    = array();

        #Set Unique Name for each Client-File 
        $user[] = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'IP_unknown';
        $user[] = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
        $user[] = strtolower(gethostbyaddr($user[0]));

        # Notice that I use files because bots do not accept Sessions
        $botFile = $dir . substr($user[0], 0, 8) . '_' . substr(md5(join('', $user)), 0, 5) . '.txt';


        if (file_exists($botFile)) {
                $file   = file_get_contents($botFile);
                $client = unserialize($file);

        } else {
                $client                = array();
                $client['time'][$time] = 0;
        }

        # Set/Unset Blocktime for blocked Clients
        if (isset($client['block'])) {
                foreach ($client['block'] as $ruleNr => $timestampPast) {
                        $elapsed = $time - $timestampPast;
                        if (($elapsed ) > $rules[$ruleNr]['blockTime']) {
                                unset($client['block'][$ruleNr]);
                                continue;
                        }
                        $blockIt[] = 'Block active for Rule: ' . $ruleNr . ' - unlock in ' . ($elapsed - $rules[$ruleNr]['blockTime']) . ' Sec.';
                }
                if (!empty($blockIt)) {
                        return $blockIt;
                }
        }

        # log/count each access
        if (!isset($client['time'][$time])) {
                $client['time'][$time] = 1;
        } else {
                $client['time'][$time]++;

        }

        #check the Rules for Client
        $min = array(
                0
        );
        foreach ($rules as $ruleNr => $v) {
                $i            = 0;
                $tr           = false;
                $sum[$ruleNr] = 0;
                $requests     = $v['requests'];
                $sek          = $v['sek'];
                foreach ($client['time'] as $timestampPast => $count) {
                        if (($time - $timestampPast) < $sek) {
                                $sum[$ruleNr] += $count;
                                if ($tr == false) {
                                        #register non-use Timestamps for File 
                                        $min[] = $i;
                                        unset($min[0]);
                                        $tr = true;
                                }
                        }
                        $i++;
                }

                if ($sum[$ruleNr] > $requests) {
                        $blockIt[]                = 'Limit : ' . $ruleNr . '=' . $requests . ' requests in ' . $sek . ' seconds!';
                        $client['block'][$ruleNr] = $time;
                }
        }
        $min = min($min) - 1;
        #drop non-use Timestamps in File 
        foreach ($client['time'] as $k => $v) {
                if (!($min <= $i)) {
                        unset($client['time'][$k]);
                }
        }
        $file = file_put_contents($botFile, serialize($client));


        return $blockIt;

}


if ($t = requestBlocker()) {
        echo 'dont pass here!';
        print_R($t);
} else {
        echo "go on!";
}

1

Siteniz sahte google bot (lar) ı tarafından dizine eklenmiş olabilir. Bir kontrol eklemeyi deneyebilir ve tüm sahte google bot istekleri için 404 sunabilirsiniz.

Googlebot’un nasıl doğrulanacağını açıklayan bir makale: http://googlewebmastercentral.blogspot.com/2006/09/how-to-verify-googlebot.html

Ayrıca, kayıtlarınızı bilinen sahte botlara karşı kontrol edebilirsiniz: http://stopmalvertising.com/security/fake-google-bots.html


Teşekkürler, ama aslında ben de denedim, kullanıcı ajanları belirlemek ve bir giriş sayfasına botlar gönderme. Bu da başarısız oldu.

Bot özgünlüğünü belirlemek için kullanıcı aracısına güvenmek noktayı kaçırmış gibi görünüyor.
BluesRockAddict

1

İlk etapta, bir kullanıcı tarafından talep edilen herhangi bir sayfaya, hangi kötü niyetli tarayıcıya sahip olursanız olun, statik bir sayfa sunulacağından emin olmalısınız.

Http sunucunuzla bir koşul veya eşdeğeri olan bir apache mod_rewrite. Apache için şöyle bir şey:

RewriteCond  %{HTTP_USER_AGENT}  ^GoogleBot [OR]
RewriteCond  %{HTTP_USER_AGENT}  ^OtherAbusiveBot
RewriteRule  ^/$                 /static_page_for_bots.html  [L]

teşekkürler, ama tamamen Google, MS Bing ve Yahoo botları engellemek istemiyorum, ama orada rss2html.php komut dosyası doğrudan hit sınırlamak istiyorum. Sadece index.php komut dosyası ile erişilemediğinde çalışmasını engelleyecek rss2html.php komut dosyasının başına bir şey eklemeniz gerekir. Botlar şu anda index.php dosyasını atlayarak rss2html.php komut dosyasını çalıştırıyor.
Sammy

Bu onları engellemez .. sadece php önbelleğe alınmış bir sürümünü hizmet .. bu bir sunucu yapılacak için çok kolaydır, bir daha az php örneği / bir daha az apache çocuk süreci. => Maliyet (statik dosya) <Maliyet (php örneği).
smassey

sayfaları nasıl önbelleğe alırım? Sayfalar RSS olduğundan, önbelleğe alınan sayfalar yeni veriler sağlayacak kadar sık ​​yenilenecek mi?
Sammy

Tabii ki ... Bunu sizin için yapan bir cronjob yazın. Eğer sunucuları 10req / s vurursanız, sayfaları 1 dakikalığına önbelleğe alırsanız, sunucunuzu 599 ekstra php örneği (kesinlikle db bağlantılarını / sorgularını içerir) kurtardınız .. Ve dakikada bir kez ne oy çok daha fazla için: 10 / 15dk.
smassey

1

Smassey'in gönderisine devam etmek için birkaç koşul koyabilirsiniz:

RewriteCond  %{HTTP_USER_AGENT}  ^GoogleBot [OR]
RewriteCond  %{HTTP_USER_AGENT}  ^OtherAbusiveBot
RewriteRule  ^rss2html\.php$     /static.html  [L]

Bu şekilde, botlar sayfalarınıza erişmeye devam eder, ancak bunlara değil. (Meşru) botların kurallara uymaması garip olduğundan, botları sayfanıza diğer kaynaklardan (alan adı iletme, ...) yönlendiren herhangi bir referansınız var mı?


1

Aynı sorunu http://perishablepress.com/blackhole-bad-bots/ adresinde bulunan komut dosyasıyla da çözdüm . Bu kara delik yaklaşımı ile kötü amaçlı bir ip listesi topladım ve .htaccess kullanarak onları reddetti. (Bu zorunlu değildir, çünkü betiğin kendisi yasaklamayı yapar. Ama bilinen istenmeyen ips için php ayrıştırmadan kaçınarak sunucu yükünü azaltmalıyım) üç gün içinde trafiğim günde 5GB'dan 300MB'a indi, bu da beklenen bir durum.

Bilinen önemsiz botları engellemek için htaccess kurallarının tam listesi için bu sayfayı da kontrol edin. http://www.askapache.com/htaccess/blocking-bad-bots-and-scrapers-with-htaccess.html


0
// Checks to see if this script was called by the main site pages,
// (i.e. index.php or mobile.php) and if not, then sends to main page
session_start();  
if (isset($_SESSION['views'])) {$_SESSION['views'] = $_SESSION['views']+ 1;} else {$_SESSION['views'] = 1;}
if ($_SESSION['views'] > 1) {header("Location: http://website.com/index.php");}

Bu betik yorumun söylediklerini yapmaz, aslında tam tersini yapar. Bu olacak hep bot komut istediğinde oturum değişkeni ayarlanmamış asla beri aracılığıyla botlar edelim. Tek yapması gereken, meşru isteklerin (index.php veya mobile.php'den) komut dosyasını birden fazla kez çağırmasını engellemektir.

Bir botun komut dosyasına erişmesini önlemek için, yalnızca bir oturum değişkeni (veya çerez) gerçekten ayarlanmışsa erişime izin vermelisiniz. Tabii ki (kötü amaçlı) botun çerezleri kabul etmediğini varsayarsak. (Gerçek Googlebot'un bilmediğini biliyoruz.)

Daha önce de belirtildiği gibi, rss2html.php dosyasını web kökünün üzerine (genel web alanının dışında) yerleştirmek, bir botun komut dosyasına doğrudan erişmesini engelleyecektir - ancak bunun başka sorunlara neden olduğunu mu söylüyorsunuz? Veya bir dizine yerleştirin ve bu dizini .htaccess ile koruyun. Ya da .htaccess dosyasındaki dosyanın kendisini doğrudan isteklerden koruyabilir misiniz?


0

Alanınızı Cloudflare'de ayarlayın (bunun için ücretsiz hizmet). Sunucunuza ulaşmadan önce etki alanı düzeyinde kötü amaçlı botları engellerler. Yaklaşık 20 dakika sürer, asla kodla maymunlaşmak zorunda kalmazsınız.

Bu hizmeti tüm sitelerimde ve tüm istemci sitelerimde kullanıyorum. Kötü amaçlı botları, Bal Çömleği projesinden yararlanma dahil olmak üzere bir dizi tekniğe göre tanımlarlar.


0

Yapmanız gereken, apache / nginx / email / ftp için sunucunuza bir SSL Sertifikası yüklemek. HSTS'yi etkinleştirdiğinizde ssl.conf dosyanızı, SSLv2 SSLv3 TLSv1'in devre dışı bırakılması ve gelen bağlantılara izin vermemesi için düzenlemeniz gerekir. Sunucunuzu doğru şekilde güçlendirin ve botlardan herhangi bir sorun yaşamayacaksınız.


Bu durumda SSL / TLS'nin hangi sorunu çözdüğü açık değil. Görünüşe göre soruyu yalvarıyor ve sonuca varıyorsunuz. Lütfen bu çözümün sorunu nasıl kontrol ettiğini açıklayın.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.