Hvorfor stripper denne bort all teksten?

Mr Vest

Sjefen over alle sjefer!
For en tid tilbake fikk jeg en flott kodesnutt fra en hyggelig kar her på forumet som skulle strippe bort det verste fra tekst brukeren har lagt inn i en input, i mitt tilfelle ved bruk av et textarea med TinyMCE. Jeg finner ikke igjen tråden, men koden var slik:

PHP:
$string=$_POST['content'];
    function _strip_attributes($string,$allowtags='<embed><object><b><br><br /><center><i><p><strong><u><ul><li><ol><h1><h2><h3><h4><h5><h6><span><em>',$allowattributes=NULL) {
        if($allowattributes) {
            if(!is_array($allowattributes)) $allowattributes = explode(",",$allowattributes);
            if(is_array($allowattributes)) $allowattributes = implode("|",$allowattributes);
            $rep = '/([^>]*) ('.$allowattributes.')(=)(\'.*\'|".*")/i';
            $string = preg_replace($rep,'$1 $2_-_-$4',$string);
        }
        $string = preg_replace('/([^>]*) (.*)(=\'.*\'|=".*")(.*)/i','$1$4',$string);
        $rep = '/([^>]*) ('.$allowattributes.')(_-_-)(\'.*\'|".*")/i';
        if($allowattributes) $string = preg_replace($rep,'$1 $2=$4',$string);
        return strip_tags($string,$allowtags);
    }
    function _strip_javascript($filter){      
        # realign javascript href to onclick
        $filter = preg_replace("/href=(['\"]).*?javascript:(.*)?\\1/i","onclick=' $2 '",$filter);
        # remove javascript from tags
        while( preg_match("/<(.*)?javascript.*?\(.*?((?>[^()]+)|(?R)).*?\)?\)(.*)?>/i",$filter)) {
            $filter = preg_replace("/<(.*)?javascript.*?\(.*?((?>[^()]+)|(?R)).*?\)?\)(.*)?>/i","<$1$3$4$5>",$filter);
        }
        # dump expressions from contibuted content
        if(0) {
            $filter = preg_replace("/:expression\(.*?((?>[^(.*?)]+)|(?R)).*?\)\)/i","",$filter);
        }
        while(preg_match("/<(.*)?:expr.*?\(.*?((?>[^()]+)|(?R)).*?\)?\)(.*)?>/i",$filter)) {
            $filter = preg_replace("/<(.*)?:expr.*?\(.*?((?>[^()]+)|(?R)).*?\)?\)(.*)?>/i", "<$1$3$4$5>", $filter);
        }
        # remove all on* events   
        while(preg_match("/<(.*)?\s?on.+?=?\s?.+?(['\"]).*?\\2\s?(.*)?>/i",$filter)) {
            $filter = preg_replace("/<(.*)?\s?on.+?=?\s?.+?(['\"]).*?\\2\s?(.*)?>/i", "<$1$3>", $filter);
        }
        return $filter;
    }  
$string = _strip_javascript($string);
$string = _strip_attributes($string);

Det jeg nå legger merke til som jeg faktisk ikke forstår, det er at dersom jeg skriver et "innlegg" og skal publisere dette, da kjører det gjennom denne koden. Resultatet etter dette har gått gjennom koden er ingenting. Absolutt alt blir nemlig filtrert bort. Dette er den eneste koden jeg kjører dette gjennom før det blir lagt opp i databasen.

Taggene jeg har brukt i et tilfelle hvor dette skjer:

<p></p><strong></strong><span style="text-decoration: underline;"></span>

Merk: Dette skjer ikke alltid. Det skjer kun dersom jeg bruker enkelte tagger, men jeg er usikker på hvilken. De som jeg har nevnt over har jeg akkurat testet, og ja, resultatet blir tomt dersom jeg bruker disse taggene. Hvilken av disse taggene er det som gjør at dette går galt?
 

Nextri

Rebel
Dette var jeg borti også. Og jeg brukte lang tid på å skjønne hva som skjedde. Det går nemlig fint hvis strengen er under 1000 tegn. men har du mer enn 1000 tegn forsvinner alt. Er noe galt med regex funksjonene der.

