Ik stuurde een $_POST van een formulier altijd naar dezelfde pagina en dan ving ik het bovenaan mijn script af.
Alleen doordat je dan dubbele posts kan krijgen door per ongeluk de pagina te verversen wil ik nu mijn formulier waardes doorsturen naar login2.php en dan laten inloggen.

Alleen hoe weet ik dat die gene van mijn loginpagina komt? Is het slim om een hidden $_POST mee te sturen? Maar dat kan weer onderschept worden lees ik?
Hoe pakken jullie dat aan?
@ -Aar-: Zal ik die md5 sessie op basis van $_SERVER['REMOTE_ADDR'] maken?
is dat slim?
Ik zou het niet doen, omdat een IP-adres meerdere gebruikers kan bevatten. Liever uniqid() ofzo.
- Aar - op 20/08/2015 15:45:21

Ik zou het niet doen, omdat een IP-adres niet uniek is. Liever uniqid() ofzo.


Maar ik zie op php.net dat deze functie een id maakt op basis van de current-time in microseconds. Dat kan ik op de pagina er na toch niet meer verifieren omdat de microsecondes dan al gewijzigd zijn?
Wel als je dezelfde waarde bewaard in je sessie. Bovendien kun je tevens een timeout maken .
Danny von Gaal op 20/08/2015 16:33:37

[quote="- Aar - op 20/08/2015 15:45:21"]
Ik zou het niet doen, omdat een IP-adres niet uniek is. Liever uniqid() ofzo.


Maar ik zie op php.net dat deze functie een id maakt op basis van de current-time in microseconds. Dat kan ik op de pagina er na toch niet meer verifieren omdat de microsecondes dan al gewijzigd zijn?
[/quote]
Je slaat die waarde op in een sessie, dus dan kan je het prima later nog vergelijken.
- Aar - op 20/08/2015 15:45:21

Ik zou het niet doen, omdat een IP-adres niet uniek is.


Een ip-adres is wel uniek.
Wat jij bedoelt is dat je er niet van op aan kunt omdat het kan veranderen. (mobiel b.v.)
Ja, dat bedoelde ik. Maar er kunnen meerdere gebruikers op zitten, en deze kan veelvuldig wijzigen op een mobiel netwerk. Wel weet ik dat dat gebeurt als je dataverbinding langdurig wegvalt.
Nou ik heb wat gemaakt en ben benieuwd of jullie nog aanmerkingen hebben.

Bovenaan mijn loginpagina maak ik een md5 hash aan op basis van ip-adres. (Sorry -Aar- maar die uniqueid begreep ik niet helemaal en deze applicatie is alleen maar beschikbaar op kantoor waar iedere pc een unieke ip-adres heeft).

<?php
session_start();
$_SESSION['hash'] = md5($_SERVER['REMOTE_ADDR']);
?>



<form name="login" method="post" action="login2.php">
				<table class="login">
					<tr>
						<td class="label">&nbsp;</td>
						<td class="invoerveld"><p class="login verplicht">Verplichte velden zijn gemarkeerd met een *</p></td>
					</tr>
					<tr>
						<td class="input"><p class="login">Gebruikersnaam *</p></td>
						<td class="input"><input type="text" name="gebruikersnaam" id="gebruikersnaam" maxlength="100"/></td>
					</tr>
					<tr>
						<td class="input"><p class="login">Wachtwoord *</p></td>
						<td class="input"><input type="password" name="wachtwoord" id="wachtwoord" maxlength="20"/></td>
					</tr>
					<tr>
						<td>&nbsp;</td>
						<td>
						<?php
							// Laat errors zien bij het missen van gebruikersnaam of wachtwoord
							if (isset($_GET['error'])) {
								if ($_GET['error'] == "1") {
									echo "<div class='error'>Er is geen gebruikersnaam ingevuld</div>";
								} elseif ($_GET['error'] == "2") {
									echo "<div class='error'>Er is geen wachtwoord ingevuld</div>";
								}
							}
						?>
						</td>
					</tr>
					<tr>
						<td><input type="hidden" value="<?php echo $_SESSION['hash']; ?>" name="client-hash" /></td>
						<td><input type="submit" value="Inloggen"/></td>
					</tr>
				</table>
			</form>


Vervolgens handel ik alles af in een nieuw bestand zodat ik geen last heb van dubbele posts:

<?php
session_start();

// Laat errors zien
error_reporting(E_ALL);
ini_set('display_errors', 1);

// Maak een connectie met de database
include("connect.php");

