Single login script dubbele hash
Ik moest even snel een single login scriptje hebben tegen pottenkijkers :-)
Het werkt prima alleen bij inloggen wordt een hash opgeslagen, en daarna wordt deze vergeleken en wordt de hash 'vernieuwt'. Omdat ik een location header gebruik na het inloggen wordt dit beide uitgevoerd en dus twee hashes gegenereerd. Het is geen ramp, maar was benieuwd of iemand hier een idee over heeft. En gelijk een goed moment voor een check, aangezien twee ogen meer zien dan één.
Code (php)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?php
defined('ROOT') or define('ROOT',dirname(__FILE__));
require_once ROOT . '/_config.php';
define('ACCESS_PASSWORD', '$argon2i$v=19$m=65536,t=4,p=1$bXFoSC9vYTZhWmNJVGtwYQ$FMYDTnYGuvtBjGM+oJTp/ByjXr1veFG5KBxVjssLKEM');
function generatehash() {
return base64_encode(openssl_random_pseudo_bytes(32));
}
$timenow = (new \DateTime())->format('Y-m-d H:i:s');
$access = false;
if($_SERVER['REMOTE_ADDR'] == REMOTE_ADDR) {
$access = true;
} else if(isset($_SESSION['hash'])) {
try {
$stmt = $db->prepare("
SELECT
hash,
hashtime
FROM
" . TABLE_PREFIX . "access
WHERE
hash = '" . $_SESSION['hash'] . "'
AND
hashtime >= (NOW() - INTERVAL 30 MINUTE)
LIMIT
0,1
");
$stmt->execute();
if($stmt->rowCount() > 0) {
$stmt = $db->prepare("
INSERT IGNORE
INTO " . TABLE_PREFIX . "access
(
hash,
hashtime
)
VALUES
(
:hash,
:hashtime
)
");
$stmt->bindParam(':hash', $hash, PDO::PARAM_STR);
$stmt->bindParam(':hashtime', $hashtime, PDO::PARAM_STR);
$hash = generatehash();
$hashtime = $timenow;
$stmt->execute();
if($stmt->rowCount() > 0) {
$access = true;
unset($_SESSION['hash']);
$_SESSION['hash'] = $hash;
}
} else {
unset($_SESSION['hash']);
$notes[] = 'Your session has expired';
}
}
catch(PDOException $e)
{
$debugMsg = 'Line: '.$e->getLine().'<br />'
.'File: '.$e->getFile().'<br />'
.'Message: '.$e->getMessage();
echo '<p>' . $debugMsg . '</p>';
}
} else if($_SERVER['REQUEST_METHOD'] == "POST") {
if(isset($_POST['password']) && password_verify($_POST['password'], ACCESS_PASSWORD)) {
try {
$stmt = $db->prepare("
INSERT IGNORE
INTO " . TABLE_PREFIX . "access
(
hash,
hashtime
)
VALUES
(
:hash,
:hashtime
)
");
$stmt->bindParam(':hash', $hash, PDO::PARAM_STR);
$stmt->bindParam(':hashtime', $hashtime, PDO::PARAM_STR);
$hash = generatehash();
$hashtime = (new \DateTime())->format('Y-m-d H:i:s');
$stmt->execute();
if($stmt->rowCount() > 0) {
$access = true;
unset($_SESSION['hash']);
$_SESSION['hash'] = $hash;
header('Location: ' . $_SERVER['SCRIPT_NAME']);
exit();
}
} catch(PDOException $e) {
$debugMsg = 'Line: '.$e->getLine().'<br />'
.'File: '.$e->getFile().'<br />'
.'Message: '.$e->getMessage();
echo '<p>' . $debugMsg . '</p>';
}
} else $notes[] = 'Password incorrect';
}
if($access === false) {
?>
<!-- Hier het inlogformulier -->
<?php
exit();
}
?>
defined('ROOT') or define('ROOT',dirname(__FILE__));
require_once ROOT . '/_config.php';
define('ACCESS_PASSWORD', '$argon2i$v=19$m=65536,t=4,p=1$bXFoSC9vYTZhWmNJVGtwYQ$FMYDTnYGuvtBjGM+oJTp/ByjXr1veFG5KBxVjssLKEM');
function generatehash() {
return base64_encode(openssl_random_pseudo_bytes(32));
}
$timenow = (new \DateTime())->format('Y-m-d H:i:s');
$access = false;
if($_SERVER['REMOTE_ADDR'] == REMOTE_ADDR) {
$access = true;
} else if(isset($_SESSION['hash'])) {
try {
$stmt = $db->prepare("
SELECT
hash,
hashtime
FROM
" . TABLE_PREFIX . "access
WHERE
hash = '" . $_SESSION['hash'] . "'
AND
hashtime >= (NOW() - INTERVAL 30 MINUTE)
LIMIT
0,1
");
$stmt->execute();
if($stmt->rowCount() > 0) {
$stmt = $db->prepare("
INSERT IGNORE
INTO " . TABLE_PREFIX . "access
(
hash,
hashtime
)
VALUES
(
:hash,
:hashtime
)
");
$stmt->bindParam(':hash', $hash, PDO::PARAM_STR);
$stmt->bindParam(':hashtime', $hashtime, PDO::PARAM_STR);
$hash = generatehash();
$hashtime = $timenow;
$stmt->execute();
if($stmt->rowCount() > 0) {
$access = true;
unset($_SESSION['hash']);
$_SESSION['hash'] = $hash;
}
} else {
unset($_SESSION['hash']);
$notes[] = 'Your session has expired';
}
}
catch(PDOException $e)
{
$debugMsg = 'Line: '.$e->getLine().'<br />'
.'File: '.$e->getFile().'<br />'
.'Message: '.$e->getMessage();
echo '<p>' . $debugMsg . '</p>';
}
} else if($_SERVER['REQUEST_METHOD'] == "POST") {
if(isset($_POST['password']) && password_verify($_POST['password'], ACCESS_PASSWORD)) {
try {
$stmt = $db->prepare("
INSERT IGNORE
INTO " . TABLE_PREFIX . "access
(
hash,
hashtime
)
VALUES
(
:hash,
:hashtime
)
");
$stmt->bindParam(':hash', $hash, PDO::PARAM_STR);
$stmt->bindParam(':hashtime', $hashtime, PDO::PARAM_STR);
$hash = generatehash();
$hashtime = (new \DateTime())->format('Y-m-d H:i:s');
$stmt->execute();
if($stmt->rowCount() > 0) {
$access = true;
unset($_SESSION['hash']);
$_SESSION['hash'] = $hash;
header('Location: ' . $_SERVER['SCRIPT_NAME']);
exit();
}
} catch(PDOException $e) {
$debugMsg = 'Line: '.$e->getLine().'<br />'
.'File: '.$e->getFile().'<br />'
.'Message: '.$e->getMessage();
echo '<p>' . $debugMsg . '</p>';
}
} else $notes[] = 'Password incorrect';
}
if($access === false) {
?>
<!-- Hier het inlogformulier -->
<?php
exit();
}
?>
Gewijzigd op 08/11/2019 16:03:32 door Michael -
Daarbij, stel dat je dadelijk een complete applicatie hebt die zo werkt, en je hebt dan AJAX-calls enzo... dat wordt op den duur een behoorlijke puinzooi?
En dan, stel dat je sessie expired (standaard na ca. 24 minuten) en je submit dan een zwik werk, dan wil je eigenlijk dat je sessie onder water doorstart zonder dat je je werk kwijt bent.
Dan nog wat inhoudelijke opmerkingen:
- een prepared statement met hierin een $_SESSION geconcateneerd? errrr
- waarom een unset en daarna een assignment? doe de assignment meteen?
- $timenow en $hashtime zullen meestal dezelfde waarde hebben, houd het op 1 variabele?
- heb je al gekeken of je access table niet langzaan volloopt op deze manier?
- implicaties van rowCount() > 0 tov rowCount() = 1 al bekeken?
- misschien is het ook handig om de sessies zelf op te slaan in de database?
- je gebruikt nergens session_regenerate_id(), waarom (niet)?
- waar wordt $notes gebruikt?
- dat hele INSERT-gedoe, je doet in wezen 2x nagenoeg hetzelfde, kun je dat niet inkorten? het enige wat verschilt is de conditie vooraf, en de actie achteraf
- zou het record niet gelocked moeten worden tijdens het inspecteren en bijwerken?
meh.
Quote:
Dit is dus een soort "nonce" ofzo?
Het is niet voor eenmalig gebruik.
Quote:
Mja, dat gaat waarschijnlijk wel werken. Maar alles dichtgooien op IP lijkt mij om kortstondig pottenkijkers buiten te houden afdoende? Of voorziet dit juist in dynamische IP's ofzo?
Voor thuis is dit handig ja, maar de meeste tijd ben ik dat niet dus dan is het handig dat er een mogelijkheid is om in te loggen ;-)
Quote:
Daarbij, stel dat je dadelijk een complete applicatie hebt die zo werkt, en je hebt dan AJAX-calls enzo... dat wordt op den duur een behoorlijke puinzooi?
Waarom zou dat een puinzooi zijn? Het gaat er om dat je zo eenvoudig één of meerder pagina's kan afschermen.
Quote:
En dan, stel dat je sessie expired (standaard na ca. 24 minuten) en je submit dan een zwik werk, dan wil je eigenlijk dat je sessie onder water doorstart zonder dat je je werk kwijt bent.
Dat zal niet heel snel gebeuren. Daarnaast als je formulieren waar je een halfuur mee bezig bezig om in te vullen zou ik sowieso tussentijds opslaan.
Quote:
een prepared statement met hierin een $_SESSION geconcateneerd? errrr
:-)
Quote:
waarom een unset en daarna een assignment? doe de assignment meteen?
Ik sommige gevallen gebeurt het wel eens dat een sessie niet goed wordt overschreven. Hiermee voorkom je dat dus. Meer uit voorzorg dus.
Quote:
$timenow en $hashtime zullen meestal dezelfde waarde hebben, houd het op 1 variabele?
Ze zijn het zelfde, maar ik zie nu inderdaad de verwarring. Ik zal dit oplossen.
Quote:
heb je al gekeken of je access table niet langzaan volloopt op deze manier?
Het scheelt dat er niet dagelijks verschillende mensen inloggen, maar ja er worden al snel meerdere hashes opgeslagen. Opzich is dit niet echt een probleem.
Quote:
implicaties van rowCount() > 0 tov rowCount() = 1 al bekeken?
Nee, wat bedoel je hiermee? Ik doe altijd > 0. Dat is een gewoonte ;-)
Quote:
misschien is het ook handig om de sessies zelf op te slaan in de database?
Waarom zou je dit willen?
Quote:
je gebruikt nergens session_regenerate_id(), waarom (niet)?
Nee, waarom zou je die willen gebruiken?
Quote:
waar wordt $notes gebruikt?
Om meldingen weer te geven ;-)
Quote:
dat hele INSERT-gedoe, je doet in wezen 2x nagenoeg hetzelfde, kun je dat niet inkorten? het enige wat verschilt is de conditie vooraf, en de actie achteraf
Ja klopt helemaal. Ziet er inderdaad niet logisch uit. Ik zal hierna kijken.
Quote:
zou het record niet gelocked moeten worden tijdens het inspecteren en bijwerken?
Geen idee? Inprincipe bestaat deze maar één keer en hij wordt nooit bijgewerkt, maar een nieuwe aangemaakt. Dus ik zou denken nee.
Gewijzigd op 11/11/2019 12:54:24 door Michael -