her er nye:
PHP:
	function _strip_attributes($string,$allowtags=NULL,$allowattributes=NULL){
		$string = strip_tags($string,$allowtags);
		if (!is_null($allowattributes)) {
			if(!is_array($allowattributes))
				$allowattributes = explode(",",$allowattributes);
			if(is_array($allowattributes))
				$allowattributes = implode(")(?<!",$allowattributes);
			if (strlen($allowattributes) > 0)
				$allowattributes = "(?<!".$allowattributes.")";
			$string = preg_replace_callback("/<[^>]*>/i",create_function(
				'$matches',
				'return preg_replace("/ [^ =]*'.$allowattributes.'=(\"[^\"]*\"|\'[^\']*\')/i", "", $matches[0]);'   
			),$string);
		}
		return $string;
	} 
	function _strip_javascript($string){	  
		# realign javascript href to onclick
		$string = preg_replace("/href=(['\"]).*?javascript:(.*)?\\1/i","onclick=' $2 '",$string);
		# remove javascript from tags
		while( preg_match("/<(.*)?javascript.*?\(.*?((?>[^()]+)|(?R)).*?\)?\)(.*)?>/i",$string)){
			$string = preg_replace("/<(.*)?javascript.*?\(.*?((?>[^()]+)|(?R)).*?\)?\)(.*)?>/i","<$1$3$4$5>",$string);
		}
		# dump expressions from contibuted content
		if(0){
			$string = preg_replace("/:expression\(.*?((?>[^(.*?)]+)|(?R)).*?\)\)/i",'',$string);
		}
		while(preg_match("/<(.*)?:expr.*?\(.*?((?>[^()]+)|(?R)).*?\)?\)(.*)?>/i",$string)){
			$string = preg_replace("/<(.*)?:expr.*?\(.*?((?>[^()]+)|(?R)).*?\)?\)(.*)?>/i","<$1$3$4$5>",$string);
		}
		# remove all on* events
		while(preg_match("/<(.*)?\s?on.+?=?\s?.+?(['\"]).*?\\2\s?(.*)?>/i",$string)){
			$string = preg_replace("/<(.*)?\s?on.+?=?\s?.+?(['\"]).*?\\2\s?(.*)?>/i","<$1$3>",$string);
		}
		return $string;
	}

sånn til info. $allowedattributes bestemmer hvilke attributter tags har lov å ha. som ikke skal strippes vekk.


Hvis du vil at koden du postet skal gå igjennom uten at noe av det du har der skal strippes, må du sette $allowedattributes = array('style'); for at style="text-decoration:underline;" ikke skal bli strippet bort.

kan være greit å sette den til $allowedattributes = array('style','height','alt','title','width','rel','href','target','src');

for at disse tingene ikke skal bli strippet bort fra f.eks. en link eller et bilde. men den vil da fortsatt ikke tillate f.eks. onclick eller andre atttributter som kan missbrukes.

i f.eks:
<img src="bilde.jpg" alt="mittbilde" onclick="alert('her er javascript som kan være skummelt');" />

her ville den da altså tillatt src og alt, men ikke onclick.
 

Mr Vest

Sjefen over alle sjefer!
I alle dager, takk så mye. Både for kode og forklaringer. Skikkelig kult gjort av deg at du tok deg tid. :)
 

Mr Vest

Sjefen over alle sjefer!
Koden er nå testet og fungerer over alle forventninger. Herlig, dette her var virkelig moro. Er det noe som virkelig fortjener ryktepoeng fra alle medlemmene på hele WF må det være innlegget nextri skrev over.
 

Mr Vest

Sjefen over alle sjefer!
@Sjefskoder, hehe, finner den nok ikke. Jeg har ikke alltid vært så flink med titler på trådene jeg har startet vet du. Egentlig er den linken grei og ha i menyen, selv om det jeg selv har gjort den siste tiden er å klikke på brukernavnet mitt der oppe i toppen, som da leder meg til min profil, hvor jeg da har klikket meg videre til mine innlegg. :)
 

Sjefskoder

Sjefskoder
Stemmer det.. du er en sånn som ikke skriver en tittel som er relatert til inneholdet i tråden :p hehe
 

Mr Vest

Sjefen over alle sjefer!
@Tonny - Flott. Kodesnutten han har kommet med hjelper ikke bare meg. Den kan være nyttig for alle som vil la brukere legge fra seg innhold på et nettsted. Kjapt fortalt (selv om du sikkert har forstått det) stripper den bort farlig kode fra innholdet de sender inn.

@Sjefskoder - Korrekt. Det er meg, men jeg var jo flink på denne tråden da. Hurra for meg. :D

Jeg kom til å tenke litt på dette med innholdet som bllir sendt opp nå. Det som er greia er jo dette med at brukeren faktisk kan legge inn HTML. Dersom de da tilfeldigvis legger inn en <b> uten en </b> på slutten så vil jo resten av nettsiden etter innholdet mitt være i uthevet tekst. Hvilken løsning anbefaler dere for å løse dette?

Må jeg telle opp antall <b> ? og deretter legge til en dersom antallet </b> er mindre? Når jeg tenker på det høres det tungvint ut, men en eller annen løsning må jeg jo altså ha... Det kan jo også oppstå en link i innholdet som ikke lukkes. Denne vil jo også da fortsette videre utenfor innholdet...

Noen tanker på denne utfordringen?
 

Nextri

Rebel
Hvis du lar brukeren bruke en wysiwyg editor så vil de jo ikke trenge å redigere html direkte. Og de vil jo selv se at det mangler en b hvis halve teksten er i bold.

Hvis du teller opp antall <b>. Hvordan skal du vite hvor du skal legge til den som eventuelt mangler?

