Avatar billede Smitche Praktikant
05. januar 2013 - 20:13 Der er 26 kommentarer og
1 løsning

Hvor og hvordan skal man beskytte sig mod SQL-injections og XSS?

Hvor og hvordan skal man beskytte sig mod SQL-injections og XSS?

Jeg har lavet en hjemmeside som fungerer som et mini-CMS. Siden er lavet med HTML, CSS, JavaScript og til database er der gjort brug af MySQL samt PHP.

Det hele går ud på en at en superbruger kan logge ind på hjemmesiden, og derefter bliver der vist en 'admin' side. Her kan en bruger oprette nyheder og nye brugere (men disse nyoprettede kan ikke selv oprette brugere - det er kun superbrugeren). Det er muligt for alle besøgende at kommentere disse nyheder (altså uden at være logget ind). Derudover er det også muligt for alle brugerne at slette kommentar, nyheder og opdatere nyhederne.

Hvad jeg så kan forstå er at man skal beskytte sig mod SQL-injections, fordi med disse SQL-injections kan man 'indskyde' SQL-kode ved et SQL-kald. Fx ved en login-side med en HTML form (som min side), her kan der opnås uautoriseret adgang som superbruger. Bør jeg så kun bruge prepared statements ved login-siden?

Jeg har forstået at man kan beskytte sig mod SQL-injections ved at bruge prepared statements, hvor der indgår bruger-input i SQL-sætningen. Det kan gøres med PDO fordi det er den nemmeste metode og den fungerer til mange forskellige database typer. Jeg har så gjort brug af prepared statements ved login, se nedenstående:

