Fossil SCM

Stronger CSRF token based on a SHA1 hash of the login cookie.

drh 2023-09-18 14:13 csrf-defense-enhancement
Commit ff3746c4c214a50e74328463604d9ae1c5aa82576d308a64c18c32f7fa130442
--- src/builtin.c
+++ src/builtin.c
@@ -666,18 +666,10 @@
666666
CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");
667667
CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false");
668668
CX("};\n"/*fossil.user*/);
669669
CX("if(fossil.config.skin.isDark) "
670670
"document.body.classList.add('fossil-dark-style');\n");
671
-#if 0
672
- /* Is it safe to emit the CSRF token here? Some pages add it
673
- ** as a hidden form field. */
674
- if(g.zCsrfToken[0]!=0){
675
- CX("window.fossil.csrfToken = %!j;\n",
676
- g.zCsrfToken);
677
- }
678
-#endif
679671
/*
680672
** fossil.page holds info about the current page. This is also
681673
** where the current page "should" store any of its own
682674
** page-specific state, and it is reserved for that purpose.
683675
*/
684676
--- src/builtin.c
+++ src/builtin.c
@@ -666,18 +666,10 @@
666 CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");
667 CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false");
668 CX("};\n"/*fossil.user*/);
669 CX("if(fossil.config.skin.isDark) "
670 "document.body.classList.add('fossil-dark-style');\n");
671 #if 0
672 /* Is it safe to emit the CSRF token here? Some pages add it
673 ** as a hidden form field. */
674 if(g.zCsrfToken[0]!=0){
675 CX("window.fossil.csrfToken = %!j;\n",
676 g.zCsrfToken);
677 }
678 #endif
679 /*
680 ** fossil.page holds info about the current page. This is also
681 ** where the current page "should" store any of its own
682 ** page-specific state, and it is reserved for that purpose.
683 */
684
--- src/builtin.c
+++ src/builtin.c
@@ -666,18 +666,10 @@
666 CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");
667 CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false");
668 CX("};\n"/*fossil.user*/);
669 CX("if(fossil.config.skin.isDark) "
670 "document.body.classList.add('fossil-dark-style');\n");
 
 
 
 
 
 
 
 
671 /*
672 ** fossil.page holds info about the current page. This is also
673 ** where the current page "should" store any of its own
674 ** page-specific state, and it is reserved for that purpose.
675 */
676
+22 -3
--- src/login.c
+++ src/login.c
@@ -49,10 +49,25 @@
4949
# define sleep Sleep /* windows does not have sleep, but Sleep */
5050
# endif
5151
#endif
5252
#include <time.h>
5353
54
+/*
55
+** Compute an appropriate Anti-CSRF token into g.zCsrfToken[].
56
+*/
57
+static void login_create_csrf_secret(const char *zSeed){
58
+ unsigned char zResult[20];
59
+ int i;
60
+
61
+ sha1sum_binary(zSeed, zResult);
62
+ for(i=0; i<sizeof(g.zCsrfToken)-1; i++){
63
+ g.zCsrfToken[i] = "abcdefghijklmnopqrstuvwxyz"
64
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
65
+ "0123456789-/"[zResult[i]%64];
66
+ }
67
+ g.zCsrfToken[i] = 0;
68
+}
5469
5570
/*
5671
** Return the login-group name. Or return 0 if this repository is
5772
** not a member of a login-group.
5873
*/
@@ -1289,10 +1304,11 @@
12891304
|| (g.fSshClient & CGI_SSH_CLIENT)!=0 )
12901305
&& g.useLocalauth
12911306
&& db_get_int("localauth",0)==0
12921307
&& P("HTTPS")==0
12931308
){
1309
+ char *zSeed;
12941310
if( g.localOpen ) zLogin = db_lget("default-user",0);
12951311
if( zLogin!=0 ){
12961312
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
12971313
}else{
12981314
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
@@ -1299,11 +1315,14 @@
12991315
}
13001316
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
13011317
zCap = "sxy";
13021318
g.noPswd = 1;
13031319
g.isHuman = 1;
1304
- sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
1320
+ zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
1321
+ " FROM user WHERE uid=%d", uid);
1322
+ login_create_csrf_secret(zSeed);
1323
+ fossil_free(zSeed);
13051324
}
13061325
13071326
/* Check the login cookie to see if it matches a known valid user.
13081327
*/
13091328
if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){
@@ -1354,11 +1373,11 @@
13541373
if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){
13551374
uid = login_find_user(zUser, zHash);
13561375
if( uid ) record_login_attempt(zUser, zIpAddr, 1);
13571376
}
13581377
}
1359
- sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
1378
+ login_create_csrf_secret(zHash);
13601379
}
13611380
13621381
/* If no user found and the REMOTE_USER environment variable is set,
13631382
** then accept the value of REMOTE_USER as the user.
13641383
*/
@@ -1404,11 +1423,11 @@
14041423
if( uid==0 ){
14051424
/* If there is no user "nobody", then make one up - with no privileges */
14061425
uid = -1;
14071426
zCap = "";
14081427
}
1409
- sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "none");
1428
+ login_create_csrf_secret("none");
14101429
}
14111430
14121431
login_set_uid(uid, zCap);
14131432
}
14141433
14151434
--- src/login.c
+++ src/login.c
@@ -49,10 +49,25 @@
49 # define sleep Sleep /* windows does not have sleep, but Sleep */
50 # endif
51 #endif
52 #include <time.h>
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
55 /*
56 ** Return the login-group name. Or return 0 if this repository is
57 ** not a member of a login-group.
58 */
@@ -1289,10 +1304,11 @@
1289 || (g.fSshClient & CGI_SSH_CLIENT)!=0 )
1290 && g.useLocalauth
1291 && db_get_int("localauth",0)==0
1292 && P("HTTPS")==0
1293 ){
 
1294 if( g.localOpen ) zLogin = db_lget("default-user",0);
1295 if( zLogin!=0 ){
1296 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
1297 }else{
1298 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
@@ -1299,11 +1315,14 @@
1299 }
1300 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
1301 zCap = "sxy";
1302 g.noPswd = 1;
1303 g.isHuman = 1;
1304 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
 
 
 
1305 }
1306
1307 /* Check the login cookie to see if it matches a known valid user.
1308 */
1309 if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){
@@ -1354,11 +1373,11 @@
1354 if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){
1355 uid = login_find_user(zUser, zHash);
1356 if( uid ) record_login_attempt(zUser, zIpAddr, 1);
1357 }
1358 }
1359 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
1360 }
1361
1362 /* If no user found and the REMOTE_USER environment variable is set,
1363 ** then accept the value of REMOTE_USER as the user.
1364 */
@@ -1404,11 +1423,11 @@
1404 if( uid==0 ){
1405 /* If there is no user "nobody", then make one up - with no privileges */
1406 uid = -1;
1407 zCap = "";
1408 }
1409 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "none");
1410 }
1411
1412 login_set_uid(uid, zCap);
1413 }
1414
1415
--- src/login.c
+++ src/login.c
@@ -49,10 +49,25 @@
49 # define sleep Sleep /* windows does not have sleep, but Sleep */
50 # endif
51 #endif
52 #include <time.h>
53
54 /*
55 ** Compute an appropriate Anti-CSRF token into g.zCsrfToken[].
56 */
57 static void login_create_csrf_secret(const char *zSeed){
58 unsigned char zResult[20];
59 int i;
60
61 sha1sum_binary(zSeed, zResult);
62 for(i=0; i<sizeof(g.zCsrfToken)-1; i++){
63 g.zCsrfToken[i] = "abcdefghijklmnopqrstuvwxyz"
64 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
65 "0123456789-/"[zResult[i]%64];
66 }
67 g.zCsrfToken[i] = 0;
68 }
69
70 /*
71 ** Return the login-group name. Or return 0 if this repository is
72 ** not a member of a login-group.
73 */
@@ -1289,10 +1304,11 @@
1304 || (g.fSshClient & CGI_SSH_CLIENT)!=0 )
1305 && g.useLocalauth
1306 && db_get_int("localauth",0)==0
1307 && P("HTTPS")==0
1308 ){
1309 char *zSeed;
1310 if( g.localOpen ) zLogin = db_lget("default-user",0);
1311 if( zLogin!=0 ){
1312 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
1313 }else{
1314 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
@@ -1299,11 +1315,14 @@
1315 }
1316 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
1317 zCap = "sxy";
1318 g.noPswd = 1;
1319 g.isHuman = 1;
1320 zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
1321 " FROM user WHERE uid=%d", uid);
1322 login_create_csrf_secret(zSeed);
1323 fossil_free(zSeed);
1324 }
1325
1326 /* Check the login cookie to see if it matches a known valid user.
1327 */
1328 if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){
@@ -1354,11 +1373,11 @@
1373 if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){
1374 uid = login_find_user(zUser, zHash);
1375 if( uid ) record_login_attempt(zUser, zIpAddr, 1);
1376 }
1377 }
1378 login_create_csrf_secret(zHash);
1379 }
1380
1381 /* If no user found and the REMOTE_USER environment variable is set,
1382 ** then accept the value of REMOTE_USER as the user.
1383 */
@@ -1404,11 +1423,11 @@
1423 if( uid==0 ){
1424 /* If there is no user "nobody", then make one up - with no privileges */
1425 uid = -1;
1426 zCap = "";
1427 }
1428 login_create_csrf_secret("none");
1429 }
1430
1431 login_set_uid(uid, zCap);
1432 }
1433
1434
+1 -1
--- src/main.c
+++ src/main.c
@@ -256,11 +256,11 @@
256256
/* all Tcl related context necessary for integration */
257257
struct TclContext tcl;
258258
#endif
259259
260260
/* For defense against Cross-site Request Forgery attacks */
261
- char zCsrfToken[12]; /* Value of the anti-CSRF token */
261
+ char zCsrfToken[16]; /* Value of the anti-CSRF token */
262262
int okCsrf; /* -1: unsafe
263263
** 0: unknown
264264
** 1: same origin
265265
** 2: same origin + is POST
266266
** 3: same origin, POST, valid csrf token */
267267
--- src/main.c
+++ src/main.c
@@ -256,11 +256,11 @@
256 /* all Tcl related context necessary for integration */
257 struct TclContext tcl;
258 #endif
259
260 /* For defense against Cross-site Request Forgery attacks */
261 char zCsrfToken[12]; /* Value of the anti-CSRF token */
262 int okCsrf; /* -1: unsafe
263 ** 0: unknown
264 ** 1: same origin
265 ** 2: same origin + is POST
266 ** 3: same origin, POST, valid csrf token */
267
--- src/main.c
+++ src/main.c
@@ -256,11 +256,11 @@
256 /* all Tcl related context necessary for integration */
257 struct TclContext tcl;
258 #endif
259
260 /* For defense against Cross-site Request Forgery attacks */
261 char zCsrfToken[16]; /* Value of the anti-CSRF token */
262 int okCsrf; /* -1: unsafe
263 ** 0: unknown
264 ** 1: same origin
265 ** 2: same origin + is POST
266 ** 3: same origin, POST, valid csrf token */
267
+13
--- src/sha1.c
+++ src/sha1.c
@@ -392,10 +392,23 @@
392392
blob_resize(pCksum, 40);
393393
SHA1Final(zResult, &ctx);
394394
DigestToBase16(zResult, blob_buffer(pCksum));
395395
return 0;
396396
}
397
+
398
+/*
399
+** Compute a binary SHA1 checksum of a zero-terminated string. The
400
+** result is stored in zOut, which is a buffer that must be at least
401
+** 20 bytes in size.
402
+*/
403
+void sha1sum_binary(const char *zIn, unsigned char *zOut){
404
+ SHA1Context ctx;
405
+
406
+ SHA1Init(&ctx);
407
+ SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
408
+ SHA1Final(zOut, &ctx);
409
+}
397410
398411
/*
399412
** Compute the SHA1 checksum of a zero-terminated string. The
400413
** result is held in memory obtained from mprintf().
401414
*/
402415
--- src/sha1.c
+++ src/sha1.c
@@ -392,10 +392,23 @@
392 blob_resize(pCksum, 40);
393 SHA1Final(zResult, &ctx);
394 DigestToBase16(zResult, blob_buffer(pCksum));
395 return 0;
396 }
 
 
 
 
 
 
 
 
 
 
 
 
 
397
398 /*
399 ** Compute the SHA1 checksum of a zero-terminated string. The
400 ** result is held in memory obtained from mprintf().
401 */
402
--- src/sha1.c
+++ src/sha1.c
@@ -392,10 +392,23 @@
392 blob_resize(pCksum, 40);
393 SHA1Final(zResult, &ctx);
394 DigestToBase16(zResult, blob_buffer(pCksum));
395 return 0;
396 }
397
398 /*
399 ** Compute a binary SHA1 checksum of a zero-terminated string. The
400 ** result is stored in zOut, which is a buffer that must be at least
401 ** 20 bytes in size.
402 */
403 void sha1sum_binary(const char *zIn, unsigned char *zOut){
404 SHA1Context ctx;
405
406 SHA1Init(&ctx);
407 SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
408 SHA1Final(zOut, &ctx);
409 }
410
411 /*
412 ** Compute the SHA1 checksum of a zero-terminated string. The
413 ** result is held in memory obtained from mprintf().
414 */
415

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button