Det du kan gjøre er å telle opp antallet og gi en feilmelding hvis noe er galt. og da ikke lagre endringene. Men syns man bør kunne gi brukeren såpass ansvar at de klarer å legge det inn riktig på egenhånd. Br ikke være et problem hvis du bruker wysiwyg og ikke tillater at de redigerer html koden direkte.
 

Mr Vest

Sjefen over alle sjefer!
Joda, brukerne skal så absolutt få styre på og lagre selv om det er noe galt. Men det som er greia er at dersom de lagrer, og det er en <b> som ikke blir lukket i deres kode, da ender jo resten av nettstedet utenfor innholdet deres opp som bold også. Dermed ender det jo opp med at resten av nettstedet tuller seg til... Skjønner du?

La oss si at jeg har en sidebar på venstre side, contentområde, og en sidebar på høyreside. Sidebar på venstre side blir bra. De mekker feil i innholdet på contentdelen, og dermed tuller også hele høyre sidebar seg til pga deres feil. Det er det jeg vil unngå...

Det jeg tenkte med å telle opp f.eks antall <p> var og telle dem opp, og dersom antallet </p> er mindre kan jeg jo legge inn antall </p> som det er for lite av på slutten før jeg sender det opp til databasen. Det eneste er bare at jeg syns det høres tungvint ut. Derfor kunne jeg tenke meg alternative løsninger på den. Feilmeldinger er litt sånn kjedelig. Det ville jeg aldri likt selv skjønner du, og jeg tror heller ikke brukerne mine ville likt det.
 
Hm... men hvis du bruker en wysiwyg, er du da nødt til å tillate HTML? Kan du ikke la wysiwyg-en sette inn og tolke BBcode eller noe sånt istedetfor? Og så kjører du bare htmlentities() og en regular expression replace etterpå (er det preg_replace() man skal bruke? Husker ikke i farta) der du erstatter [ b]...[/b] med <b>...</b>, så dermed vet du både at antallet er riktig og at alle </b>-er står på riktig sted? Dersom det da skulle lure seg inn en ekstra et sted, vil denne ikke gjøre at noe som helst blir fet skrift. Og desom brukeren på pur dævelskap legger inn en manuell <b> for å provosere, kommer htmlentities()-en til å fikse det.
 

Mr Vest

Sjefen over alle sjefer!
Jeg vil helst tillate HTML ja. Om noen skulle lure når jeg fortsetter nå, jeg er faktisk blond på ekte også og ikke bare i mine innlegg på forumet. Jeg forstod ikke tanken der olafmoriarty. Og brukeren hadde lagt inn en for mye ville det jo allikevel blitt snudd om til en <b> for mye?
 
Nei, det hadde det ikke. Ikke så lenge du bruker en preg_replace() til å fikse det slik at det bare er -par som blir gjort om og ikke enkeltstående -er.

Gi meg tjue minutter, så skal jeg slenge sammen et eksempelscript for deg ... Snart tilbake ...
 
Ok, se for eksempel på denne, som du til og med ikke trenger å skrive om til bbcode for:

PHP:
<?php
$string = '<p><b>Dette</b> er en <b>test</p>';
$string = hvitvasking($string);
echo $string;
echo '<p>Denne skal ikke være fet</p>';

function hvitvasking($tekst) {
	$tekst = htmlentities($tekst);
	$tillatte_tags = array('b', 'i', 'strong', 'em', 'p'); // Her skriver du inn alle taggene du tillater
	$num = count($tillatte_tags);
	for ($i = 0; $i < $num; $i++) {
		$tekst = preg_replace('/&lt;'.$tillatte_tags[$i].'&gt;(.*?)&lt;\/'.$tillatte_tags[$i].'&gt;/i', 
			'<'.$tillatte_tags[$i].'>\\1</'.$tillatte_tags[$i].'>', $tekst);
	}
	return $tekst;
}
?>

Her gjør vi om alle <b> og </b> til &lt;b&gt; og &lt;/b&gt;, og deretter bruker vi preg_replace() for å gjøre at bare det faktiske tag-paret blir endret tilbake til <b></b> og ikke den enkeltstående <b>-en.

Når jeg tester dette på egen server blir resultatet at den opprinnelige stringen min,
<p><b>Dette</b> er en <b>test</p>
... printes ut som
Dette er en <b>test

... med andre ord, bare de <b>-ene som faktisk har en </b> tilknyttet seg blir gjort om til HTML.

Ikke en optimal løsning, den tar ikke høyde for html-attributter og den gjør dessuten at du må legge inn en array med alle godkjente tags på forhånd i stedet for at dette går automatisk. Men det er bare fordi dette er gjort på en halvtime og jeg ikke er helt stø på regular expressions. Det betyr på ingen måte at dette ikke er mulig -- dette var bare et raskt eksempel for å vise deg hva jeg mener.

Det beste for deg hadde sannsynligvis vært å bake inn noe slikt i den funksjonen du allerede har, såklart.
 
Sist redigert:
Topp