// Controleer of de gebruiker wel van onze login pagina komt
if (isset($_SESSION['hash'])) {
	
	// Controleer of de sessie overeenkomt met de login pagina
	if ($_SESSION['hash'] == md5($_SERVER['REMOTE_ADDR'])) {

		// Controleer of gebruikersnaam en wachtwoord is ingevuld
		if (isset($_POST['gebruikersnaam'], $_POST['wachtwoord'])) {

			// Controleer of gebruikersnaam niet leeg is
			if (empty($_POST['gebruikersnaam'])) {
				header( "refresh:0;url=login.php?error=1" ); // Error1 = gebruikersnaam is leeg
				
			// Controleer of wachtwoord niet leeg is
			} elseif (empty($_POST['wachtwoord'])) {
				header( "refresh:0;url=login.php?error=2" ); // Error2 = wachtwoord is leeg
				
			} else {
				// Voorkom SQL injection
				$gebruikersnaam = mysqli_real_escape_string($conn, $_POST['gebruikersnaam']);
				$wachtwoord = mysqli_real_escape_string($conn, $_POST['wachtwoord']);
				
				// Haal gebruiker op
				$sql = "SELECT gebruikersnaam, wachtwoord FROM gebruikers WHERE gebruikersnaam = '$gebruikersnaam'";
				$result = $conn->query($sql);
				
				// Ga door als er een gebruiker met dezelfde gebruikersnaam aanwezig is
				if ($result->num_rows > 0) {
					while($row = $result->fetch_assoc()) {
						if (password_verify($wachtwoord, $row['wachtwoord'])) {
							$_SESSION['gebruikersnaam'] = $row['gebruikersnaam'];
							header( "refresh:0;url=index.php" );
						} else {
							echo 'Ongeldige gebruikersnaam/wachtwoord combinatie.';
						}
					}
				} else {
					echo "Kan geen gebruiker vinden met deze gebruikersnaam.";
				}
			}
			
		} else {
			// Geen $_POST sessie? Naar login.php!
			header( "refresh:0;url=login.php" );
		}
		
	} else {
		header( "refresh:0;url=login.php" );
	}
	
} else {
	// Geen sessie?? Wegwezen!
	header( "refresh:0;url=login.php" );
}

?>
Danny von Gaal op 21/08/2015 14:36:13

Bovenaan mijn loginpagina maak ik een md5 hash aan op basis van ip-adres. (Sorry -Aar- maar die uniqueid begreep ik niet helemaal en deze applicatie is alleen maar beschikbaar op kantoor waar iedere pc een unieke ip-adres heeft).


Het stelt bar weinig voor:

<?php
echo md5(uniqid());
?>

En de uitwerking:
https://3v4l.org/i9HHT

Ingeval van een loginformulier lijkt het mij logisch dat je zowel een gebruikersnaam als een wachtwoord invoert, ik weet niet of het heel veel zin heeft gebruikers hier expliciet op te wijzen. Volgens mij kun je gewoon volstaan met "login mislukt" en eventueel al voor verzenden voorkomen dat een (half) leeg formulier verzonden wordt.

In andere formulieren zou het handig / gebruiksvriendelijk zijn als de reeds ingevulde velden teruggeplaatst worden bij een foutmelding met eventueel een hint van wat er fout was. Een loginformulier leent zich hier overigens niet echt voor (je zou in dat geval ook geen hints moeten geven over wat er niet klopt).

Je token wordt nergens ongeldig gemaakt en is ook niet random. Dat maakt het token niet erg geschikt voor zijn doel. De opzet met uniqid() is geschikter. En zorg dat deze meteen ongeldig wordt gemaakt nadat je deze hebt gebruikt. Voorbeeld van een "random" token functie:
<?php
function getRandomToken() {
    return sha1(uniqid(mt_rand(), true));
}
?>


Dan is een token formulier-invoer waarbij het formulierveld een bepaalde waarde zou moeten hebben waarop gevalideerd moet worden. De eerste controle die je dus zou moeten uitvoeren in login2.php zou eigenlijk een check moeten zijn of je met een POST request van doen hebt. Daarna zou je in eerste instantie kunnen kijken of het token voldoet en de rest van de velden valideren.

Je wachtwoord escapen met mysqli_real_escape_string (waarom niet $conn->real_escape_string()?) lijkt mij in dit geval niet goed, want als er in het wachtwoord karakters zitten die in de MySQL context geescaped zouden worden, dan mislukt je password_verify() aanroep. Je gebruikt $wachtwoord ook helemaal niet in een query...

De syntax header("refresh:0;url=..." ) ben ik niet bekend mee. Waarom gebruik je geen header('Location: ...')? Daarnaast zou na elke header die je redirect een exit moeten staan anders gaat de executie van het script gewoon door. Headers worden pas aan het einde van het script / voor het begin van output verstuurd.

Als je dus zoiets hebt:
<?php
if ($als_de_authenticatie_check_mislukt) {
    header('Location: ergens_anders.php');
}
// CODE DIE UITGEVOERD WORDT ALS AUTHENTICATIE SLAAGT
// ...
?>

En de authenticatie-check mislukt, dan wordt je header geset, vervolgens wordt CODE DIE UITGEVOERD WORDT ALS AUTHENTICATIE SLAAGT uitgevoerd, en dan word je geredirect.

Het toevoegen of weglaten van een exit kan het verschil maken tussen een veilige of een onveilige applicatie.

Of nog beter, maak een functie/methode zodat je dit nooit vergeet:
<?php
function redirect($url) {
    header('Location: '.$url);
    exit;
}
?>

Je kunt hier ook nog HTTP status codes aan toevoegen als je wilt (bijvoorbeeld HTTP/1.1 303 See Other), en officieel moet $url een volledige URL zijn.

Reageren