Stikkordarkiv: Tyveri

Hotlinkere er som oftest fjortiser

Jeg skrev tidligere en liten innføring i hvordan jeg har stoppet hotlinkere fra å vise bilder liggende på bloggen min. Der beskrev jeg hvordan jeg brukte .htaccess filen for å hindre de jeg la til i listen. Dette fungerer glimrende og har egentlig ingen store ulemper. Jeg oppnår det jeg vil, som altså er å hindre uvedkommende i å stjele båndbredden min.

Men det kan bli så mye bedre. Jeg savner nemlig et par ting. Nemlig å se hvilke som blir fanget opp er tungvint, og ikke minst kunne kontrollere tilgang raskt og effektivt basert på data i sanntid.

Eneste måten jeg kan se hvem som snylter på er nemlig å gå gjennom logger, for så å plukke ut de som synder mest (eller bruke fantastisk lang tid på å lete i loggene…). Og ettersom jeg ønsker å kunne se hvorvidt tiltaket virker (eller bare bidrar til gratis reklame for meg) er det ingen god løsning å lete i logger som kun viser fordeling på måned.

Men det var før. Nå har jeg gjort noen små endringer som fungerer enda bedre. Kan det fungere bedre enn glimrende sier du? Javisst. Følg med.

I den nye versjonen har jeg byttet ut loggfilen med et par tabeller i databasen min. En tabell styrer tilgang og en er for logg. htacces filen er endret til å sende alle forespørsler som ikke er fra eget domene eller et fåtall andre, deriblant bildesøk fra google, til et php script som tar seg kontroll og visning av bilder. Alt som som routes til scriptet logges med nødvendig info.

Tabellen som styrer tilgang har et flagg som forteller om det aktuelle domenet skal få tilgang til bildene. Om domenet ikke ligger i tabellen fra før blir det lagt til, med tilgang. Ønsker jeg å fjerne eller gi tilgang kan jeg når som helst endre dette. Verdien fra denne tabellen brukes for å vise det riktige bildet eller denne snasne dama.

Tabellen for logging inneholder hver eneste forespørsel som blir gjort med henvisende url, bildets url, domeneid koblet til tilgangstabellen, og hvorvidt tilgang er gitt. Dermed har jeg mulighet til å trekke ut det jeg trenger av statistikk. Og statistikk liker jeg, så det viser jeg med all data jeg har tilgengelig. Jeg har derfor statistikk fordelt på domener, bilder, tilgang, tidspunkt, i topplister og siste treff. IP og denslags logger jeg ikke.

Og hvis noen lurer på overskriften kan jeg fortelle at blogg.no står for brorparten av hotlinkere mot denne bloggen, sammen med et par forum hvor brukere har hotlinket avatar bildet sitt fra bloggen min. De bytter nok ganske snart…

Se forøvrig mer om hot linking her.

Oppdatering: Koden for php scriptet er lagt ut. Husk å endre filen til php, samt legge inn server, brukernavn, passord og database. Jeg benytter samme database som for WordPress, men med annet prefix for å skille tilhørighet for tabellene fra hverandre.

PHP (scriptet i filen du finner over):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
< ?php
$hotlink['hotlinkimage'] = 'donthotlink.jpg';
 
$hotlink['url'] = '';
if(!empty($_GET['url'])) {
	$hotlink['url'] = $_GET['url'];
 
 
	$hotlink['ref_domain'] = '';
	$hotlink['referrer'] = '';
	$hotlink['fullurl'] = 'http://'.$_SERVER['HTTP_HOST'].'/'.$hotlink['url'];
 
	// mod_rewrite should have already established that it is a hot-link.
	// This is a double-check. We need to capture the referrer, anyway.
 
	if (!empty($_SERVER['HTTP_REFERER'])) {
		$hotlink['referrer'] = $_SERVER['HTTP_REFERER'];
		$hotlink['ref_domain'] = substr($hotlink['referrer'], 7, strpos($hotlink['referrer'], "/", 7) - 7);
	}
 
	// splitting the referrer into a domain part, saves us searching for multiple variations.
	// so long as your host name appears somewhere in the domain part, it's not a hot-link.
 
	if (!stristr($hotlink['ref_domain'], $_SERVER['HTTP_HOST'])) {
 
		$domain = getDomain($hotlink['ref_domain']);
 
		if($domain->allow){
			log_hotlink_to_db($domain, $hotlink['referrer'], $hotlink['fullurl'], 1);
			serveImage('../'.$hotlink['url']);
		}
		else{
			log_hotlink_to_db($domain, $hotlink['referrer'], $hotlink['fullurl'], 0);
			serveImage($hotlink['hotlinkimage']);
		}
	}else{
		serveImage($hotlink['url']);
	}
 
}else{
	// No url? Redirect them to the main site.
	header( "Location: http://" . $_SERVER['HTTP_HOST'] );
}

