TOPIC: het ultieme veilige formulier
tennapel op 21 februari 2006 #
Velen van ons werken met de combinatie van Apache, MySQL en PHP. Velen van ons werken ook met formulieren op hun websites. Het beveiligen van die formulieren blijft een vraagstuk apart en ik wil dit topic starten om gezamenlijk het ultiem beveiligde forumlier te maken.
Iedereen kan dan gebruik maken van de code.
Laten we het volgende form als voorbeeld nemen:
<form method="post" action="form_verwerking.php">
<p>
<label>Naam: <input type="text" name="naam"></label><br>
<label>E-mail: <input type="text" name="email"></label><br>
<label>Reactie: <textarea name="reactie"></textarea></label><br>
<input type="submit">
</p>
</form>
Op form_verwerking.php lees je dan de variabelen uit in PHP
$naam = $_POST["naam"];
$email = $_POST["email"];
$reactie = $_POST["reactie"];
Het beveiligen doe je door jezelf er van te verzekeren dat in de $_POST variabelen geen kwalijke code zit die misbruik kan maken door je form, door middel van misbruik te maken van een PHP e-mail procedure (waarbij je de resultaten van een form ergens heen laat mailen), of door sql-injectie wanneer je de resultaten van het form in een (MySQL) database opslaat.
Als reacties graag je bijdrage aan hoe dit form te beveiligen:
[list:7411ac50bd]Op een gebruikersvriendelijke manier, zodat mensen die per ongeluk iets verkeerds intikken wel de kans krijgen om wat ingevoerd is te herstellen[/list:u:7411ac50bd]
[list:7411ac50bd]Op een generieke manier, dus bij voorkeur door functies aan te roepen, zodat je de code makkelijk kan herbruiken of inpassen in een bestaande website[/list:u:7411ac50bd]
Ik hoop dat we gezamenlijk de beste oplossing neer kunnen zetten; goede links naar heldere voorbeelden op het internet zijn natuurlijk ook welkom, maar uiteindelijk is het handig om een compleet voorbeeld hier uit te werken. Het lastige is namelijk om al die 'best practices' samen te brengen.
Kyokushinkai op 21 februari 2006 #
Laat ik het eens proberen.
Dit is de tabel structuur:
$query = "CREATE TABLE gegevens (
ID INTEGER auto_increment PRIMARY KEY,
naam VARCHAR(100),
email VARCHAR(100),
reactie MEDIUMTEXT)";
Dit is de php code, inclusief de functies. Die functies kun je het beste in een appart bestand zetten, maar aangezien dat misschien wat verwarrend kan zijn voor bepaalde mensen heb ik het eerst maar zo gelaten.
Opmerking: Ik heb de database code niet getest, aangezien ik momenteel even geen database bij de hand heb.
<?php
//##==-- Functies --==##
// MySQL database gegevens
$dbHost = "localhost";
$dbUser = "";
$dbPass = "";
$dbName = "";
// Connectie met database
function dbConnect() {
global $dbHost;
global $dbUser;
global $dbPass;
global $dbName;
$link = @mysql_connect($dbHost, $dbUser, $dbPass);
if (!$link) {
echo "<p>Kon geen verbinding maken met database server</p>";
exit;
}
if (!mysql_select_db($dbName)) {
echo "<p>Kon database niet vinden</p>";
exit;
}
return $link;
}
//------------------------------------------------------------------------------
// Functie om te controleren of het email adres de juiste samenstelling heeft
function checkEmail($email) {
$regexp = "^[_a-z0-9-]+(.[_a-z0-9-]+)*@[a-z0-9-]+(.[a-z0-9-]+)*(.[a-z]{2,3})$";
if (eregi($regexp, $email)) {
return true;
} else {
return false;
}
}
//------------------------------------------------------------------------------
//##==-- Resterende php code --==##
// Wanneer de verstuur knop ingedrukt is
if (isset($_POST['verstuur'])) {
// Variabelen ophalen
$naam = $_POST["naam"];
$email = $_POST["email"];
$reactie = $_POST["reactie"];
// Lege melding variabele starten
$melding = '';
// Wanneer er geen email is ingevuld
if ($email == '') {
// Toevoegen aan melding
$melding .= "<p>Er is geen email adres opgegeven.</p>n";
// Wanneer het email adres een verkeerde samenstelling heeft
} elseif (!checkEmail($email)) {
// Toevoegen aan melding
$melding .= "<p>Het email adres is niet juist. Pas het a.u.b. aan.</p>n";
}
// Wanneer er geen naam is ingevuld
if ($naam == '') {
// Toevoegen aan melding
$melding .= "<p>Er is geen naam opgegeven.</p>n";
}
// Wanneer er geen reactie is ingevuld
if ($reactie == '') {
// Toevoegen aan melding
$melding .= "<p>Er is geen reactie opgegeven.</p>n";
}
// Wanneer alles door de controle gekomen is
if ($melding == '') {
// Functie uitvoeren om contact te maken met de database
$link = dbConnect();
// Addslashes om verkeerde invoer te voorkomen
$snaam = addslashes($naam);
$semail = addslashes($email);
$sreactie = addslashes($reactie);
// Query aanmaken
$query = "INSERT INTO gegevens VALUES ('', '$snaam', '$semail' , '$sreactie')";
// Query uitvoeren
$result = mysql_query($query);
// Wanneer de query mislukt is
if (!$result) {
echo "<p>Kon de gegevens niet opslaan in de database.<br />" . mysql_error() . "</p>";
exit;
}
echo "Gegevens verstuurd!";
// Script stoppen
exit;
// Wanneer er fouten zijn opgetreden bij de controle
} else {
echo $melding;
} // if ($melding == '')
} // if (isset($_POST['verstuur']))
//------------------------------------------------------------------------------
?>
<form action="<?php echo basename($_SERVER['PHP_SELF']); ?>" method="POST">
<p>
<label>
Naam: <input type="text" name="naam" value="<?php echo $naam; ?>" />
</label><br />
<label>
E-mail: <input type="text" name="email" value="<?php echo $email ?>" />
</label><br />
<label>
Reactie: <textarea name="reactie"><?php echo $reactie; ?></textarea>
</label><br />
<input type="submit" name="verstuur" value="Verstuur" />
</p>
</form>
tennapel op 21 februari 2006 #
iBert Past JavaScript als secundaire validatie ook in dit verhaal?
Ja natuurlijk, maar pas als secundaire validatie. Voor het melden van gebruikersfouten is dat prima, maar het zal geen kwaadwillende hacker tegenhouden.
nipro op 21 februari 2006 #
$naam = $_POST["naam"];
$email = $_POST["email"];
$reactie = $_POST["reactie"];
in
$Naam = $_POST["naam"];
$NaamSlash = addslashes($Naam);
$Mail = $_POST["email"];
$MailSlash = addslashes($Mail);
$Reactie = $_POST["reactie"];
$ReactieSlash = addslashes($Reactie);
voor als iemand bijv iets als "'t" of "zo'n" in wil vullen, krijgt deze een extra slash voor de '. Stukje veiligheid lijkt me..![]()
iBert op 21 februari 2006 #
nipro$naam = $_POST["naam"]; $email = $_POST["email"]; $reactie = $_POST["reactie"];in$Naam = $_POST["naam"]; $NaamSlash = addslashes($Naam); $Mail = $_POST["email"]; $MailSlash = addslashes($Mail); $Reactie = $_POST["reactie"]; $ReactieSlash = addslashes($Reactie);voor als iemand bijv iets als "'t" of "zo'n" in wil vullen, krijgt deze een extra slash voor de '. Stukje veiligheid lijkt me..
Is reeds gezegd:
Kyokushinkai // Addslashes om verkeerde invoer te voorkomen $snaam = addslashes($naam); $semail = addslashes($email); $sreactie = addslashes($reactie);
nipro op 21 februari 2006 #
iBertnipro [code] ..Is reeds gezegd:
Kyokushinkai // Addslashes om verkeerde invoer te voorkomen $snaam = addslashes($naam); $semail = addslashes($email); $sreactie = addslashes($reactie);
Sorry, ik had er over heen gekeken
ops:
Sen op 21 februari 2006 #
Mysql_real_escape_string() is nog veiliger dan addslashes() omdat die ook rekening houdt met de gebruikte character encoding. Meer info: http://shiflett.org/archive/184 En als je PHP5 kan gebruiken met het nieuwe Mysqli object dan zijn prepared statements ook een goede oplossing. Query en variabelen zijn dan volledig gescheiden.
tennapel op 21 februari 2006 #
Sen; prima info, alleen de bedoeling van dit topic is om uiteindelijk de code te hebben voor Het Perfecte Formulier. Ik zal een poging doen om de code te integreren. ps: het lijkt me handig om uit te gaan van PHP4, er zijn zoveel providers en websites die op PHP4 draaien dat ondersteuning daarvan wel gewenst lijkt.
iBert op 21 februari 2006 #
Het lijkt me ook geen slecht idee om naast het toevoegen van slashes de input van eventuele HTML-code te strippen.
$naam = strip_tags($naam);
$email = strip_tags($email);
$reactie = strip_tags($reactie);
tijn22 op 21 februari 2006 #
Op de site die ik onderhoud, heb ik bij het reserveringssysteem deze code in de php staan:
Zorgt ervoor dat alleen POST werkt (niet GET) en checkt het domein van de vorige pagina, en doet nog wat dingen. Weet niet meer waar ik het weg heb, maar het is wel handig, en lijkt me relatief veilig.
Deze php code komt dus bovenaan in form_verwerking.php, en de domeinnaam erin moet je aanpassen aan je eigen domeinnaam.
<?php
// First, make sure the form was posted from a browser.
// For basic web-forms, we don't care about anything
// other than requests from a browser:
if(!isset($_SERVER['HTTP_USER_AGENT'])){
die("Forbidden - You are not authorized to view this page");
exit;
}
// Make sure the form was indeed POST'ed:
// (requires your html form to use: action="post")
if(!$_SERVER['REQUEST_METHOD'] == "POST"){
die("Forbidden - You are not authorized to view this page");
exit;
}
// Host names from where the form is authorized
// to be posted from:
$authHosts = array("JOUWDOMEINNAAM.COM");
// Where have we been posted from?
$fromArray = parse_url(strtolower($_SERVER['HTTP_REFERER']));
// Test to see if the $fromArray used www to get here.
$wwwUsed = strpos($fromArray['host'], "www.");
// Make sure the form was posted from an approved host name.
if(!in_array(($wwwUsed === false ? $fromArray['host'] : substr(stristr($fromArray['host'], '.'), 1)), $authHosts)){
header("HTTP/1.0 403 Forbidden");
exit;
}
// Attempt to defend against header injections:
$badStrings = array("Content-Type:",
"MIME-Version:",
"Content-Transfer-Encoding:",
"bcc:",
"cc:");
// Loop through each POST'ed value and test if it contains
// one of the $badStrings:
foreach($_POST as $k => $v){
foreach($badStrings as $v2){
if(strpos($v, $v2) !== false){
header("HTTP/1.0 403 Forbidden");
exit;
}
}
}
// Made it past spammer test, free up some memory
// and continue rest of script:
unset($k, $v, $v2, $badStrings, $authHosts, $fromArray, $wwwUsed);
?>
Sen op 21 februari 2006 #
tennapel Sen; prima info, alleen de bedoeling van dit topic is om uiteindelijk de code te hebben voor Het Perfecte Formulier. Ik zal een poging doen om de code te integreren.
Hier is een functie om te escapen. Real_escape is pas beschikbaar vanaf versie 4.3
function escapeSQL($str)
{
if (get_magic_quotes_gpc())
{
$str = stripslashes($str);
}
switch (function_exists('mysql_real_escape_string'))
{
case true:
return mysql_real_escape_string($str);
break;
default:
return mysql_escape_string($str);
break;
}
}
Sen op 21 februari 2006 #
iBert Het lijkt me ook geen slecht idee om naast het toevoegen van slashes de input van eventuele HTML-code te strippen.
Check ook is de KSES filter. De toegelaten tags bij strip_tags worden niet gecontroleerd op attributen met potentieel gevaarlijke javascript code. KSES houdt hier allemaal rekening mee.
kaydie op 21 februari 2006 #
Voor dat we het formulier in behandeling laten nemen, eerst een anti spammers filter?
<?php
// Wordt dit formulier gepost vanaf een web browser?
if(!isset($_SERVER['HTTP_USER_AGENT'])){
die("Forbidden - You are not authorized to view this page");
exit;
}
// Is het formulier gepost? (method="post"):
if(!$_SERVER['REQUEST_METHOD'] == "POST"){
die("Forbidden - You are not authorized to view this page");
exit;
}
// De host namen waar vanaf gepost mag worden:
$authHosts = array("domain.com", "domain2.com", "domain3.com");
// De plek waarvanaf het formulier gepost wordt:
$fromArray = parse_url(strtolower($_SERVER['HTTP_REFERER']));
// Checkt of www in de referer voorkomt:
$wwwUsed = strpos($fromArray['host'], "www.");
// Check of de url waarvanaf het formulier gepost wordt ok is.
if(!in_array(($wwwUsed === false ? $fromArray['host'] : substr(stristr($fromArray['host'], '.'), 1)), $authHosts)){
logBadRequest();
header("HTTP/1.0 403 Forbidden");
exit;
}
// Check tegen header injections in de POST headers:
$badStrings = array("Content-Type:",
"MIME-Version:",
"Content-Transfer-Encoding:",
"bcc:",
"cc:");
foreach($_POST as $k => $v){
foreach($badStrings as $v2){
if(strpos($v, $v2) !== false){
header("HTTP/1.0 403 Forbidden");
exit;
}
}
}
// Einde anti spammmers filter, maak geheugen vrij en ga verder.
unset($k, $v, $v2, $badStrings, $authHosts, $fromArray, $wwwUsed);
?>
tennapel op 21 februari 2006 #
Kaydie, je anti-spammer/hacker routine levert een probleem op bij subdomeinen, je checkt op de string 'www'. Is het niet beter om de boel te 'exploden' op de punt in de url en het resultaat van de explode in een array te zetten?
Quack op 22 februari 2006 #
Mijn 2 centjes.. heeft bij mijn vorige werkgever al een aantal sites van de ondergang gered. [list:8b0ac5696b]Gebruik geen registered globals.[/list:u:8b0ac5696b] [list:8b0ac5696b]Zet geen bestanden met configuraties in je document root. Stop configuraties in een bestand wat buiten de docroot staat en include deze.[/list:u:8b0ac5696b] [list:8b0ac5696b]Zorg dat de document root niet schrijfbaar is voor de user waar onder de webserver draait.[/list:u:8b0ac5696b] [list:8b0ac5696b]Als je toch ergens moet kunnen schrijven, zorg dan dat in die dir geen php bestanden uitgevoerd kunnen worden[/list:u:8b0ac5696b] [list:8b0ac5696b]Controleer data voordat je de boel de database in schiet. Is de data niet te groot, als je een getal verwacht, is het dan ook een getal, etc.[/list:u:8b0ac5696b] [list:8b0ac5696b]In je php.ini: allow_url_fopen = Off[/list:u:8b0ac5696b] En eigenlijk de beste... [list:8b0ac5696b]Gebruik geen PHP![/list:u:8b0ac5696b]
pvdheijden op 22 februari 2006 #
Ik werd vandaag gewezen op Smarty, een template/formulier generator in PHP. Ik heb nog niet de tijd gehad om de boel te bekijken maar ik kan me voorstellen dat zij flink over beveiliging hebben nagedacht. http://smarty.php.net/
iBert op 22 februari 2006 #
Quack [list:8247eb1603]Gebruik geen PHP![/list:u:8247eb1603]
Waar baseer je dat op?
andreascreten op 22 februari 2006 #
De code die ik gebruik om te mailen:
<?php
$to = "email@domein.tld";
function isValidName($string){
return eregi("^[a-z0-9]*$",$string);
}
function isValidMailAddress($string){
return ereg("^[a-z0-9_.-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}$", $string);
}
if(!isValidName($_POST['name']))
die("Ongeldige naam opgegeven!");
if(!isValidMailAddress($_POST['email']))
die("Ongeldig e-mail adres opgegeven!");
$headers = "From: ".$_POST['name']." <".$_POST['email'].">rn";
$headers .= "Reply-To: ".$_POST['email']."rn";
$headers .= "Content-Type: text/plainrnrn";
mail($to, $subject, $_POST['body'], $headers);
echo "De e-mail is verstuurd!
?>
tennapel op 22 februari 2006 #
Quack Mijn 2 centjes..
Quack, heb jij het topic wel gelezen? Hoe aardig je 2 centjes je ook zijn, je hebt er niets aan als het gaat om het beveiligen van een formulier. En geen PHP gebruiken slaat natuurlijk helemaal nergens op.
Advertentie
Over dit topic
Gestart op 21 februari 2006 door tennapel
Laatste reactie door EL Mystica
Reageer op dit topic