Fossil SCM
Make anonymous cookies valid for 8 hours. Include the client IP address as part of the cookie hash, but do not display the client IP address within the text of the cookie.
Commit
68da4784aa3d5f3efae6e549957c65d6c8cace01e0c52d7b43fa5c96ab97f29e
Parent
144c5dbe005354e…
1 file changed
+21
-15
+21
-15
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -339,37 +339,44 @@ | ||
| 339 | 339 | }else{ |
| 340 | 340 | free(zCookie); |
| 341 | 341 | } |
| 342 | 342 | } |
| 343 | 343 | |
| 344 | +/* | |
| 345 | +** Lifetime of an anoymous cookie, in seconds. | |
| 346 | +*/ | |
| 347 | +#define ANONYMOUS_COOKIE_LIFESPAN 28800 /* 28800 seconds == 8 hours */ | |
| 348 | + | |
| 344 | 349 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 345 | 350 | ** |
| 346 | -** HASH/TIME:IPADDR/anonymous | |
| 351 | +** HASH/TIME/anonymous | |
| 347 | 352 | ** |
| 348 | -** Where HASH is the sha1sum of TIME:IPADDR/SECRET, in which SECRET | |
| 353 | +** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which SECRET | |
| 349 | 354 | ** is captcha-secret. |
| 350 | 355 | ** |
| 351 | 356 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 352 | 357 | ** *zCookieDest and the caller must eventually free() it. |
| 353 | 358 | ** |
| 354 | 359 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| 360 | +** | |
| 361 | +** Search for tag-20250817a to find the code that recognizes this cookie. | |
| 355 | 362 | */ |
| 356 | 363 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 357 | 364 | char *zNow; /* Current time (julian day number) */ |
| 358 | 365 | char *zCookie; /* The login cookie */ |
| 359 | 366 | const char *zIpAddr; /* IP Address */ |
| 360 | 367 | const char *zCookieName; /* Name of the login cookie */ |
| 361 | 368 | Blob b; /* Blob used during cookie construction */ |
| 362 | - int expires = bSessionCookie ? 0 : 3600; /* Valid for 60 minutes */ | |
| 369 | + int expires = bSessionCookie ? 0 : ANONYMOUS_COOKIE_LIFESPAN; | |
| 363 | 370 | zCookieName = login_cookie_name(); |
| 364 | 371 | zNow = db_text("0", "SELECT julianday('now')"); |
| 365 | 372 | assert( zCookieName && zNow ); |
| 366 | 373 | blob_init(&b, zNow, -1); |
| 367 | 374 | zIpAddr = PD("REMOTE_ADDR","nil"); |
| 368 | - blob_appendf(&b, ":%s/%z", zIpAddr, captcha_secret(0)); | |
| 375 | + blob_appendf(&b, "/%s/%z", zIpAddr, captcha_secret(0)); | |
| 369 | 376 | sha1sum_blob(&b, &b); |
| 370 | - zCookie = mprintf("%s/%s:%s/anonymous", blob_buffer(&b), zNow, zIpAddr); | |
| 377 | + zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); | |
| 371 | 378 | blob_reset(&b); |
| 372 | 379 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 373 | 380 | if( zCookieDest ){ |
| 374 | 381 | *zCookieDest = zCookie; |
| 375 | 382 | }else{ |
| @@ -1412,36 +1419,35 @@ | ||
| 1412 | 1419 | } |
| 1413 | 1420 | } |
| 1414 | 1421 | if( zUser==0 ){ |
| 1415 | 1422 | /* Invalid cookie */ |
| 1416 | 1423 | }else if( fossil_strcmp(zUser, "anonymous")==0 ){ |
| 1417 | - /* Cookies of the form "HASH/TIME:IPADDR/anonymous". The TIME must | |
| 1418 | - ** not be too old and the sha1 hash of TIME:IPADDR/SECRET must match | |
| 1419 | - ** HASH. SECRET is the "captcha-secret" value in the repository. | |
| 1424 | + /* Cookies of the form "HASH/TIME/anonymous". The TIME must | |
| 1425 | + ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and | |
| 1426 | + ** the sha1 hash of TIME/IPADDR/SECRET must match HASH. IPADDR | |
| 1427 | + ** is the remote address of the client and SECRET is the | |
| 1428 | + ** "captcha-secret" value in the repository. See tag-20250817a | |
| 1429 | + ** for the code the creates this cookie. | |
| 1420 | 1430 | */ |
| 1421 | 1431 | double rTime = atof(zArg); |
| 1422 | - const char *zCookieIP; | |
| 1423 | 1432 | Blob b; |
| 1424 | 1433 | char *zSecret; |
| 1425 | 1434 | int n = 0; |
| 1426 | 1435 | |
| 1427 | - zCookieIP = strchr(zArg,':'); | |
| 1428 | - if( zCookieIP && strcmp(zCookieIP+1,zIpAddr)!=0 ) zCookieIP = 0; | |
| 1429 | 1436 | do{ |
| 1430 | - if( zCookieIP==0 ) break; | |
| 1431 | 1437 | blob_zero(&b); |
| 1432 | 1438 | zSecret = captcha_secret(n++); |
| 1433 | 1439 | if( zSecret==0 ) break; |
| 1434 | - blob_appendf(&b, "%s/%s", zArg, zSecret); | |
| 1440 | + blob_appendf(&b, "%s/%s/%s", zArg, zIpAddr, zSecret); | |
| 1435 | 1441 | sha1sum_blob(&b, &b); |
| 1436 | 1442 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1437 | 1443 | uid = db_int(0, |
| 1438 | 1444 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1439 | 1445 | " AND octet_length(cap)>0" |
| 1440 | 1446 | " AND octet_length(pw)>0" |
| 1441 | - " AND %.17g+0.0416667>julianday('now')", | |
| 1442 | - rTime | |
| 1447 | + " AND %.17g>julianday('now')", | |
| 1448 | + rTime+ANONYMOUS_COOKIE_LIFESPAN/86400.0 | |
| 1443 | 1449 | ); |
| 1444 | 1450 | } |
| 1445 | 1451 | }while( uid==0 ); |
| 1446 | 1452 | blob_reset(&b); |
| 1447 | 1453 | }else{ |
| 1448 | 1454 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -339,37 +339,44 @@ | |
| 339 | }else{ |
| 340 | free(zCookie); |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 345 | ** |
| 346 | ** HASH/TIME:IPADDR/anonymous |
| 347 | ** |
| 348 | ** Where HASH is the sha1sum of TIME:IPADDR/SECRET, in which SECRET |
| 349 | ** is captcha-secret. |
| 350 | ** |
| 351 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 352 | ** *zCookieDest and the caller must eventually free() it. |
| 353 | ** |
| 354 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| 355 | */ |
| 356 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 357 | char *zNow; /* Current time (julian day number) */ |
| 358 | char *zCookie; /* The login cookie */ |
| 359 | const char *zIpAddr; /* IP Address */ |
| 360 | const char *zCookieName; /* Name of the login cookie */ |
| 361 | Blob b; /* Blob used during cookie construction */ |
| 362 | int expires = bSessionCookie ? 0 : 3600; /* Valid for 60 minutes */ |
| 363 | zCookieName = login_cookie_name(); |
| 364 | zNow = db_text("0", "SELECT julianday('now')"); |
| 365 | assert( zCookieName && zNow ); |
| 366 | blob_init(&b, zNow, -1); |
| 367 | zIpAddr = PD("REMOTE_ADDR","nil"); |
| 368 | blob_appendf(&b, ":%s/%z", zIpAddr, captcha_secret(0)); |
| 369 | sha1sum_blob(&b, &b); |
| 370 | zCookie = mprintf("%s/%s:%s/anonymous", blob_buffer(&b), zNow, zIpAddr); |
| 371 | blob_reset(&b); |
| 372 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 373 | if( zCookieDest ){ |
| 374 | *zCookieDest = zCookie; |
| 375 | }else{ |
| @@ -1412,36 +1419,35 @@ | |
| 1412 | } |
| 1413 | } |
| 1414 | if( zUser==0 ){ |
| 1415 | /* Invalid cookie */ |
| 1416 | }else if( fossil_strcmp(zUser, "anonymous")==0 ){ |
| 1417 | /* Cookies of the form "HASH/TIME:IPADDR/anonymous". The TIME must |
| 1418 | ** not be too old and the sha1 hash of TIME:IPADDR/SECRET must match |
| 1419 | ** HASH. SECRET is the "captcha-secret" value in the repository. |
| 1420 | */ |
| 1421 | double rTime = atof(zArg); |
| 1422 | const char *zCookieIP; |
| 1423 | Blob b; |
| 1424 | char *zSecret; |
| 1425 | int n = 0; |
| 1426 | |
| 1427 | zCookieIP = strchr(zArg,':'); |
| 1428 | if( zCookieIP && strcmp(zCookieIP+1,zIpAddr)!=0 ) zCookieIP = 0; |
| 1429 | do{ |
| 1430 | if( zCookieIP==0 ) break; |
| 1431 | blob_zero(&b); |
| 1432 | zSecret = captcha_secret(n++); |
| 1433 | if( zSecret==0 ) break; |
| 1434 | blob_appendf(&b, "%s/%s", zArg, zSecret); |
| 1435 | sha1sum_blob(&b, &b); |
| 1436 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1437 | uid = db_int(0, |
| 1438 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1439 | " AND octet_length(cap)>0" |
| 1440 | " AND octet_length(pw)>0" |
| 1441 | " AND %.17g+0.0416667>julianday('now')", |
| 1442 | rTime |
| 1443 | ); |
| 1444 | } |
| 1445 | }while( uid==0 ); |
| 1446 | blob_reset(&b); |
| 1447 | }else{ |
| 1448 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -339,37 +339,44 @@ | |
| 339 | }else{ |
| 340 | free(zCookie); |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | /* |
| 345 | ** Lifetime of an anoymous cookie, in seconds. |
| 346 | */ |
| 347 | #define ANONYMOUS_COOKIE_LIFESPAN 28800 /* 28800 seconds == 8 hours */ |
| 348 | |
| 349 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 350 | ** |
| 351 | ** HASH/TIME/anonymous |
| 352 | ** |
| 353 | ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which SECRET |
| 354 | ** is captcha-secret. |
| 355 | ** |
| 356 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 357 | ** *zCookieDest and the caller must eventually free() it. |
| 358 | ** |
| 359 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| 360 | ** |
| 361 | ** Search for tag-20250817a to find the code that recognizes this cookie. |
| 362 | */ |
| 363 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 364 | char *zNow; /* Current time (julian day number) */ |
| 365 | char *zCookie; /* The login cookie */ |
| 366 | const char *zIpAddr; /* IP Address */ |
| 367 | const char *zCookieName; /* Name of the login cookie */ |
| 368 | Blob b; /* Blob used during cookie construction */ |
| 369 | int expires = bSessionCookie ? 0 : ANONYMOUS_COOKIE_LIFESPAN; |
| 370 | zCookieName = login_cookie_name(); |
| 371 | zNow = db_text("0", "SELECT julianday('now')"); |
| 372 | assert( zCookieName && zNow ); |
| 373 | blob_init(&b, zNow, -1); |
| 374 | zIpAddr = PD("REMOTE_ADDR","nil"); |
| 375 | blob_appendf(&b, "/%s/%z", zIpAddr, captcha_secret(0)); |
| 376 | sha1sum_blob(&b, &b); |
| 377 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 378 | blob_reset(&b); |
| 379 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 380 | if( zCookieDest ){ |
| 381 | *zCookieDest = zCookie; |
| 382 | }else{ |
| @@ -1412,36 +1419,35 @@ | |
| 1419 | } |
| 1420 | } |
| 1421 | if( zUser==0 ){ |
| 1422 | /* Invalid cookie */ |
| 1423 | }else if( fossil_strcmp(zUser, "anonymous")==0 ){ |
| 1424 | /* Cookies of the form "HASH/TIME/anonymous". The TIME must |
| 1425 | ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and |
| 1426 | ** the sha1 hash of TIME/IPADDR/SECRET must match HASH. IPADDR |
| 1427 | ** is the remote address of the client and SECRET is the |
| 1428 | ** "captcha-secret" value in the repository. See tag-20250817a |
| 1429 | ** for the code the creates this cookie. |
| 1430 | */ |
| 1431 | double rTime = atof(zArg); |
| 1432 | Blob b; |
| 1433 | char *zSecret; |
| 1434 | int n = 0; |
| 1435 | |
| 1436 | do{ |
| 1437 | blob_zero(&b); |
| 1438 | zSecret = captcha_secret(n++); |
| 1439 | if( zSecret==0 ) break; |
| 1440 | blob_appendf(&b, "%s/%s/%s", zArg, zIpAddr, zSecret); |
| 1441 | sha1sum_blob(&b, &b); |
| 1442 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1443 | uid = db_int(0, |
| 1444 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1445 | " AND octet_length(cap)>0" |
| 1446 | " AND octet_length(pw)>0" |
| 1447 | " AND %.17g>julianday('now')", |
| 1448 | rTime+ANONYMOUS_COOKIE_LIFESPAN/86400.0 |
| 1449 | ); |
| 1450 | } |
| 1451 | }while( uid==0 ); |
| 1452 | blob_reset(&b); |
| 1453 | }else{ |
| 1454 |