En enkel klasse for domene. Kunne godt laget en klasse for referrer også, men er ikke nødvendig.

46
47
48
49
50
51
// Simple class for domain
class Domain{
	public $id;
	public $domain;
	public $allow;
}

Logge til db.

53
54
55
56
57
58
59
60
//log the attempt to db
function log_hotlink_to_db($domain, $referrer, $url, $allowed){
	$db_connection = getDBConnection();
	$statement = $db_connection->prepare("INSERT INTO hotlink_log (domain_id, referrer,url,allow) VALUES (?,?,?,?)") or die ("Failed to prepare the statement!");
	$statement->bind_param("issi", $domain->id, $referrer,$url,$allowed);
	$statement->execute();
	$statement->close();
}

Et par funksjoner for å håndtere domener.

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
function getDomain($ref_domain){
	if (preg_match("/^www./i",$ref_domain)){
		// WWW. is deprecated anyway...
		$ref_domain = preg_replace("/^www./i", "", $ref_domain);
	}
 
	$domain = new Domain();
	$domain->id = 0;
	$domain->domain = $ref_domain;
	$domain = checkDomain($domain);
	if($domain->id == 0){
		insertNewDomain($domain);
		$domain = checkDomain($domain);
	}
	return $domain;
}
 
function checkDomain($domain){
	$db_connection = getDBConnection();
	$statement = $db_connection->prepare("SELECT id, allow FROM hotlink_access WHERE domain = ?") or die ("Failed to prepare the statement!");
	$statement->bind_param("s", $domain->domain);
	$statement->execute();
	$statement->bind_result($id, $allow);
	if($statement->fetch()){
		$domain->id = $id;
		$domain->allow = $allow;
	}
	$statement->close();
 
	return $domain;
}
 
function insertNewDomain($domain){
	$db_connection = getDBConnection();
	$statement = $db_connection->prepare("INSERT INTO hotlink_access (domain,allow) VALUES (?,1)") or die ("Failed to prepared the statement!");
	$statement->bind_param("s", $domain->domain);
	$statement->execute();
	//$statement->affected_rows
 
	$statement->close();
}

Vær så god, her har du et bilde. Håper du blir fornøyd.

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
function serveImage($url){
	if (!empty($url) and file_exists($url)) {
		$hotlink['img_type'] = end(explode('.', $url));
		if (strcasecmp($hotlink['img_type'],'png') == 0 ){
			$hotlink['img'] = imagecreatefrompng($url);
			imagesavealpha($hotlink['img'],true);
		} elseif (strcasecmp($hotlink['img_type'],'jpg') == 0 || strcasecmp($hotlink['img_type'],'jpeg') == 0) {
			$hotlink['img'] = imagecreatefromjpeg($url);
		} elseif (strcasecmp($hotlink['img_type'],'gif') == 0) {
			$hotlink['img'] = imagecreatefromgif($url);
		} else {
			trigger_error("HOTLINK - Image $url is of unknown type", E_USER_ERROR);
		}
		//TODO Other file types?
 
	} else {
		// Log image not found!
		trigger_error("HOTLINK - Image $url was not found", E_USER_ERROR);
	}
 
	// send the image to the browser..
	if ($hotlink['img_type'] == 'png') {
		header('Content-type: image/png');
		imagepng($hotlink['img']) or die("there was an error. sorry about that...");
	} else {
		header('Content-type: image/jpg');
		imagejpeg($hotlink['img']) or die("there was an error. sorry about that...");
	}
	imagedestroy($hotlink['img']);
}

Skaff en connection til db. Det er her data om server, brukernavn, passord og database skal inn.

135
136
137
138
139
140
function getDBConnection(){
	$db_connection = new mysqli("server", "username", "password", "database") or die ("Failed to obtain connection to db!");
	return $db_connection;
}
 