<?php
session_start();
//Tjekker om email og password er sat
if(isset($_POST['userid']) && isset($_POST['password']))
{
    $userid    = $_POST['userid'];
    $password  = $_POST['password'];

    require_once('connect.inc.php');
    $conn = dbConnect('pdo') or die('no connection');

    //Henter data hvor email er lig med userid
    $stmt = $conn->prepare("SELECT * FROM users_tbl WHERE email = ? AND password = ?");
    $stmt->bindParam(1, $userid, PDO::PARAM_STR);
    $stmt->bindParam (2, sha1($password), PDO::PARAM_INT);
    $stmt->execute();

    if($stmt->fetch())
    {
    $_SESSION['valid_user'] = $userid;
    }
    $stmt = null;

    //Hvis okay gå til prefs, ellers udskriv fejl
    if(isset($_SESSION['valid_user'])){
        header("location:prefs.php");
    //Udskriv fejl her
    }else{
    ?>

Men er så ikke helt sikker på jeg forstår hvad det er der egentlig sker og hvorfor?
Er det korrekt at:
1.   
'prepare' metoden sender queryen til serveren og bruger en placeholder i form af et ?-tegn som argument i stedet for variablerne. Derefter bruges 'execute' metoden til at erstatte placeholderen i SQL med det rigtige. Fordi queryen og variablerne sendes separat er der på ingen måde noget SQL der vil blive kunne 'executed' hvis det befandt sig i variablerne?

2.   
Det er bedst og bruge bindParam - fremfor bindValue eller gøre det i selve execute sætningen, fordi variablen så ikke nødvendigvis behøver at være defineret endnu? Men kan ikke helt se hvornår det er interessant?

3.   
Man SKAL altid lukke sin database forbindelse efter man har kørt alle sine queries på hver PHP script? Ved fx at sige $stmt = null;. Og hvorfor skal man det?

Foruden prepared statements bør man så satinere alt input og output for at undgå HTML i databasen og her kan man bruge htmlspecialchars?

Bør man også checke om input eller output har det rigtige format og her kan man bruge regex?

Se nedenstående eksempel på begge:

//Bruger
$username     = htmlspecialchars($_POST['username']);
$email        = htmlspecialchars($_POST['email']);
$password    = htmlspecialchars($_POST['password']);
$usertype    = htmlspecialchars($_POST['usertype']);


//Indsæt bruger i databasen
if (isset($_POST['email'])){

  if (preg_match(if (preg_match("/^[\w\d]$/", $username) && preg_match("#.*^(?=.{8,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$#", $password) && preg_match("/([^@]+)@(.+)/", $email))
  {
  //Opret bruger
  $query  = "INSERT INTO users_tbl(username, email, password, usertype) VALUES (?,?,?, 1)";
     
  $stmt = $conn->prepare($query);
  $stmt->bindParam(1, $username, PDO::PARAM_STR);
  $stmt->bindParam(2, $email, PDO::PARAM_STR);
  $stmt->bindParam(3, sha1($password), PDO::PARAM_STR);

  $stmt->execute();
     
    header("location:prefs.php");
  }
  else
  {

Udover SQL-injections findes der også XSS, som er hvad helt præcist? Og hvordan beskytter man sig mod det? Kan man sikrer sig mod XSS ved at sørge for at en bruger aldrig nogensinde kan komme til at skrive egentlig HTML - ved at konvertere de specielle tegn (<, >, & og ") til HTML-entiteter inden de udskrives?

Jeg bruger fx en JavaScript funktioner som alertbox. Når en bruger er logget ind og vil slette nyhederne, bruges den til at spørge om man 'vil slette denne nyhed?' og her sender jeg så id'et på nyheden videre gennem URL'en for at slette den. Kan forstå at når man bruger alert() funktioner er man XSS vulnerabel? I hvilke andre tilfælde er man også det?

Kan man bruge htmlspecialchars for at beskytte sig mod XSS når man udtrækker data fra databasen? Det er hvad jeg kunne læse mig frem til på en tråd fra stackoverflow:
http://stackoverflow.com/questions/14148937/is-using-htmlspecialchars-for-input-output-html-sanitization-for-mysql-databa


Jeg finder dette spørgsmål meget interessant og håber nogen kan hjælpe mig med det, da jeg  også tror det kan hjælpe andre med at forstå hvad XSS og SQL-injections er for en størrelse og hvordan man kan beskytte sig mod det.

På forhånd mange tak.
Avatar billede erikjacobsen Ekspert
05. januar 2013 - 21:06 #1
Eet af dine punkter: Når bruger A vil slette en nyhed, som har ID=117, så laver du altså en ..../slet.php?id=117 - og så er spørgsmålet om du i slet.php yderligere kontrollerer, at der er en bruger logget ind, og at denne bruger, A, har lov til at slette nyhed med ID=117?

For ellers har du, hvad jeg vil kalde en URL-injection, hvor man blot kan ændre lidt på URL-en, og lave lidt ravage ;)
Avatar billede Smitche Praktikant
05. januar 2013 - 21:17 #2
Det er den eneste metode jeg kender til. Men nej det gør jeg faktisk ikke, men kan gøre det ved brug af min $_SESSION['valid_user'].

Hvad ville ellers være smart og gøre? Jeg sender nemlig ID igennem URL'en ved delete, update og insert.
Avatar billede erikjacobsen Ekspert
05. januar 2013 - 21:45 #3
Du kan fint gøre det vha din sessions-variabel.

1) Den må ikke være tom
2) Brugeren i variablen skal have lov til at slette det valgte ID, så det må du se efter i databasen
3) Og så kan ud gøre det

Og noget andet: Din brug af htmlspecialchar på brugernavn osv er vel overflødig, når du alligevel har regler for hvad de må indeholde.

Din kontrol af password synes meget streng. Da passwordet alligevel aldrig vises, kan det vel indeholde hvad som helst.
Avatar billede Smitche Praktikant
05. januar 2013 - 21:55 #4
Jeg bruger denne metode

<?php
// Start session
session_start();
// Tjek om session er sat, og om man er logget ind, ellers gå til login-side
if (!isset($_SESSION['valid_user'])) {
header('Location:login.php');
}
?>

Men forstår ikke din 2) Er det fordi du tænker på om brugeren har rettigheder til at fx slette? Fordi alle brugerne har nemlig ret til det.

Kan man bruge htmlspecialchars for at beskytte sig mod XSS? Eller er der andet man skal tage højde for, fx i forhold til min alert() funktion? Bør jeg ellers bruge regex på alt input?
Avatar billede erikjacobsen Ekspert
05. januar 2013 - 22:22 #5
Hvis alle brugere har lov til at slette alt, så kan du hoppe over den kontrol.

Ellers er det jo altid sjovt som bruger at skrive en URL : ..../slet.php?id=118 - og se hvad der sker.

Naturligvis skal man bruge htmlspecialchars, hvis man skal udskrive vilkårlig tekst i en html side - med mindre det allerede er (kendt) html. Hvis man ikke gør "<" til "&lt;" får man fx jo problemer med de sjove smileys <3.

I princippet er det ikke nok, men det er nok, hvis du udelukkende anvender de html-encodede tekster som brødtekst (tænk <div> og <p>).

Anvender du f.eks. en tekst fra en bruger i en javascript funktion:

  alert('<?php echo $_SESSION['username'];?>');

så hjælper html-encoding ingenting.

Hver gang man skriver brugerinput ud, skal man encode i forhold til det sted det anvendes, og DERFOR må man aldrig bare gemme tekst i databasen som html-encoded.

Gem teksten præcis som brugeren har skrevet det, og anvend den encoding, der nu måtte være nødvendig på det sted det skal anvendes ved udskrift etc.
Avatar billede Smitche Praktikant
05. januar 2013 - 22:31 #6
Er ikke sikker på jeg helt forstå det sidste du skriver, dvs man ikke må bruge htmlspecialchars til at gemme input, men jeg skal bruge det når jeg output'er bruger-input?

Men kan ikke helt forstå hvorfor man ikke må gemme det sådan?
Avatar billede Smitche Praktikant
05. januar 2013 - 22:31 #7
Vil det sige at i mit tilfælde jeg ikke behøver at tage højde for XSS?
Avatar billede erikjacobsen Ekspert
05. januar 2013 - 22:50 #8
Så vidt jeg kan se, gør du det modsatte: Gemmer efter htmlspecialchars. Det er li'som det jeg siger man aldrig skal gøre.

Ved du positivt at brugernavnet ikke kan indeholde html-tags, så BEHØVER du ikke lave htmlspecialchars på det ved udskrift som html, men det vil være fornuftigt at gøre alligevel. Så kan man nemlig ved kodelæsning umiddelbart se, at det er ufarligt.

Email-adressen i din kode kan derimod indholde html-tags, som jeg lige ser det, så hvis den nogensinde skal skrives ud igen, så skal den en tur igennem htmlspecialchars.

...så...ALTID htmlspecialchars.
Avatar billede Smitche Praktikant
05. januar 2013 - 23:07 #9
Men hvordan kan det være man aldrig må gemme tekst i databasen som html-encoded?
Avatar billede erikjacobsen Ekspert
05. januar 2013 - 23:13 #10
Står i #5
Avatar billede Smitche Praktikant
05. januar 2013 - 23:31 #11
Jeg har nok misforstået brugen af htmlspecialchars, så er stadig ikke helt med.

Hvis jeg gemmer input med htmlspecialchars så bliver fx <b>Hej</b> vist således, og hvis jeg ikke gemmer det med hemtlspecialchars bliver det vist som b (i bold). Så vidt jeg forstod var det meningen at HTML gemt i databasen ikke må blive fortolket i selve HTML-dokumentet, for at beskytte mod XSS?
Avatar billede Smitche Praktikant
05. januar 2013 - 23:34 #12
Har forstået det det således at man skal sanitere alt input og output for at beskytte sig mod XSS. Ud fra denne artikel om HTML sanitization.

http://en.wikipedia.org/wiki/HTML_sanitization
Avatar billede erikjacobsen Ekspert
05. januar 2013 - 23:39 #13
Det kommer jo an på hvad man vil. I wikipedia-artiklen vil man fx tillade et harmløst <b>-tag. Det vil du tilsyneladende ikke.

Og som der står i #5: "Naturligvis skal man bruge htmlspecialchars" - eller gøre noget andet - det er slet ikke til diskussion. Det skal man.

Men htmlspecialchars skal man gøre ved udskrift fra databasen, og ikke før man putter det i databasen. Ønsker man at tillade <b> med ikke tillade <script>, så er det fornuftigt nok at sortere fra, før man lægger det i databasen.
Avatar billede Smitche Praktikant
06. januar 2013 - 22:49 #14
erikjacobsen mange tak for dine svar.

Men hensyn det du skriver "Ellers er det jo altid sjovt som bruger at skrive en URL : ..../slet.php?id=118 - og se hvad der sker."

Kan man gøre noget mod det? Fx med URL rewrite?
Avatar billede erikjacobsen Ekspert
06. januar 2013 - 22:53 #15
Nej. Står der en værdi i en url, eller i et <form>-felt, må man forvente at brugeren ændrer det - for sjov eller for alvor. I begge tilfælde må det max gå ud over ham selv (m/k).
Avatar billede Smitche Praktikant
06. januar 2013 - 22:59 #16
Er der nogle andre tilfælde, udover når man henter data fra databasen til output, man er sårbar over for XSS?

Her tænker jeg på hvis man fx har en javascript alert funktion om en bruger sikker på om han/hun vil slette nyheden, hvis ja sendes id gennem URL'en, skal man tage højde for noget der?

Fx ligesom her:

//alert deleteNews
function deleteNews($newsidfk)
{
  var ans = window.confirm(Are you sure you want to delete this news post?');
  if (ans == true)
  {
    window.location.href="delete.php?news_id_fk="+$newsidfk;
  }
}
Avatar billede Smitche Praktikant
06. januar 2013 - 23:00 #17
Det er så via link at brugeren trykker på det ligesom her:

echo '<a class="btn-small btn-danger" onclick="deleteNews('. htmlspecialchars($newsidfk) .')" ">Slet nyhed</a>';
Avatar billede erikjacobsen Ekspert
06. januar 2013 - 23:01 #18
Når det er din egen tekst, så kan der ikke ske XSS.
Avatar billede erikjacobsen Ekspert
06. januar 2013 - 23:02 #19
Hvis $newsidfk kommer fra brugeren, så kan det gå galt. Det er ikke nok med htmlspecialchars, der skal også javascript encodes specialt med henblik på '-tegnet.
Avatar billede Smitche Praktikant
06. januar 2013 - 23:08 #20
Det kan man vel gøre med htmlspecialchars($newsid, ENT_QUOTES) så?
Det burde jeg måske putte på alle output?

Altså id'et kommer fra databaseudtræk, i dette tilfælde fra at vise nogle nyheder, her trykke brugeren så på et link og det link sender nyhedsid'et videre gennem URL'en.
Avatar billede erikjacobsen Ekspert
06. januar 2013 - 23:11 #21
Nej, det skal javascript encodes. htmlspecialchars er ikke nok.

Hvis du ved det er et tal, behøver man ikke gøre noget. Et tal kan aldrig gå galt.
Avatar billede Smitche Praktikant
06. januar 2013 - 23:18 #22
Okay, men der bruger nemlig også en streng et andet sted. Kan man bruge encodeURI() function til javascript encoding?
Avatar billede erikjacobsen Ekspert
06. januar 2013 - 23:22 #23
Nej, det er til URL-encoding (ok, URI-encoding) - der er andre regler.

Javascript encode hedder sommetider noget sjovt i et programmeringssprog:  http://php.net/json_encode
Avatar billede Smitche Praktikant
06. januar 2013 - 23:34 #24
Mange tak! Så egentlig burde man, når man arbejder med PHP, gøre det med JSON?
Avatar billede erikjacobsen Ekspert
07. januar 2013 - 13:48 #25
Nej. Du bruger javascript enocde (json_encode), når du skal skrive ukendt tekst i noget javascript.
Avatar billede Smitche Praktikant
14. januar 2013 - 15:36 #26
Mange tak for din hjælp. Jeg lukker denne tråd med eget svar, da jeg kan se du:
http://jeg.samler.slet.ikke.paa.point.tak.erikjacobsen.com/
Avatar billede Smitche Praktikant
14. januar 2013 - 15:36 #27
Lukket
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester