xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
Commit 14b76a10a18045ae9d6b71614772ca2ba0653034

Checkpoint before non-storing tokens
Author: Catalin(ux) M. BOIE
Author date (UTC): 2014-12-20 09:03
Committer name: Catalin(ux) M. BOIE
Committer date (UTC): 2014-12-20 09:03
Parent(s): a87a60265dd93710118fc92b13121b7d7296823b
Signing key:
Tree: a0aaf599ccf216a479150034832b504b55b14a9a
File Lines added Lines deleted
TODO 33 0
inc/cache.inc.php 3 0
inc/struct.inc.php 2 1
inc/token.inc.php 5 4
tests/token.php 34 12
File TODO changed (mode: 100644) (index 59dfe3d..a503461)
... ... e-mail to him. Or I may comment on LWN or "PEP 474".
7 7 Find a way to distribute this code and a way to support it in rg. Find a way to distribute this code and a way to support it in rg.
8 8 Probably I will allow only one plan (Friends) till they all create Probably I will allow only one plan (Friends) till they all create
9 9 accounts. After this, I will remove this plan? accounts. After this, I will remove this plan?
10 [ ] CSRF for logout. Seems is not so easy because I need to generate a token
11 all the time to embed it in the logout link. Another possibility
12 is to have a form on the logout page. But some people probably will
13 assume that pressing the logout link is enough and will not press
14 the logout _button_. A signed token is enough, if I prepare it special
15 for logout. It is important to work only there. And, I can embed it in
16 the link.
17 Anyway, I can link all tokens with the forms and I can store them in
18 db when they are used, so we cannot use them anymore. This way I do not
19 have to save them in database at generation time.
20 What is the problem with the token?! Double post? And using same token
21 for two forms?
22 [ ] If email is not confirmed, do not send e-mail!
23 [ ] Now, we gen generate tokens without storing in database, and store tokens
24 in db only after they are used. In rg_token_valid, we will check if it
25 was already used, after we check the signing.
26 generate_token
27 user post form
28 we insert it in db => ok
29 user post it again
30 we insert it in db => get error!
31 this means was already used it
32 abort
33 continue work
34 What I have to change:
35 - rg_token_get must return a signed token. Maybe we add the form id to
36 the equation.
37 - rg_token_valid: we insert it in db as used, return FALSE if we get
38 an error. We need to know if db failed or we could not
39 insert because of already present key
40 - get rid of tokens.used field. What about expiration?
41
10 42 [ ] [ ]
11 43
12 44 == BEFORE NEXT RELEASE == == BEFORE NEXT RELEASE ==
45 [ ] Check cache socket is protected agains other users.
13 46 [ ] Ce se intimpla daca un atacator seteaza un cookie pe .com, de exemplu. [ ] Ce se intimpla daca un atacator seteaza un cookie pe .com, de exemplu.
14 47 El se va trimite si pe rocketgit.com. Deci, daca user-ul viziteaza site-ul El se va trimite si pe rocketgit.com. Deci, daca user-ul viziteaza site-ul
15 48 atacatorului, se seteaza acest cookie, care apoi va fi trimis catre rg.com. atacatorului, se seteaza acest cookie, care apoi va fi trimis catre rg.com.
File inc/cache.inc.php changed (mode: 100644) (index 77c1962..188a1e2)
... ... function rg_cache_unset($ns_var)
469 469 break; break;
470 470 } }
471 471
472 if (strncmp($ret, "NOT_FOUND", 9) == 0)
473 break;
474
472 475 // TODO: return old value? // TODO: return old value?
473 476 if (strncmp($ret, "OK", 2) != 0) { if (strncmp($ret, "OK", 2) != 0) {
474 477 rg_internal_error("Invalid answer: $ret"); rg_internal_error("Invalid answer: $ret");
File inc/struct.inc.php changed (mode: 100644) (index 8332f23..27bbb3d)
... ... $rg_sql_struct[30]['tables'] = array();
399 399 $rg_sql_struct[30]['other'] = array( $rg_sql_struct[30]['other'] = array(
400 400 "events.fail" => "ALTER TABLE events ADD fail SMALLINT NOT NULL DEFAULT 0", "events.fail" => "ALTER TABLE events ADD fail SMALLINT NOT NULL DEFAULT 0",
401 401 "events.tries" => "ALTER TABLE events ADD tries SMALLINT NOT NULL DEFAULT 0", "events.tries" => "ALTER TABLE events ADD tries SMALLINT NOT NULL DEFAULT 0",
402 "events.next_try" => "ALTER TABLE events ADD next_try INTEGER NOT NULL DEFAULT 0"
402 "events.next_try" => "ALTER TABLE events ADD next_try INTEGER NOT NULL DEFAULT 0",
403 "tokens.used" => "ALTER TABLE tokens ADD used SMALLINT NOT NULL DEFAULT 0"
403 404 ); );
404 405
405 406
File inc/token.inc.php changed (mode: 100644) (index 1b8f04b..ecf6f7e)
... ... function rg_token_valid($db, $rg)
138 138 $rand = substr($rg['token'], 0, 16); $rand = substr($rg['token'], 0, 16);
139 139 $search = $rand . $ua_hash; $search = $rand . $ua_hash;
140 140 $params = array("sid" => $rg['sid'], "token" => $search); $params = array("sid" => $rg['sid'], "token" => $search);
141 $sql = "SELECT 1 AS junk FROM tokens"
141 $sql = "UPDATE tokens SET used = 1"
142 142 . " WHERE token = @@token@@" . " WHERE token = @@token@@"
143 . " AND sid = @@sid@@";
143 . " AND sid = @@sid@@"
144 . " AND used = 0";
144 145 $res = rg_sql_query_params($db, $sql, $params); $res = rg_sql_query_params($db, $sql, $params);
145 146 if ($res === FALSE) { if ($res === FALSE) {
146 147 rg_token_set_error("cannot get token (" . rg_sql_error() . ")"); rg_token_set_error("cannot get token (" . rg_sql_error() . ")");
147 148 break; break;
148 149 } }
149 150
150 $rows = rg_sql_num_rows($res);
151 $rows = rg_sql_affected_rows($res);
151 152 rg_sql_free_result($res); rg_sql_free_result($res);
152 153 if ($rows == 0) if ($rows == 0)
153 154 break; break;
 
... ... function rg_token_insert($db, $sid, $token)
199 200
200 201 /* /*
201 202 * Returns a token to be used on a form/url * Returns a token to be used on a form/url
202 * We generate only one per session.
203 * We generate only one per form, but multiple per session.
203 204 */ */
204 205 $rg_token = FALSE; $rg_token = FALSE;
205 206 function rg_token_get($db, $rg) function rg_token_get($db, $rg)
File tests/token.php changed (mode: 100644) (index f384d12..2fac7ce)
... ... rg_log_set_file("token.log");
14 14 $rg_no_db = TRUE; $rg_no_db = TRUE;
15 15 require_once("common.php"); require_once("common.php");
16 16
17 $sid = "session1";
18 $token = rg_token_get($db, $sid, "user-agent1");
17 $a = array("ua" => "user-agent1", "sid" => "session1");
18 $token = rg_token_get($db, $a);
19 19 if ($token === FALSE) { if ($token === FALSE) {
20 20 rg_log("Generating a token should not fail (" . rg_token_error() . ")!"); rg_log("Generating a token should not fail (" . rg_token_error() . ")!");
21 21 exit(1); exit(1);
22 22 } }
23 23 rg_log("Correct token: $token"); rg_log("Correct token: $token");
24 24
25 $a = array("sid" => $sid, "token" => $token);
25 $a['token'] = $token;
26 26 $r = rg_token_valid($db, $a); $r = rg_token_valid($db, $a);
27 27 if ($r === FALSE) { if ($r === FALSE) {
28 28 rg_log("Validating a correct token must work (" . rg_token_error() . ")!"); rg_log("Validating a correct token must work (" . rg_token_error() . ")!");
29 29 exit(1); exit(1);
30 30 } }
31 31
32 $r = rg_token_delete($db, $sid, $token);
32 $r = rg_token_delete($db, $a['sid'], $a['token']);
33 33 if ($r['ok'] != 1) { if ($r['ok'] != 1) {
34 34 rg_log("We should be able to delete a token!"); rg_log("We should be able to delete a token!");
35 35 exit(1); exit(1);
36 36 } }
37 37
38 $a = array("sid" => $sid, "token" => $token);
39 38 $r = rg_token_valid($db, $a); $r = rg_token_valid($db, $a);
40 39 if ($r !== FALSE) { if ($r !== FALSE) {
41 40 rg_log("Token should not be available after delete!"); rg_log("Token should not be available after delete!");
 
... ... if ($r !== FALSE) {
45 44
46 45 rg_log("Now, test pre-login sessions..."); rg_log("Now, test pre-login sessions...");
47 46 $rg_token = FALSE; /* we must remove it from memory */ $rg_token = FALSE; /* we must remove it from memory */
48 $sid = "Xsession2";
49 $token = rg_token_get($db, $sid, "user-agent1");
47 $a = array("ua" => "user-agent1", "sid" => "Xsession2");
48 $token = rg_token_get($db, $a);
50 49 if ($token === FALSE) { if ($token === FALSE) {
51 50 rg_log("Generating a token should not fail (" . rg_token_error() . ")!"); rg_log("Generating a token should not fail (" . rg_token_error() . ")!");
52 51 exit(1); exit(1);
53 52 } }
54 53 rg_log("Correct token: $token"); rg_log("Correct token: $token");
54 $a['token'] = $token;
55 55
56 $copy = "y" . substr($token, 1);
57 $a = array("sid" => $sid, "token" => $token);
58 $r = rg_token_valid($db, $a);
56 $copy = $a;
57 $copy['token'] = "y" . substr($a['token'], 1);
58 $r = rg_token_valid($db, $copy);
59 59 if ($r !== FALSE) { if ($r !== FALSE) {
60 60 rg_log("An altered token must return error!"); rg_log("An altered token must return error!");
61 61 exit(1); exit(1);
62 62 } }
63 63
64 $a = array("sid" => $sid, "token" => $token);
65 64 $r = rg_token_valid($db, $a); $r = rg_token_valid($db, $a);
66 65 if ($r === FALSE) { if ($r === FALSE) {
67 66 rg_log("Validating a correct token must work (" . rg_token_error() . ")!"); rg_log("Validating a correct token must work (" . rg_token_error() . ")!");
68 67 exit(1); exit(1);
69 68 } }
70 69
71 echo "token: OK!\n";
70
71 rg_log("Testing double posting...");
72 $rg_token = FALSE; /* we must remove it from memory */
73 $a = array("ua" => "user-agent3",
74 "sid" => "session_double");
75 $token = rg_token_get($db, $a);
76 if ($token === FALSE) {
77 rg_log("Generating a token should not fail (" . rg_token_error() . ")!");
78 exit(1);
79 }
80 $a['token'] = $token;
81
82 $r = rg_token_valid($db, $a);
83 if ($r === FALSE) {
84 rg_log("Calling 'valid' first time must work!");
85 exit(1);
86 }
87 $r = rg_token_valid($db, $a);
88 if ($r !== FALSE) {
89 rg_log("Calling 'valid' second time must NOT work!");
90 exit(1);
91 }
92
93 rg_log("OK!");
72 94 ?> ?>
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master