?>

Databasetruktur (SQL) er som følger:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE TABLE hotlink_access (
  id INT(11) NOT NULL AUTO_INCREMENT,
  DOMAIN VARCHAR(50) NOT NULL,
  allow INT(11) NOT NULL,
  PRIMARY KEY  (id)
)
 
CREATE TABLE hotlink_log (
  id INT(11) NOT NULL AUTO_INCREMENT,
  domain_id INT(11) NOT NULL,
  referrer VARCHAR(200) NOT NULL,
  url VARCHAR(100) NOT NULL,
  allow tinyint(1) NOT NULL,
  TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY  (id)
)

Egentlig er det også en timestamp i access tabellen også, men den trengs strengt tatt ikke da samme data ligger i loggtabellen.

Til slutt den delen som gjør selve redirekten: .htaccess filen:

1
2
3
4
5
6
7
8
# BEGIN Hotlink stopper
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?xmasb\. [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://((.+)\.)?google\.(.+imgres) [NC]
RewriteCond %{HTTP_REFERER} !^http(s)?://((.+)\.)?google\.(.+reader) [NC]
RewriteRule ^(.*)\.(gif|jpe?g|png)$ http://%{HTTP_HOST}/hotlink/hotlink.php?url=$1.$2 [R,NC,L]
# END Hotlink stopper

Alle request som ikke er direkte på bildene (gif|jpe?g|png), via xmasb, google’s bildesøk eller google reader blir sendt til scriptet. Scriptet sørger for resten. Her kan alle tillatte domener legges inn, så det ikke brukes unødig prossesering på tillatte domener. Eksempel på andre domener som kanskje bør ligge her er RSS lesere (Google, Bloglines, Netvibes f.eks) og andre sider for bildesøk. Det fine med denne fremgangsmåten er at det er lett å oppdage disse underveis, uten at de er utestengt i mellomtiden.

Det er lett å bare legge inn Google som tillatt domene i htaccess filen, men husk at det også eksisterer noe som heter sites.google.com. Scriptet får et par treff om dagen derfra.

Oppdatering 2: Har gjort en liten endring til på scriptet mitt. Ettersom blant annet blogg.no er kraftig overrepresentert i loggene er det like greit å sperre de ute med en gang. Jeg kunne selvsagt gjort dette via htaccess filen, men får å få med loggingen tar jeg de via scriptet likevel. For å gjøre dette har jeg gjort følgende endring i insertNewDomain funksjonen:

138
139
140
141
142
143
144
145
146
147
148
149
150
function insertNewDomain($domain, $allow = 1){
	global $hotlink;
	if(anyNeedleInString($domain->domain, $hotlink['blockedBaseDomains'])){
		$allow = 0;
	}
	$db_connection = getDBConnection();
	$statement = $db_connection->prepare("INSERT INTO hotlinkaccess (domain,allow) VALUES (?,?)") or die ("Failed to prepared the statement!");
	$statement->bind_param("si", $domain->domain, $allow);
	$statement->execute();
	//$statement->affected_rows
 
	$statement->close();
}

AnyNeedleInString ser slik ut:

187
188
189
190
191
192
193
194
function anyNeedleInString($haystack, $needle){
	foreach($needle as $key => $search_needle) { 
		if(stristr($haystack, $search_needle)) { 
			return true;
		}
	}
	return false;
}

Og så er det bare å definere en array som inneholder de domener man ikke vil ha med:

3
$hotlink['blockedBaseDomains'] = array('blogg.no', 'nettby.no');

Her illustrert med blogg.no og nettby.no. Dermed slipper jeg å håndtere de som tilhører diverse plattformer. Smart, ikke sant?

En bildetyv?

Dette er merkelig. Tidligere idag fikk jeg en melding på Flickr fra en person som hadde funnet navnet mitt i et jpg bilde på en side. Bildet var av et lyn, og hadde mitt navn skjult i EXIF infoen. Jeg har nemlig satt opp kameraet mitt til å legge inn kontaktinfo på alle bilder jeg tar under «user comment» i EXIF infoen som ligger i bildene. Ettersom infoen han hadde funnet var nøyaktig den teksten jeg tidligere hadde satt opp regnet jeg derfor med at det var snakk om noen som tok æren for et bilde jeg hadde tatt. Men der tok jeg feil. Jeg har nemlig ikke tatt bildet det er snakk om.

Bildet jeg fikk link til kunne minne om bilder jeg selv har tatt, men jeg kunne fort fastslå at dette er en annen persons verk. Jeg kikket likevel på EXIf informasjonen og kunne se selv at bildet riktignok hadde mitt navn i seg. I tillegg til resten av informasjonen som også passer med mitt utstyr. Dette var virkelig merkelig.

For å dobbeltsjekke om det virkelig kunne vært meg selv som hadde tatt bildet, åpnet jeg arkivet og bladde meg frem til tidspunktet bildet skal være tatt på. Jeg fant et bilde av et lyn på det nøyaktige tidspunktet som bildet på nettsiden skulle være tatt. Resten av EXIF passet også helt perfekt. Men bildet var ikke det samme.

Jeg har nå sendt en mail til innehaveren av galleriet, hvor jeg spør om en forklaring på fenomentet. Jeg er spent på svaret, om jeg får noe svar i det hele tatt. Jeg har mine tvil. Bildet det er snakk om kan du finne her. Altså ikke bildet som jeg har tatt, men med mine metadata. En rask titt på galleriet til denne brukeren kan tyde på at ikke alle bildene er tatt av samme person. Ikke bare fordi motivene er såpass forskjellige, men fordi bildene er tatt med mye forskjellig utstyr. Men vi vet jo allerede at vi ikke kan stole på EXIF data fra dette galleriet…

Bildet jeg har tatt «samtidig» ser du her. Klikk på bildet for å komme til bildet på flickr. Lightning

Anti-Hotlinking – andre forsøk

Jeg skrev tidligere litt om problemet med hotlinking. Et heller dårlig forsøk på å få stoppet det endte med at bilder fra siden min heller ikke kunne vises i feeds eller uavhengige RSS-lesere. Etter fiaskoen bestemte jeg meg for å la det være, og tenkte ikke noe særlig mer over det. Men så skjedde det igjen. Nok en gang fikk jeg en link til siden min, fra en som viser et bilde jeg har liggende på mitt domene.

Jeg bestemte meg for å angripe problemet på en litt annen måte. Istedet for å stoppe alle, stopper jeg kun de jeg oppdager. Greit nok, mange kan bruke bildene mine uten at jeg nødvendigvis oppdager det, men forhåpentligvis får jeg gortsatt vist bildene til dere som abonnerer på min nyhetsstrøm ((Herlig med slike norske ord, synes du ikke?))/feed via RSS. Jeg kan alltids legge til flere etterhvert som flere blir oppdaget. Jeg sjekker litt statistikk og logg iblant, så det hender jeg finner noen syndere.

For å få til dette har jeg nok engang vært å hacket litt i .htaccess ((Den morsomme filen som du definitivt bør ta backup av før du begynner å tulle med, hvis du ikke vet hva du driver med.))-filen. Denne gangen la jeg inn følgende kode:

BEGIN Hotlink stopper

RewriteEngine on RewriteCond %{HTTP_REFERER} ^(.+.)?blogspot.com/ [OR] RewriteCond %{HTTP_REFERER} ^(.+.)?livejournal.com/ [OR] RewriteCond %{HTTP_REFERER} ^(.+.)?myspace.com/ [OR] RewriteCond %{HTTP_REFERER} ^(.+.)?trykker.com/ [OR] RewriteCond %{HTTP_REFERER} ^(.+.)?wordpress.com/ RewriteCond %{REQUEST_URI} !/wp-content/uploads/hotlink.jpg RewriteRule .*.(gif|jpg|png|avi)$ /wp-content/uploads/hotlink.jpg

END Hotlink stopper

Kort fortalt gjør den følgende. Dersom HTTP_REFERER ((HTTP_REFERER forteller hvor etterspørselen kommer fra. Les mer på Wikipedia.)) viser til en av sidene jeg har listet opp (blogspot, livejournal, myspace, trykker og wordpress foreløpig) skal den vise bildet hotlink.jpg istedet for bildet som etterspurt. Foreløpig er dette bare et bilde jeg fant på nettet, men denne skal erstattes med eget bilde etterhvert.

Listen over nettsider kan jeg redigere etterhvert, foreløpig har jeg valgt å legge inn de største bloggesidene, da dette er de hyppigste tilfellene av leeching hos meg.

Et eksempel på hvordan dette ser ut i praksis kan du se her: Fascisme på norsk (han byttet visst bilde ettehvert naturlig nok :)). Egentlig skulle det være et bilde av Kong Harald der, men det beholder jeg selv.

Erstatningsbilde ved hotlinking Bildet som jeg foreløpig bruker ser slik ut. Det er kanskje ikke så rart at jeg ønsker å bytte det ut?

Stopp tyven!

Noen stjeler av meg. De trodde kanskje ikke at jeg ville merke noe, men jeg følger alltid med. Jeg vet hva hva som foregår. Det er tyver på ferde. De finner bilder på siden min og stjeler dem. Og verre: de hotlinker. Bilder fra xmasb.com blir brukt på andre nettsider uten min tillatelse, og uten å hente ned filene. De lar nemlig bildene ligge på serveren min, slik at det er jeg bruker båndbredden hver gang de blir vist. Men nå er det slutt.

Jeg har nå sperret tilgangen til bilder fra andre nettsider, og jeg har tatt høyde for at de skal vises i feeds også. Skulle andre være interessert finner de oppskriften som skal legges inn i .htaccess-filen her:

Hotlink Protection with Feedburner Access

RewriteEngine on RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://example.com$ [NC] RewriteCond %{HTTP_REFERER} !^http://example.com/.$ [NC] RewriteCond %{HTTP_REFERER} !^http://www.example.com$ [NC] RewriteCond %{HTTP_REFERER} !^http://www.example.com/.$ [NC] RewriteCond %{HTTP_REFERER} !^http://www.feedburner.com/.$ [NC] RewriteCond %{HTTP_REFERER} !^http://feeds.feedburner.com/example$ [NC] RewriteCond %{HTTP_REFERER} !^http://feeds.feedburner.com/exampleComments$ [NC] RewriteRule ..(jpe?g|gif|bmp|png)$ http://example.com/wp-content/uploads/hotlink.jpe [L] NB:Jeg tar selvsagt intet ansvar skulle du ødelegge noe ved bruk av denne koden.

Bytt ut «example.com» med ditt domene, og sett riktig url for feeds. Siste linje erstatter alle bilder med et eget bilde kalt «hotlink.jpe». Jeg har brukt filendelsen «.jpe» for å forhindre at ReWriteRule forsøker å erstatte den igjen (og dermed skape en evig løkke). Jeg skal ikke gå gjennom hva hver enkelt linke gjør, men foreslår at du leser litt om bruken av .htaccess dersom du skal ta i bruk dette. Et kjapt søk på google med «prevent hotlinking images feedburner» gir mye info for den som ønsker det.

Det finnes mange varianter til .htaccess som gjør samme nytten, dette ser ut til å fungere for meg. For ordens skyld legger jeg ved et bilde i denne posten, for å sjekke at feeds også fungerer som de skal.

Lys Bare et bilde jeg tok av noen lys på bordet, mest for å teste hvordan lysene fra juletreet bak ville se ut.

Bryllup nærmer seg nå.

Som de fleste har fått med seg skal jeg i ilden om ikke lenge.

Brud og brudgom blir å finne i Lørenskog Kirke lørdag 8.juli klokken 15.00. I det siste har jeg begynt å tenke en del på hva jeg skal si for noen pinlige ord rundt middagstider senere på kvelden. Jeg er for å si det mildt ingen stor taler.

Lørdag som var hadde jeg utdrikningslag! Det var moro! Det var egentlig planlagt Paragliding for meg, men grunnet mye vind ble ikke dette noe av. Jeg er ikke spesielt glad i høyder, men tror det skal gå greit. Vi snakket om å prøve igjen til helga, så jeg håper det går i orden. Satser på det.

Tror jeg var full ihvertfall to ganger på lørdag. Når klokka nærmet seg to på natta var det nok, og senga ropte. Dessverre fant jeg da ut jeg ikke hadde med nøkler. Jeg hadde heller ikke fått tak i Rita, så jeg visste pent lite om hun var hjemme. Derfor endte jeg i senga hjemme hos bror, mens han la seg på sofaen.

Da jeg kom hjem viste det seg selvsagt at noen hadde stjålet mobilen til min bedre halvdel (det skulle også vise seg at de hadde ringt for 600 kroner med den…). Så da bestilte jeg en ny. Jeg er snill. 🙂